IOLoop poller implementations are now subclasses of IOLoop.

Update testing command-line flags to allow configuration of a non-default
IOLoop.
This commit is contained in:
Ben Darnell 2012-09-30 22:57:32 -07:00
parent 91c5eb83f9
commit 469e227402
5 changed files with 74 additions and 38 deletions

View File

@ -103,7 +103,20 @@ class IOLoop(Configurable):
@classmethod
def configurable_default(cls):
return IOLoop
if hasattr(select, "epoll"):
# Python 2.6+ on Linux
return EPollIOLoop
elif hasattr(select, "kqueue"):
# Python 2.6+ on BSD or Mac
return KQueueIOLoop
else:
try:
# Python 2.5 on Linux with our C module installed
from tornado import epoll
return EPoll25IOLoop
except Exception:
# Everything else
return SelectIOLoop
# Constants from the epoll module
_EPOLLIN = 0x001
@ -126,8 +139,8 @@ class IOLoop(Configurable):
_current = threading.local()
def initialize(self, impl=None):
self._impl = impl or _poll()
def initialize(self, impl):
self._impl = impl
if hasattr(self._impl, 'fileno'):
set_close_exec(self._impl.fileno())
self._handlers = {}
@ -643,6 +656,8 @@ class _EPoll(object):
_EPOLL_CTL_MOD = 3
def __init__(self):
from tornado import epoll
self.epoll = epoll
self._epoll_fd = epoll.epoll_create()
def fileno(self):
@ -652,16 +667,21 @@ class _EPoll(object):
os.close(self._epoll_fd)
def register(self, fd, events):
epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_ADD, fd, events)
self.epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_ADD, fd, events)
def modify(self, fd, events):
epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_MOD, fd, events)
self.epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_MOD, fd, events)
def unregister(self, fd):
epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_DEL, fd, 0)
self.epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_DEL, fd, 0)
def poll(self, timeout):
return epoll.epoll_wait(self._epoll_fd, int(timeout * 1000))
return self.epoll.epoll_wait(self._epoll_fd, int(timeout * 1000))
class EPoll25IOLoop(IOLoop):
def initialize(self, **kwargs):
super(EPoll25IOLoop, self).initialize(impl=_EPoll(), **kwargs)
class _KQueue(object):
@ -728,6 +748,11 @@ class _KQueue(object):
return events.items()
class KQueueIOLoop(IOLoop):
def initialize(self, **kwargs):
super(KQueueIOLoop, self).initialize(impl=_KQueue(), **kwargs)
class _Select(object):
"""A simple, select()-based IOLoop implementation for non-Linux systems"""
def __init__(self):
@ -774,23 +799,11 @@ class _Select(object):
events[fd] = events.get(fd, 0) | IOLoop.ERROR
return events.items()
class SelectIOLoop(IOLoop):
def initialize(self, **kwargs):
super(SelectIOLoop, self).initialize(impl=_Select(), **kwargs)
# Choose a poll implementation. Use epoll if it is available, fall back to
# select() for non-Linux platforms
if hasattr(select, "epoll"):
# Python 2.6+ on Linux
_poll = select.epoll
elif hasattr(select, "kqueue"):
# Python 2.6+ on BSD or Mac
_poll = _KQueue
else:
try:
# Linux systems with our C module installed
from tornado import epoll
_poll = _EPoll
except Exception:
# All other systems
import sys
if "linux" in sys.platform:
gen_log.warning("epoll module not found; using select()")
_poll = _Select
class EPollIOLoop(IOLoop):
def initialize(self, **kwargs):
super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)

View File

@ -4,6 +4,9 @@ from __future__ import absolute_import, division, with_statement
import logging
import textwrap
import sys
from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
from tornado.options import define
from tornado.test.util import unittest
TEST_MODULES = [
@ -76,6 +79,11 @@ if __name__ == '__main__':
logging.getLogger("tornado.access").setLevel(logging.CRITICAL)
define('httpclient', type=str, default=None,
callback=AsyncHTTPClient.configure)
define('ioloop', type=str, default=None,
callback=IOLoop.configure)
import tornado.testing
kwargs = {}
if sys.version_info >= (3, 2):

View File

@ -471,9 +471,6 @@ def main(**kwargs):
"""
from tornado.options import define, options, parse_command_line
define('autoreload', type=bool, default=False,
help="DEPRECATED: use tornado.autoreload.main instead")
define('httpclient', type=str, default=None)
define('exception_on_interrupt', type=bool, default=True,
help=("If true (default), ctrl-c raises a KeyboardInterrupt "
"exception. This prints a stack trace but cannot interrupt "
@ -489,10 +486,6 @@ def main(**kwargs):
argv = [sys.argv[0]] + parse_command_line(sys.argv)
if options.httpclient:
from tornado.httpclient import AsyncHTTPClient
AsyncHTTPClient.configure(options.httpclient)
if not options.exception_on_interrupt:
signal.signal(signal.SIGINT, signal.SIG_DFL)
@ -526,11 +519,7 @@ def main(**kwargs):
gen_log.info('PASS')
else:
gen_log.error('FAIL')
if not options.autoreload:
raise
if options.autoreload:
import tornado.autoreload
tornado.autoreload.wait()
if __name__ == '__main__':
main()

11
tox.ini
View File

@ -74,6 +74,17 @@ deps =
twisted>=11.1.0
commands = python -m tornado.test.runtests --httpclient=tornado.curl_httpclient.CurlAsyncHTTPClient {posargs:}
[testenv:py27-select]
# Same as py27-full, but runs the tests with the select IOLoop.
# The other tests will run with the most platform-appropriate implementation,
# but this one is the lowest common denominator and should work anywhere.
basepython = python2.7
deps =
futures
pycurl
twisted>=12.0.0
commands = python -m tornado.test.runtests --ioloop=tornado.ioloop.SelectIOLoop {posargs:}
[testenv:pypy-full]
# This configuration works with pypy 1.9. pycurl installs ok but
# curl_httpclient doesn't work. Twisted works most of the time, but

View File

@ -104,3 +104,18 @@ In progress
argument.
* Keyword arguments to `AsyncHTTPClient.configure` are no longer used
when instantiating an implementation subclass directly.
* The `IOLoop` poller implementations (``select``, ``epoll``, ``kqueue``)
are now available as distinct subclasses of `IOLoop`. Instantiating
`IOLoop` will continue to automatically choose the best available
implementation.
* `IOLoop` now has a static ``configure`` method like the one on
`AsyncHTTPClient`, which can be used to select an IOLoop implementation
other than the default.
* The deprecated ``--autoreload`` option of `tornado.testing.main` has
been removed. Use ``python -m tornado.autoreload`` as a prefix command
instead.
* The ``--httpclient`` option of `tornado.testing.main` has been moved
to `tornado.test.runtests` so as not to pollute the application
option namespace. The `tornado.options` module's new callback
support now makes it easy to add options from a wrapper script
instead of putting all possible options in `tornado.testing.main`.