bpo-32568: make select.epoll() and its docs consistent (#7840)

* `flags` is indeed deprecated, but there is a validation on its value for
  backwards compatibility reasons.  This adds mention of this in the docs.
* The docs say that `sizehint` is deprecated and ignored, but it is still
  used when `epoll_create1()` is unavailable. This adds mention of this in
  the docs.
* `sizehint=-1` is acceptable again, and is replaced with `FD_SETSIZE-1`.
  This is needed to have a default value available at the Python level,
  since `FD_SETSIZE` is not exposed to Python. (see: bpo-31938)
* Reject `sizehint=0` since it is invalid to pass on to `epoll_create()`.

The relevant tests have also been updated.
This commit is contained in:
Tal Einat 2018-06-30 15:43:23 +03:00 committed by GitHub
parent 5bb5bbfca8
commit 0cdf5f4289
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 14 deletions

View File

@ -57,7 +57,16 @@ The module defines the following:
(Only supported on Linux 2.5.44 and newer.) Return an edge polling object, (Only supported on Linux 2.5.44 and newer.) Return an edge polling object,
which can be used as Edge or Level Triggered interface for I/O which can be used as Edge or Level Triggered interface for I/O
events. *sizehint* and *flags* are deprecated and completely ignored. events.
*sizehint* informs epoll about the expected number of events to be
registered. It must be positive, or `-1` to use the default. It is only
used on older systems where :c:func:`epoll_create1` is not available;
otherwise it has no effect (though its value is still checked).
*flags* is deprecated and completely ignored. However, when supplied, its
value must be ``0`` or ``select.EPOLL_CLOEXEC``, otherwise ``OSError`` is
raised.
See the :ref:`epoll-objects` section below for the methods supported by See the :ref:`epoll-objects` section below for the methods supported by
epolling objects. epolling objects.

View File

@ -74,11 +74,11 @@ class TestEPoll(unittest.TestCase):
ep.close() ep.close()
self.assertTrue(ep.closed) self.assertTrue(ep.closed)
self.assertRaises(ValueError, ep.fileno) self.assertRaises(ValueError, ep.fileno)
if hasattr(select, "EPOLL_CLOEXEC"): if hasattr(select, "EPOLL_CLOEXEC"):
select.epoll(select.EPOLL_CLOEXEC).close() select.epoll(-1, select.EPOLL_CLOEXEC).close()
select.epoll(flags=select.EPOLL_CLOEXEC).close() select.epoll(flags=select.EPOLL_CLOEXEC).close()
select.epoll(flags=0).close() select.epoll(flags=0).close()
self.assertRaises(OSError, select.epoll, flags=12356)
def test_badcreate(self): def test_badcreate(self):
self.assertRaises(TypeError, select.epoll, 1, 2, 3) self.assertRaises(TypeError, select.epoll, 1, 2, 3)
@ -88,6 +88,13 @@ class TestEPoll(unittest.TestCase):
self.assertRaises(TypeError, select.epoll, ['foo']) self.assertRaises(TypeError, select.epoll, ['foo'])
self.assertRaises(TypeError, select.epoll, {}) self.assertRaises(TypeError, select.epoll, {})
self.assertRaises(ValueError, select.epoll, 0)
self.assertRaises(ValueError, select.epoll, -2)
self.assertRaises(ValueError, select.epoll, sizehint=-2)
if hasattr(select, "EPOLL_CLOEXEC"):
self.assertRaises(OSError, select.epoll, flags=12356)
def test_context_manager(self): def test_context_manager(self):
with select.epoll(16) as ep: with select.epoll(16) as ep:
self.assertGreater(ep.fileno(), 0) self.assertGreater(ep.fileno(), 0)
@ -117,19 +124,19 @@ class TestEPoll(unittest.TestCase):
try: try:
# TypeError: argument must be an int, or have a fileno() method. # TypeError: argument must be an int, or have a fileno() method.
self.assertRaises(TypeError, ep.register, object(), self.assertRaises(TypeError, ep.register, object(),
select.EPOLLIN | select.EPOLLOUT) select.EPOLLIN | select.EPOLLOUT)
self.assertRaises(TypeError, ep.register, None, self.assertRaises(TypeError, ep.register, None,
select.EPOLLIN | select.EPOLLOUT) select.EPOLLIN | select.EPOLLOUT)
# ValueError: file descriptor cannot be a negative integer (-1) # ValueError: file descriptor cannot be a negative integer (-1)
self.assertRaises(ValueError, ep.register, -1, self.assertRaises(ValueError, ep.register, -1,
select.EPOLLIN | select.EPOLLOUT) select.EPOLLIN | select.EPOLLOUT)
# OSError: [Errno 9] Bad file descriptor # OSError: [Errno 9] Bad file descriptor
self.assertRaises(OSError, ep.register, 10000, self.assertRaises(OSError, ep.register, 10000,
select.EPOLLIN | select.EPOLLOUT) select.EPOLLIN | select.EPOLLOUT)
# registering twice also raises an exception # registering twice also raises an exception
ep.register(server, select.EPOLLIN | select.EPOLLOUT) ep.register(server, select.EPOLLIN | select.EPOLLOUT)
self.assertRaises(OSError, ep.register, server, self.assertRaises(OSError, ep.register, server,
select.EPOLLIN | select.EPOLLOUT) select.EPOLLIN | select.EPOLLOUT)
finally: finally:
ep.close() ep.close()
@ -160,9 +167,9 @@ class TestEPoll(unittest.TestCase):
ep = select.epoll(16) ep = select.epoll(16)
ep.register(server.fileno(), ep.register(server.fileno(),
select.EPOLLIN | select.EPOLLOUT | select.EPOLLET) select.EPOLLIN | select.EPOLLOUT | select.EPOLLET)
ep.register(client.fileno(), ep.register(client.fileno(),
select.EPOLLIN | select.EPOLLOUT | select.EPOLLET) select.EPOLLIN | select.EPOLLOUT | select.EPOLLET)
now = time.monotonic() now = time.monotonic()
events = ep.poll(1, 4) events = ep.poll(1, 4)

View File

@ -0,0 +1,2 @@
Make select.epoll() and its documentation consistent regarding *sizehint* and
*flags*.

View File

@ -1297,14 +1297,17 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd)
static PyObject * static PyObject *
pyepoll_new(PyTypeObject *type, PyObject *args, PyObject *kwds) pyepoll_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{ {
int flags = 0, sizehint = FD_SETSIZE - 1; int flags = 0, sizehint = -1;
static char *kwlist[] = {"sizehint", "flags", NULL}; static char *kwlist[] = {"sizehint", "flags", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:epoll", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:epoll", kwlist,
&sizehint, &flags)) &sizehint, &flags))
return NULL; return NULL;
if (sizehint < 0) { if (sizehint == -1) {
PyErr_SetString(PyExc_ValueError, "negative sizehint"); sizehint = FD_SETSIZE - 1;
}
else if (sizehint <= 0) {
PyErr_SetString(PyExc_ValueError, "sizehint must be positive or -1");
return NULL; return NULL;
} }
@ -1314,7 +1317,6 @@ pyepoll_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL; return NULL;
} }
#endif #endif
return newPyEpoll_Object(type, sizehint, -1); return newPyEpoll_Object(type, sizehint, -1);
} }