[Python-checkins] cpython: Close #18794: Add a fileno() method and a closed attribute to select.devpoll

victor.stinner python-checkins at python.org
Thu Aug 22 00:21:05 CEST 2013


http://hg.python.org/cpython/rev/ccbe2132392b
changeset:   85300:ccbe2132392b
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Thu Aug 22 00:19:50 2013 +0200
summary:
  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.

files:
  Doc/library/select.rst   |  31 +++++++++
  Lib/test/test_devpoll.py |  25 +++++++
  Lib/test/test_epoll.py   |  25 +++++++
  Lib/test/test_kqueue.py  |  22 ++++++
  Misc/NEWS                |   3 +
  Modules/selectmodule.c   |  92 +++++++++++++++++++++++++--
  6 files changed, 191 insertions(+), 7 deletions(-)


diff --git a/Doc/library/select.rst b/Doc/library/select.rst
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -147,6 +147,27 @@
 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 @@
    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 @@
    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
--- a/Lib/test/test_devpoll.py
+++ b/Lib/test/test_devpoll.py
@@ -87,6 +87,31 @@
         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
--- a/Lib/test/test_epoll.py
+++ b/Lib/test/test_epoll.py
@@ -225,6 +225,31 @@
         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
--- a/Lib/test/test_kqueue.py
+++ b/Lib/test/test_kqueue.py
@@ -185,6 +185,28 @@
         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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -38,6 +38,9 @@
 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
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -684,6 +684,13 @@
 
 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 @@
     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 @@
 {
     int fd;
 
+    if (self->fd_devpoll < 0)
+        return devpoll_err_closed();
+
     fd = PyObject_AsFileDescriptor( o );
     if (fd == -1)
         return NULL;
@@ -817,6 +830,9 @@
     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 @@
     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 @@
      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 @@
     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 @@
     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 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 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;
 }
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list