diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2013-08-21 22:19:50 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2013-08-21 22:19:50 (GMT) |
commit | 13423c3726ac4aa42125ac49e6c5038a015fe3eb (patch) | |
tree | 6658b82985abb3f4c2a201cb2a6320dcc5391a66 | |
parent | 43f80e6c1f49c3590843d81a1b5ebbe1a2e1c62a (diff) | |
download | cpython-13423c3726ac4aa42125ac49e6c5038a015fe3eb.zip cpython-13423c3726ac4aa42125ac49e6c5038a015fe3eb.tar.gz cpython-13423c3726ac4aa42125ac49e6c5038a015fe3eb.tar.bz2 |
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.rst | 31 | ||||
-rw-r--r-- | Lib/test/test_devpoll.py | 25 | ||||
-rw-r--r-- | Lib/test/test_epoll.py | 25 | ||||
-rw-r--r-- | Lib/test/test_kqueue.py | 22 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/selectmodule.c | 92 |
6 files changed, 191 insertions, 7 deletions
diff --git a/Doc/library/select.rst b/Doc/library/select.rst index b73f11e..73b990d 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 ec350cd..167e0ee 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 7077a70..93a9e1d 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 5b33af2..930f088 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) @@ -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 414f05a..865725d 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; } |