Change make_selector() to use DefaultSelector() path, add unique cases tests
MODIFY
test/test_selectors.py
test/test_selectors.py
@@ -2,6 +2,7 @@ from __future__ import with_statement
import errno
import os
import psutil
+import select
import signal
import socket
import sys
@@ -120,7 +121,6 @@ class TimerMixin(object):
@skipUnless(selectors.HAS_SELECT, "Platform doesn't have a selector")
class BaseSelectorTestCase(unittest.TestCase, AlarmMixin, TimerMixin):
""" Implements the tests that each type of selector must pass. """
- SELECTOR = selectors.DefaultSelector
def make_socketpair(self):
rd, wr = socketpair()
@@ -135,10 +135,18 @@ class BaseSelectorTestCase(unittest.TestCase, AlarmMixin, TimerMixin):
return rd, wr
def make_selector(self):
- s = self.SELECTOR()
+ s = selectors.DefaultSelector()
self.addCleanup(s.close)
return s
+ def setup_select_module(self, selector):
+ selectors._DEFAULT_SELECTOR = None
+ for s in ['select', 'poll', 'epoll', 'kqueue']:
+ if s != selector and hasattr(select, s):
+ old_selector = getattr(select, s)
+ delattr(select, s)
+ self.addCleanup(setattr, select, s, old_selector)
+
def standard_setup(self):
s = self.make_selector()
rd, wr = self.make_socketpair()
@@ -681,24 +689,93 @@ class ScalableSelectorMixin(object):
self.assertEqual(limit_nofile // 2, len(s.select()))
+@skipUnless(selectors.HAS_SELECT, "Platform doesn't have a selector.")
+class TestUniqueSelectScenarios(BaseSelectorTestCase):
+ def test_select_module_patched_after_import(self):
+ # This test is to make sure that after import time
+ # calling DefaultSelector() will still give a good
+ # return value. This issue is caused by gevent, eventlet.
+
+ # Now remove all selectors except `select.select`.
+ self.setup_select_module('select')
+
+ # Make sure that the selector returned only uses the selector available.
+ selector = self.make_selector()
+ self.assertIsInstance(selector, selectors.SelectSelector)
+
+ @skipUnless(hasattr(errno, 'ENOSYS'), 'Platform does not have errno.ENOSYS.')
+ def test_select_module_defines_does_not_implement_poll(self):
+ # This test is to make sure that if a platform defines
+ # a selector as being available but does not actually
+ # implement it (kennethreitz/requests#3906) does not
+ # make DefaultSelector() fail.
+
+ # Reset the _DEFAULT_SELECTOR value as if using for the first time.
+ selectors._DEFAULT_SELECTOR = None
+
+ had_poll = hasattr(select, 'poll')
+
+ # Remove all selectors except `select.select`.
+ self.setup_select_module('select')
+
+ # Now we're going to patch in a bad `poll`.
+ class BadPoll(object):
+ def poll(self, timeout):
+ raise OSError(errno.ENOSYS)
+
+ select.poll = BadPoll
+ if not had_poll:
+ self.addCleanup(delattr, select, 'poll')
+
+ selector = self.make_selector()
+ self.assertIsInstance(selector, selectors.SelectSelector)
+
+ @skipUnless(hasattr(errno, 'ENOSYS'), 'Platform does not have errno.ENOSYS.')
+ def test_select_module_defines_does_not_implement_epoll(self):
+ # Same as above test except with `select.epoll`.
+
+ # Reset the _DEFAULT_SELECTOR value as if using for the first time.
+ selectors._DEFAULT_SELECTOR = None
+
+ had_epoll = hasattr(select, 'epoll')
+
+ # Remove all selectors except `select.select`.
+ self.setup_select_module('select')
+
+ # Now we're going to patch in a bad `epoll`.
+ def bad_epoll(*args, **kwargs):
+ raise OSError(errno.ENOSYS)
+
+ select.epoll = bad_epoll
+ if not had_epoll:
+ self.addCleanup(delattr, select, 'epoll')
+
+ selector = self.make_selector()
+ self.assertIsInstance(selector, selectors.SelectSelector)
+
+
@skipUnless(hasattr(selectors, "SelectSelector"), "Platform doesn't have a SelectSelector")
class SelectSelectorTestCase(BaseSelectorTestCase):
- SELECTOR = getattr(selectors, "SelectSelector", None)
+ def setUp(self):
+ self.setup_select_module('select')
@skipUnless(hasattr(selectors, "PollSelector"), "Platform doesn't have a PollSelector")
class PollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixin):
- SELECTOR = getattr(selectors, "PollSelector", None)
+ def setUp(self):
+ self.setup_select_module('poll')
@skipUnless(hasattr(selectors, "EpollSelector"), "Platform doesn't have an EpollSelector")
class EpollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixin):
- SELECTOR = getattr(selectors, "EpollSelector", None)
+ def setUp(self):
+ self.setup_select_module('epoll')
@skipUnless(hasattr(selectors, "KqueueSelector"), "Platform doesn't have a KqueueSelector")
class KqueueSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixin):
- SELECTOR = getattr(selectors, "KqueueSelector", None)
+ def setUp(self):
+ self.setup_select_module('kqueue')
@skipUnless(hasattr(selectors, "SelectSelector"), "Platform doesn't have a SelectSelector")

MODIFY
urllib3/util/selectors.py
urllib3/util/selectors.py
@@ -23,6 +23,7 @@ EVENT_WRITE = (1 << 1)
HAS_SELECT = True # Variable that shows whether the platform has a selector.
_SYSCALL_SENTINEL = object() # Sentinel in case a system call returns None.
+_DEFAULT_SELECTOR = None
class SelectorError(Exception):
@@ -547,39 +548,34 @@ def _can_allocate(struct):
try:
# select.poll() objects won't fail until used.
if struct == 'poll':
- poll = getattr(select, struct)()
- try:
- poll.poll()
- finally:
- poll.close()
+ p = select.poll()
+ p.poll(0)
+
# All others will fail on allocation.
else:
getattr(select, struct)().close()
return True
- except (OSError, AttributeError):
+ except (OSError, AttributeError) as e:
return False
# Choose the best implementation, roughly:
# kqueue == epoll > poll > select. Devpoll not supported. (See above)
# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
-def _default_selector(*_):
+def DefaultSelector(*_):
""" This function serves as a first call for DefaultSelector to
detect if the select module is being monkey-patched incorrectly
by eventlet, greenlet, and preserve proper behavior. """
- global DefaultSelector
+ global _DEFAULT_SELECTOR
+ if _DEFAULT_SELECTOR is None:
if _can_allocate('kqueue'): # Platform-specific: Mac OS
- selector_type = KqueueSelector
+ _DEFAULT_SELECTOR = KqueueSelector
elif _can_allocate('epoll'): # Platform-specific: Linux
- selector_type = EpollSelector
+ _DEFAULT_SELECTOR = EpollSelector
elif _can_allocate('poll'): # Platform-specific: Linux
- selector_type = PollSelector
+ _DEFAULT_SELECTOR = PollSelector
elif hasattr(select, 'select'): # Platform-specific: Windows
- selector_type = SelectSelector
+ _DEFAULT_SELECTOR = SelectSelector
else: # Platform-specific: AppEngine
raise ValueError('Platform does not have a selector')
- DefaultSelector = selector_type
- return selector_type()
-
-
-DefaultSelector = _default_selector
+ return _DEFAULT_SELECTOR()

Change make_selector() to use DefaultSelector() path, add unique cases tests