Commit d099423c authored by Jason Madden's avatar Jason Madden

Fix tests on windows, where SelectSelector._select is a normal method.

parent 3d4404d2
......@@ -28,14 +28,17 @@ else:
try:
from select import poll as original_poll
from select import POLLIN, POLLOUT, POLLNVAL
__implements__ = ['select', 'poll']
except ImportError:
original_poll = None
POLLIN = 1
POLLOUT = 4
POLLNVAL = 32
__implements__ = ['select']
__all__ = ['error'] + __implements__
if 'poll' not in __all__:
__all__.append('poll')
import select as __select__
......@@ -184,121 +187,123 @@ def select(rlist, wlist, xlist, timeout=None): # pylint:disable=unused-argument
return result.select(rlist, wlist, timeout)
if original_poll is not None:
class PollResult(object):
__slots__ = ('events', 'event')
def __init__(self):
self.events = set()
self.event = Event()
class PollResult(object):
__slots__ = ('events', 'event')
def add_event(self, events, fd):
if events < 0:
result_flags = POLLNVAL
else:
result_flags = 0
if events & _EV_READ:
result_flags = POLLIN
if events & _EV_WRITE:
result_flags |= POLLOUT
def __init__(self):
self.events = set()
self.event = Event()
self.events.add((fd, result_flags))
self.event.set()
def add_event(self, events, fd):
if events < 0:
result_flags = POLLNVAL
else:
result_flags = 0
if events & _EV_READ:
result_flags = POLLIN
if events & _EV_WRITE:
result_flags |= POLLOUT
class poll(object):
self.events.add((fd, result_flags))
self.event.set()
class poll(object):
"""
An implementation of :class:`select.poll` that blocks only the current greenlet.
.. caution:: ``POLLPRI`` data is not supported.
.. versionadded:: 1.1b1
.. versionchanged:: 1.5
This is now always defined, regardless of whether the standard library
defines :func:`select.poll` or not. Note that it may have different performance
characteristics.
"""
def __init__(self):
# {int -> flags}
# We can't keep watcher objects in here because people commonly
# just drop the poll object when they're done, without calling
# unregister(). dnspython does this.
self.fds = {}
self.loop = get_hub().loop
def register(self, fd, eventmask=_NONE):
if eventmask is _NONE:
flags = _EV_READ | _EV_WRITE
else:
flags = 0
if eventmask & POLLIN:
flags = _EV_READ
if eventmask & POLLOUT:
flags |= _EV_WRITE
# If they ask for POLLPRI, we can't support
# that. Should we raise an error?
fileno = get_fileno(fd)
self.fds[fileno] = flags
def modify(self, fd, eventmask):
self.register(fd, eventmask)
def poll(self, timeout=None):
"""
An implementation of :class:`select.poll` that blocks only the current greenlet.
poll the registered fds.
.. caution:: ``POLLPRI`` data is not supported.
.. versionchanged:: 1.2a1
File descriptors that are closed are reported with POLLNVAL.
.. versionadded:: 1.1b1
.. versionchanged:: 1.3a2
Under libuv, interpret *timeout* values less than 0 the same as *None*,
i.e., block. This was always the case with libev.
"""
def __init__(self):
# {int -> flags}
# We can't keep watcher objects in here because people commonly
# just drop the poll object when they're done, without calling
# unregister(). dnspython does this.
self.fds = {}
self.loop = get_hub().loop
def register(self, fd, eventmask=_NONE):
if eventmask is _NONE:
flags = _EV_READ | _EV_WRITE
else:
flags = 0
if eventmask & POLLIN:
flags = _EV_READ
if eventmask & POLLOUT:
flags |= _EV_WRITE
# If they ask for POLLPRI, we can't support
# that. Should we raise an error?
fileno = get_fileno(fd)
self.fds[fileno] = flags
def modify(self, fd, eventmask):
self.register(fd, eventmask)
def poll(self, timeout=None):
"""
poll the registered fds.
.. versionchanged:: 1.2a1
File descriptors that are closed are reported with POLLNVAL.
.. versionchanged:: 1.3a2
Under libuv, interpret *timeout* values less than 0 the same as *None*,
i.e., block. This was always the case with libev.
"""
result = PollResult()
watchers = []
io = self.loop.io
MAXPRI = self.loop.MAXPRI
try:
for fd, flags in iteritems(self.fds):
watcher = io(fd, flags)
watchers.append(watcher)
watcher.priority = MAXPRI
watcher.start(result.add_event, fd, pass_events=True)
if timeout is not None:
if timeout < 0:
# The docs for python say that an omitted timeout,
# a negative timeout and a timeout of None are all
# supposed to block forever. Many, but not all
# OS's accept any negative number to mean that. Some
# OS's raise errors for anything negative but not -1.
# Python 3.7 changes to always pass exactly -1 in that
# case from selectors.
# Our Timeout class currently does not have a defined behaviour
# for negative values. On libuv, it uses a check watcher and effectively
# doesn't block. On libev, it seems to block. In either case, we
# *want* to block, so turn this into the sure fire block request.
timeout = None
elif timeout:
# The docs for poll.poll say timeout is in
# milliseconds. Our result objects work in
# seconds, so this should be *=, shouldn't it?
timeout /= 1000.0
result.event.wait(timeout=timeout)
return list(result.events)
finally:
for awatcher in watchers:
awatcher.stop()
awatcher.close()
def unregister(self, fd):
"""
Unregister the *fd*.
.. versionchanged:: 1.2a1
Raise a `KeyError` if *fd* was not registered, like the standard
library. Previously gevent did nothing.
"""
fileno = get_fileno(fd)
del self.fds[fileno]
del original_poll
result = PollResult()
watchers = []
io = self.loop.io
MAXPRI = self.loop.MAXPRI
try:
for fd, flags in iteritems(self.fds):
watcher = io(fd, flags)
watchers.append(watcher)
watcher.priority = MAXPRI
watcher.start(result.add_event, fd, pass_events=True)
if timeout is not None:
if timeout < 0:
# The docs for python say that an omitted timeout,
# a negative timeout and a timeout of None are all
# supposed to block forever. Many, but not all
# OS's accept any negative number to mean that. Some
# OS's raise errors for anything negative but not -1.
# Python 3.7 changes to always pass exactly -1 in that
# case from selectors.
# Our Timeout class currently does not have a defined behaviour
# for negative values. On libuv, it uses a check watcher and effectively
# doesn't block. On libev, it seems to block. In either case, we
# *want* to block, so turn this into the sure fire block request.
timeout = None
elif timeout:
# The docs for poll.poll say timeout is in
# milliseconds. Our result objects work in
# seconds, so this should be *=, shouldn't it?
timeout /= 1000.0
result.event.wait(timeout=timeout)
return list(result.events)
finally:
for awatcher in watchers:
awatcher.stop()
awatcher.close()
def unregister(self, fd):
"""
Unregister the *fd*.
.. versionchanged:: 1.2a1
Raise a `KeyError` if *fd* was not registered, like the standard
library. Previously gevent did nothing.
"""
fileno = get_fileno(fd)
del self.fds[fileno]
def _gevent_do_monkey_patch(patch_request):
......@@ -323,13 +328,19 @@ def _gevent_do_monkey_patch(patch_request):
# package? If so, must be careful to deal with DoNotPatch exceptions.
# Python 3 wants to use `select.select` as a member function,
# leading to this error in selectors.py (because gevent.select.select is
# not a builtin and doesn't get the magic auto-static that they do)
# leading to this error in selectors.py (because
# gevent.select.select is not a builtin and doesn't get the
# magic auto-static that they do):
#
# r, w, _ = self._select(self._readers, self._writers, [], timeout)
# TypeError: select() takes from 3 to 4 positional arguments but 5 were given
# Note that this obviously only happens if selectors was imported after we had patched
# select; but there is a code path that leads to it being imported first (but now we've
# patched select---so we can't compare them identically)
#
# Note that this obviously only happens if selectors was
# imported after we had patched select; but there is a code
# path that leads to it being imported first (but now we've
# patched select---so we can't compare them identically). It also doesn't
# happen on Windows, because they define a normal method for _select, to work around
# some weirdness in the handling of the third argument.
orig_select_select = patch_request.get_original('select', 'select')
assert target_mod.select is not orig_select_select
......
......@@ -21,6 +21,9 @@ patch_all()
)
class TestSelectors(greentest.TestCase):
@greentest.skipOnWindows(
"SelectSelector._select is a normal function on Windows"
)
def test_selectors_select_is_patched(self):
# https://github.com/gevent/gevent/issues/835
_select = selectors.SelectSelector._select
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment