summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2013-08-22 00:19:50 +0200
committerVictor Stinner <victor.stinner@gmail.com>2013-08-22 00:19:50 +0200
commit40bc8cffd375821554a4201f3a9611f25fc83595 (patch)
tree089d33be8286ed29cbf0d8886f6bff1a954ecf66
parent707c58f6cf65059309693311372eed9c5705e1b5 (diff)
downloadcpython-40bc8cffd375821554a4201f3a9611f25fc83595.tar.gz
Close #18794: Add a fileno() method and a closed attribute to select.devpoll
objects. Add also tests on fileno() method and closed attribute of select.epoll and select.kqueue.
-rw-r--r--Doc/library/select.rst31
-rw-r--r--Lib/test/test_devpoll.py25
-rw-r--r--Lib/test/test_epoll.py25
-rw-r--r--Lib/test/test_kqueue.py22
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/selectmodule.c92
6 files changed, 191 insertions, 7 deletions
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
index b73f11e227..73b990d1e2 100644
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -147,6 +147,27 @@ descriptors), ``/dev/poll`` is O(active file descriptors).
object.
+.. method:: devpoll.close()
+
+ Close the file descriptor of the polling object.
+
+ .. versionadded:: 3.4
+
+
+.. attribute:: devpoll.closed
+
+ ``True`` if the polling object is closed.
+
+ .. versionadded:: 3.4
+
+
+.. method:: devpoll.fileno()
+
+ Return the file descriptor number of the polling object.
+
+ .. versionadded:: 3.4
+
+
.. method:: devpoll.register(fd[, eventmask])
Register a file descriptor with the polling object. Future calls to the
@@ -244,6 +265,11 @@ Edge and Level Trigger Polling (epoll) Objects
Close the control file descriptor of the epoll object.
+.. attribute:: epoll.closed
+
+ ``True`` if the epoll object is closed.
+
+
.. method:: epoll.fileno()
Return the file descriptor number of the control fd.
@@ -363,6 +389,11 @@ Kqueue Objects
Close the control file descriptor of the kqueue object.
+.. attribute:: kqueue.closed
+
+ ``True`` if the kqueue object is closed.
+
+
.. method:: kqueue.fileno()
Return the file descriptor number of the control fd.
diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py
index ec350cd495..167e0ee4bf 100644
--- a/Lib/test/test_devpoll.py
+++ b/Lib/test/test_devpoll.py
@@ -87,6 +87,31 @@ class DevPollTests(unittest.TestCase):
self.assertRaises(OverflowError, pollster.poll, 1 << 63)
self.assertRaises(OverflowError, pollster.poll, 1 << 64)
+ def test_close(self):
+ open_file = open(__file__, "rb")
+ self.addCleanup(open_file.close)
+ fd = open_file.fileno()
+ devpoll = select.devpoll()
+
+ # test fileno() method and closed attribute
+ self.assertIsInstance(devpoll.fileno(), int)
+ self.assertFalse(devpoll.closed)
+
+ # test close()
+ devpoll.close()
+ self.assertTrue(devpoll.closed)
+ self.assertRaises(ValueError, devpoll.fileno)
+
+ # close() can be called more than once
+ devpoll.close()
+
+ # operations must fail with ValueError("I/O operation on closed ...")
+ self.assertRaises(ValueError, devpoll.modify, fd, select.POLLIN)
+ self.assertRaises(ValueError, devpoll.poll)
+ self.assertRaises(ValueError, devpoll.register, fd, fd, select.POLLIN)
+ self.assertRaises(ValueError, devpoll.unregister, fd)
+
+
def test_main():
run_unittest(DevPollTests)
diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py
index 7077a70057..93a9e1d320 100644
--- a/Lib/test/test_epoll.py
+++ b/Lib/test/test_epoll.py
@@ -225,6 +225,31 @@ class TestEPoll(unittest.TestCase):
server.close()
ep.unregister(fd)
+ def test_close(self):
+ open_file = open(__file__, "rb")
+ self.addCleanup(open_file.close)
+ fd = open_file.fileno()
+ epoll = select.epoll()
+
+ # test fileno() method and closed attribute
+ self.assertIsInstance(epoll.fileno(), int)
+ self.assertFalse(epoll.closed)
+
+ # test close()
+ epoll.close()
+ self.assertTrue(epoll.closed)
+ self.assertRaises(ValueError, epoll.fileno)
+
+ # close() can be called more than once
+ epoll.close()
+
+ # operations must fail with ValueError("I/O operation on closed ...")
+ self.assertRaises(ValueError, epoll.modify, fd, select.EPOLLIN)
+ self.assertRaises(ValueError, epoll.poll, 1.0)
+ self.assertRaises(ValueError, epoll.register, fd, select.EPOLLIN)
+ self.assertRaises(ValueError, epoll.unregister, fd)
+
+
def test_main():
support.run_unittest(TestEPoll)
diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py
index 5b33af2905..930f088d16 100644
--- a/Lib/test/test_kqueue.py
+++ b/Lib/test/test_kqueue.py
@@ -185,6 +185,28 @@ class TestKQueue(unittest.TestCase):
b.close()
kq.close()
+ def test_close(self):
+ open_file = open(__file__, "rb")
+ self.addCleanup(open_file.close)
+ fd = open_file.fileno()
+ kqueue = select.kqueue()
+
+ # test fileno() method and closed attribute
+ self.assertIsInstance(kqueue.fileno(), int)
+ self.assertFalse(kqueue.closed)
+
+ # test close()
+ kqueue.close()
+ self.assertTrue(kqueue.closed)
+ self.assertRaises(ValueError, kqueue.fileno)
+
+ # close() can be called more than once
+ kqueue.close()
+
+ # operations must fail with ValueError("I/O operation on closed ...")
+ self.assertRaises(ValueError, kqueue.control, None, 4)
+
+
def test_main():
support.run_unittest(TestKQueue)
diff --git a/Misc/NEWS b/Misc/NEWS
index 602d27ff54..dff9740b0c 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -38,6 +38,9 @@ Core and Builtins
Library
-------
+- Issue #18794: Add a fileno() method and a closed attribute to select.devpoll
+ objects.
+
- Issue #17119: Fixed integer overflows when processing large strings and tuples
in the tkinter module.
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 414f05a0f5..865725d319 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -684,6 +684,13 @@ typedef struct {
static PyTypeObject devpoll_Type;
+static PyObject *
+devpoll_err_closed(void)
+{
+ PyErr_SetString(PyExc_ValueError, "I/O operation on closed devpoll object");
+ return NULL;
+}
+
static int devpoll_flush(devpollObject *self)
{
int size, n;
@@ -724,6 +731,9 @@ internal_devpoll_register(devpollObject *self, PyObject *args, int remove)
PyObject *o;
int fd, events = POLLIN | POLLPRI | POLLOUT;
+ if (self->fd_devpoll < 0)
+ return devpoll_err_closed();
+
if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) {
return NULL;
}
@@ -788,6 +798,9 @@ devpoll_unregister(devpollObject *self, PyObject *o)
{
int fd;
+ if (self->fd_devpoll < 0)
+ return devpoll_err_closed();
+
fd = PyObject_AsFileDescriptor( o );
if (fd == -1)
return NULL;
@@ -817,6 +830,9 @@ devpoll_poll(devpollObject *self, PyObject *args)
long timeout;
PyObject *value, *num1, *num2;
+ if (self->fd_devpoll < 0)
+ return devpoll_err_closed();
+
if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) {
return NULL;
}
@@ -895,6 +911,45 @@ devpoll_poll(devpollObject *self, PyObject *args)
return NULL;
}
+static PyObject*
+devpoll_close(devpollObject *self)
+{
+ errno = devpoll_internal_close(self);
+ if (errno < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(devpoll_close_doc,
+"close() -> None\n\
+\n\
+Close the devpoll file descriptor. Further operations on the devpoll\n\
+object will raise an exception.");
+
+static PyObject*
+devpoll_get_closed(devpollObject *self)
+{
+ if (self->fd_devpoll < 0)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+}
+
+static PyObject*
+devpoll_fileno(devpollObject *self)
+{
+ if (self->fd_devpoll < 0)
+ return devpoll_err_closed();
+ return PyLong_FromLong(self->fd_devpoll);
+}
+
+PyDoc_STRVAR(devpoll_fileno_doc,
+"fileno() -> int\n\
+\n\
+Return the file descriptor.");
+
static PyMethodDef devpoll_methods[] = {
{"register", (PyCFunction)devpoll_register,
METH_VARARGS, devpoll_register_doc},
@@ -904,9 +959,19 @@ static PyMethodDef devpoll_methods[] = {
METH_O, devpoll_unregister_doc},
{"poll", (PyCFunction)devpoll_poll,
METH_VARARGS, devpoll_poll_doc},
+ {"close", (PyCFunction)devpoll_close, METH_NOARGS,
+ devpoll_close_doc},
+ {"fileno", (PyCFunction)devpoll_fileno, METH_NOARGS,
+ devpoll_fileno_doc},
{NULL, NULL} /* sentinel */
};
+static PyGetSetDef devpoll_getsetlist[] = {
+ {"closed", (getter)devpoll_get_closed, NULL,
+ "True if the devpoll object is closed"},
+ {0},
+};
+
static devpollObject *
newDevPollObject(void)
{
@@ -957,15 +1022,26 @@ newDevPollObject(void)
return self;
}
+static int
+devpoll_internal_close(pyEpoll_Object *self)
+{
+ int save_errno = 0;
+ if (self->fd_devpoll >= 0) {
+ int fd = self->fd_devpoll;
+ self->fd_devpoll = -1;
+ Py_BEGIN_ALLOW_THREADS
+ if (close(fd) < 0)
+ save_errno = errno;
+ Py_END_ALLOW_THREADS
+ }
+ return save_errno;
+}
+
static void
devpoll_dealloc(devpollObject *self)
{
- Py_BEGIN_ALLOW_THREADS
- close(self->fd_devpoll);
- Py_END_ALLOW_THREADS
-
+ (void)devpoll_internal_close();
PyMem_DEL(self->fds);
-
PyObject_Del(self);
}
@@ -1001,6 +1077,8 @@ static PyTypeObject devpoll_Type = {
0, /*tp_iter*/
0, /*tp_iternext*/
devpoll_methods, /*tp_methods*/
+ 0, /* tp_members */
+ devpoll_getsetlist, /* tp_getset */
};
#endif /* HAVE_SYS_DEVPOLL_H */
@@ -1084,7 +1162,7 @@ static PyTypeObject pyEpoll_Type;
static PyObject *
pyepoll_err_closed(void)
{
- PyErr_SetString(PyExc_ValueError, "I/O operation on closed epoll fd");
+ PyErr_SetString(PyExc_ValueError, "I/O operation on closed epoll object");
return NULL;
}
@@ -1776,7 +1854,7 @@ static PyTypeObject kqueue_event_Type = {
static PyObject *
kqueue_queue_err_closed(void)
{
- PyErr_SetString(PyExc_ValueError, "I/O operation on closed kqueue fd");
+ PyErr_SetString(PyExc_ValueError, "I/O operation on closed kqueue object");
return NULL;
}