]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
ioloop: Make asyncio the default when available 2072/head
authorBen Darnell <ben@bendarnell.com>
Sat, 3 Jun 2017 20:08:44 +0000 (16:08 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 4 Jun 2017 03:43:29 +0000 (23:43 -0400)
In addition to changing the configurable default, add a special case
in IOLoop.current() so that the current IOLoop will be backed by
the main asyncio event loop.

.travis.yml
tornado/ioloop.py
tornado/test/httpclient_test.py
tornado/test/ioloop_test.py
tornado/test/process_test.py
tox.ini

index 4b2ac2bcc48ff738bfaf6010cf19fee2c30aa44f..d2b549211e025e1ac4a83af38f5f54c42e05eafa 100644 (file)
@@ -61,13 +61,12 @@ script:
     - if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then python -bb $TARGET; fi
     - if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then python $TARGET --resolver=tornado.netutil.ThreadedResolver; fi
     - if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then python $TARGET --httpclient=tornado.curl_httpclient.CurlAsyncHTTPClient; fi
-    - if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then python $TARGET --ioloop_time_monotonic; fi
     - if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* ]]; then python $TARGET --ioloop=tornado.platform.twisted.TwistedIOLoop; fi
-    - if [[ $TRAVIS_PYTHON_VERSION == 3.4 || $TRAVIS_PYTHON_VERSION == 3.5 || $TRAVIS_PYTHON_VERSION == 3.6 ]]; then python $TARGET --ioloop=tornado.platform.asyncio.AsyncIOLoop; fi
+    - if [[ $TRAVIS_PYTHON_VERSION == 3.4 || $TRAVIS_PYTHON_VERSION == 3.5 || $TRAVIS_PYTHON_VERSION == 3.6 ]]; then python $TARGET --ioloop=tornado.ioloop.PollIOLoop; fi
     - if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then python $TARGET --ioloop=tornado.platform.asyncio.AsyncIOLoop; fi
     - if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then python $TARGET --resolver=tornado.platform.twisted.TwistedResolver; fi
+    - if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then python $TARGET --ioloop=tornado.ioloop.PollIOLoop --ioloop_time_monotonic; fi
     #- if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then python $TARGET --resolver=tornado.platform.caresresolver.CaresResolver; fi
-    - if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then python $TARGET --ioloop_time_monotonic; fi
     - if [[ $TRAVIS_PYTHON_VERSION != 'pypy3' ]]; then ../nodeps/bin/python -m tornado.test.runtests; fi
     # make coverage reports for Codecov to find
     - if [[ $TRAVIS_PYTHON_VERSION != nightly ]]; then coverage xml; fi
index 46a60fb36d1896e1b1f814484f9fbeabce2b71fb..0527e922200fe76a9e32607c0411168d1f729b4b 100644 (file)
@@ -61,6 +61,11 @@ if PY3:
 else:
     import thread
 
+try:
+    import asyncio
+except ImportError:
+    asyncio = None
+
 
 _POLL_TIMEOUT = 3600.0
 
@@ -242,7 +247,13 @@ class IOLoop(Configurable):
         """
         current = getattr(IOLoop._current, "instance", None)
         if current is None and instance:
-            current = IOLoop()
+            current = None
+            if asyncio is not None:
+                from tornado.platform.asyncio import AsyncIOLoop, AsyncIOMainLoop
+                if IOLoop.configured_class() is AsyncIOLoop:
+                    current = AsyncIOMainLoop()
+            if current is None:
+                current = IOLoop()
             if IOLoop._current.instance is not current:
                 raise RuntimeError("new IOLoop did not become current")
         return current
@@ -276,6 +287,9 @@ class IOLoop(Configurable):
 
     @classmethod
     def configurable_default(cls):
+        if asyncio is not None:
+            from tornado.platform.asyncio import AsyncIOLoop
+            return AsyncIOLoop
         return PollIOLoop
 
     def initialize(self, make_current=None):
index 1fa42b7ee2bc9bc45d1d13049ad9821d6be6a0b2..8281f39aaeb5657cb7519ce178620be53b2ec9cf 100644 (file)
@@ -582,16 +582,11 @@ class HTTPResponseTestCase(unittest.TestCase):
 
 class SyncHTTPClientTest(unittest.TestCase):
     def setUp(self):
-        if IOLoop.configured_class().__name__ in ('TwistedIOLoop',
-                                                  'AsyncIOMainLoop'):
+        if IOLoop.configured_class().__name__ == 'TwistedIOLoop':
             # TwistedIOLoop only supports the global reactor, so we can't have
             # separate IOLoops for client and server threads.
-            # AsyncIOMainLoop doesn't work with the default policy
-            # (although it could with some tweaks to this test and a
-            # policy that created loops for non-main threads).
             raise unittest.SkipTest(
-                'Sync HTTPClient not compatible with TwistedIOLoop or '
-                'AsyncIOMainLoop')
+                'Sync HTTPClient not compatible with TwistedIOLoop')
         self.server_ioloop = IOLoop()
 
         @gen.coroutine
index 318f3fad0932377d8d1c63e72a8ac8a1414538ce..942e33c2ccecfb01073c6c379977019f17fb7cab 100644 (file)
@@ -275,7 +275,9 @@ class TestIOLoop(AsyncTestCase):
         self.io_loop.call_later(0, results.append, 4)
         self.io_loop.call_later(0, self.stop)
         self.wait()
-        self.assertEqual(results, [1, 2, 3, 4])
+        # The asyncio event loop does not guarantee the order of these
+        # callbacks, but PollIOLoop does.
+        self.assertEqual(sorted(results), [1, 2, 3, 4])
 
     def test_add_timeout_return(self):
         # All the timeout methods return non-None handles that can be
@@ -699,10 +701,17 @@ class TestIOLoopConfiguration(unittest.TestCase):
         return native_str(subprocess.check_output(args)).strip()
 
     def test_default(self):
-        # The default is a subclass of PollIOLoop
-        is_poll = self.run_python(
-            'print(isinstance(IOLoop.current(), PollIOLoop))')
-        self.assertEqual(is_poll, 'True')
+        if asyncio is not None:
+            # When asyncio is available, it is used by default.
+            cls = self.run_python('print(classname(IOLoop.current()))')
+            self.assertEqual(cls, 'AsyncIOMainLoop')
+            cls = self.run_python('print(classname(IOLoop()))')
+            self.assertEqual(cls, 'AsyncIOLoop')
+        else:
+            # Otherwise, the default is a subclass of PollIOLoop
+            is_poll = self.run_python(
+                'print(isinstance(IOLoop.current(), PollIOLoop))')
+            self.assertEqual(is_poll, 'True')
 
     def test_explicit_select(self):
         # SelectIOLoop can always be configured explicitly.
@@ -716,7 +725,7 @@ class TestIOLoopConfiguration(unittest.TestCase):
         cls = self.run_python(
             'IOLoop.configure("tornado.platform.asyncio.AsyncIOLoop")',
             'print(classname(IOLoop.current()))')
-        self.assertEqual(cls, 'AsyncIOLoop')
+        self.assertEqual(cls, 'AsyncIOMainLoop')
 
     @unittest.skipIf(asyncio is None, "asyncio module not present")
     def test_asyncio_main(self):
index d8337f5d53590678387c11d26c26a7fd1a589474..8497a7d8f37e1662d9f3441d6e72d7c1cf14e766 100644 (file)
@@ -17,12 +17,15 @@ from tornado.testing import bind_unused_port, ExpectLog, AsyncTestCase, gen_test
 from tornado.test.util import unittest, skipIfNonUnix
 from tornado.web import RequestHandler, Application
 
+try:
+    import asyncio
+except ImportError:
+    asyncio = None
+
 
 def skip_if_twisted():
-    if IOLoop.configured_class().__name__.endswith(('TwistedIOLoop',
-                                                    'AsyncIOMainLoop')):
-        raise unittest.SkipTest("Process tests not compatible with "
-                                "TwistedIOLoop or AsyncIOMainLoop")
+    if IOLoop.configured_class().__name__.endswith('TwistedIOLoop'):
+        raise unittest.SkipTest("Process tests not compatible with TwistedIOLoop")
 
 # Not using AsyncHTTPTestCase because we need control over the IOLoop.
 
@@ -58,8 +61,10 @@ class ProcessTest(unittest.TestCase):
         super(ProcessTest, self).tearDown()
 
     def test_multi_process(self):
-        # This test can't work on twisted because we use the global reactor
-        # and have no way to get it back into a sane state after the fork.
+        # This test doesn't work on twisted because we use the global
+        # reactor and don't restore it to a sane state after the fork
+        # (asyncio has the same issue, but we have a special case in
+        # place for it).
         skip_if_twisted()
         with ExpectLog(gen_log, "(Starting .* processes|child .* exited|uncaught exception)"):
             self.assertFalse(IOLoop.initialized())
@@ -81,6 +86,10 @@ class ProcessTest(unittest.TestCase):
                 sock.close()
                 return
             try:
+                if asyncio is not None:
+                    # Reset the global asyncio event loop, which was put into
+                    # a broken state by the fork.
+                    asyncio.set_event_loop(asyncio.new_event_loop())
                 if id in (0, 1):
                     self.assertEqual(id, task_id())
                     server = HTTPServer(self.get_app())
diff --git a/tox.ini b/tox.ini
index 9e472ae85fee847275bf9c4f3cfec264f575ec7a..409f025e7d7419a35702201fd72d8c842e8e69f9 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -30,7 +30,7 @@ envlist =
         {py2,py3}-select,
         {py2,py3}-full-twisted,
         py2-twistedlayered,
-        {py3,py33}-full-asyncio,
+        py3-full-poll,
         py2-full-trollius,
 
         # Alternate Resolvers.
@@ -84,7 +84,6 @@ deps =
      {py2,py27,pypy,py3,pypy3}-full: mock
      # singledispatch became standard in py34.
      {py2,py27,pypy,py3,py33}-full: singledispatch
-     py33-asyncio: asyncio
      trollius: trollius
      py2-monotonic: monotonic
      sphinx: sphinx
@@ -120,13 +119,14 @@ commands =
          # implementations; this flag controls which client all the
          # other tests use.
          curl: --httpclient=tornado.curl_httpclient.CurlAsyncHTTPClient \
+         poll: --ioloop=tornado.ioloop.PollIOLoop \
          select: --ioloop=tornado.platform.select.SelectIOLoop \
          twisted: --ioloop=tornado.platform.twisted.TwistedIOLoop \
          twistedlayered: --ioloop=tornado.test.twisted_test.LayeredTwistedIOLoop --resolver=tornado.platform.twisted.TwistedResolver \
-         {asyncio,trollius}: --ioloop=tornado.platform.asyncio.AsyncIOLoop \
+         trollius: --ioloop=tornado.platform.asyncio.AsyncIOLoop \
          caresresolver: --resolver=tornado.platform.caresresolver.CaresResolver \
          threadedresolver: --resolver=tornado.netutil.ThreadedResolver \
-         monotonic: --ioloop_time_monotonic \
+         monotonic: --ioloop=tornado.ioloop.PollIOLoop --ioloop_time_monotonic \
          # Test with a non-english locale to uncover str/bytes mixing issues.
          locale: --locale=zh_TW \
          {posargs:}