mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
43f80e6c1f
commit
13423c3726
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue