From: Ben Darnell Date: Sun, 4 Oct 2015 01:57:33 +0000 (-0400) Subject: Support other yieldables in to_asyncio_future. X-Git-Tag: v4.3.0b1~8^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5590741eaea09d2a5477a1a016334725e2f450a8;p=thirdparty%2Ftornado.git Support other yieldables in to_asyncio_future. --- diff --git a/tornado/platform/asyncio.py b/tornado/platform/asyncio.py index c95c207fb..bf0428ec5 100644 --- a/tornado/platform/asyncio.py +++ b/tornado/platform/asyncio.py @@ -199,10 +199,15 @@ def to_tornado_future(asyncio_future): def to_asyncio_future(tornado_future): - """Convert a `tornado.concurrent.Future` to an `asyncio.Future`. + """Convert a Tornado yieldable object to an `asyncio.Future`. .. versionadded:: 4.1 + + .. versionchanged:: 4.3 + Now accepts any yieldable object, not just + `tornado.concurrent.Future`. """ + tornado_future = convert_yielded(tornado_future) af = asyncio.Future() tornado.concurrent.chain_future(tornado_future, af) return af diff --git a/tornado/test/asyncio_test.py b/tornado/test/asyncio_test.py index 94a569104..52ba9d353 100644 --- a/tornado/test/asyncio_test.py +++ b/tornado/test/asyncio_test.py @@ -14,12 +14,16 @@ from __future__ import absolute_import, division, print_function, with_statement from tornado import gen from tornado.testing import AsyncTestCase, gen_test -from tornado.test.util import unittest, skipBefore33, exec_test +from tornado.test.util import unittest, skipBefore33, skipBefore35, exec_test try: - from tornado.platform.asyncio import asyncio, AsyncIOLoop + from tornado.platform.asyncio import asyncio except ImportError: asyncio = None +else: + from tornado.platform.asyncio import AsyncIOLoop, to_asyncio_future + # This is used in dynamically-evaluated code, so silence pyflakes. + to_asyncio_future skipIfNoSingleDispatch = unittest.skipIf( gen.singledispatch is None, "singledispatch module not present") @@ -61,3 +65,54 @@ class AsyncIOLoopTest(AsyncTestCase): """) result = yield namespace['f']() self.assertEqual(result, 42) + + @skipBefore35 + def test_asyncio_adapter(self): + # This test demonstrates that when using the asyncio coroutine + # runner (i.e. run_until_complete), the to_asyncio_future + # adapter is needed. No adapter is needed in the other direction, + # as demonstrated by other tests in the package. + @gen.coroutine + def tornado_coroutine(): + yield gen.Task(self.io_loop.add_callback) + raise gen.Return(42) + native_coroutine_without_adapter = exec_test(globals(), locals(), """ + async def native_coroutine_without_adapter(): + return await tornado_coroutine() + """)["native_coroutine_without_adapter"] + + native_coroutine_with_adapter = exec_test(globals(), locals(), """ + async def native_coroutine_with_adapter(): + return await to_asyncio_future(tornado_coroutine()) + """)["native_coroutine_with_adapter"] + + # Use the adapter, but two degrees from the tornado coroutine. + native_coroutine_with_adapter2 = exec_test(globals(), locals(), """ + async def native_coroutine_with_adapter2(): + return await to_asyncio_future(native_coroutine_without_adapter()) + """)["native_coroutine_with_adapter2"] + + # Tornado supports native coroutines both with and without adapters + self.assertEqual( + self.io_loop.run_sync(native_coroutine_without_adapter), + 42) + self.assertEqual( + self.io_loop.run_sync(native_coroutine_with_adapter), + 42) + self.assertEqual( + self.io_loop.run_sync(native_coroutine_with_adapter2), + 42) + + # Asyncio only supports coroutines that yield asyncio-compatible + # Futures. + with self.assertRaises(RuntimeError): + asyncio.get_event_loop().run_until_complete( + native_coroutine_without_adapter()) + self.assertEqual( + asyncio.get_event_loop().run_until_complete( + native_coroutine_with_adapter()), + 42) + self.assertEqual( + asyncio.get_event_loop().run_until_complete( + native_coroutine_with_adapter2()), + 42)