Use functools.singledispatch for convert_yielded when available.
Register a converter for asyncio.Future and add tests.
This commit is contained in:
parent
f7167d0583
commit
d6a940924b
|
@ -16,12 +16,12 @@ env:
|
|||
install:
|
||||
# always install unittest2 on py26 even if $DEPS is unset
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then travis_retry pip install unittest2; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2* && $DEPS == true ]]; then travis_retry pip install futures mock Monotime==1.0; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' && $DEPS == true ]]; then travis_retry pip install futures mock; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2* && $DEPS == true ]]; then travis_retry pip install futures mock Monotime==1.0 singledispatch; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 'pypy' && $DEPS == true ]]; then travis_retry pip install futures mock singledispatch; fi
|
||||
# TODO(bdarnell): pycares tests are currently disabled on travis due to ipv6 issues.
|
||||
#- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* && $DEPS == true ]]; then travis_retry pip install pycares; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 'pypy'* && $DEPS == true ]]; then travis_retry pip install pycurl; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.2' && $DEPS == true ]]; then travis_retry pip install mock; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.2' && $DEPS == true ]]; then travis_retry pip install mock singledispatch; fi
|
||||
# Twisted runs on 2.x and 3.3+, but is flaky on pypy.
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != '3.2' && $TRAVIS_PYTHON_VERSION != 'pypy'* && $DEPS == true ]]; then travis_retry travis_retry pip install Twisted; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.4' && $DEPS == true ]]; then travis_retry travis_retry pip install sphinx==1.2.2 sphinx_rtd_theme; fi
|
||||
|
|
|
@ -60,6 +60,14 @@ from tornado.ioloop import IOLoop
|
|||
from tornado.log import app_log
|
||||
from tornado import stack_context
|
||||
|
||||
try:
|
||||
from functools import singledispatch # py34+
|
||||
except ImportError as e:
|
||||
try:
|
||||
from singledispatch import singledispatch # backport
|
||||
except ImportError:
|
||||
singledispatch = None
|
||||
|
||||
|
||||
class KeyReuseError(Exception):
|
||||
pass
|
||||
|
@ -900,3 +908,6 @@ def convert_yielded(yielded):
|
|||
return yielded
|
||||
else:
|
||||
raise BadYieldError("yielded unknown object %r" % (yielded,))
|
||||
|
||||
if singledispatch is not None:
|
||||
convert_yielded = singledispatch(convert_yielded)
|
||||
|
|
|
@ -12,6 +12,8 @@ unfinished callbacks on the event loop that fail when it resumes)
|
|||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
import functools
|
||||
|
||||
import tornado.concurrent
|
||||
from tornado.gen import convert_yielded
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado import stack_context
|
||||
|
||||
|
@ -138,3 +140,11 @@ class AsyncIOLoop(BaseAsyncIOLoop):
|
|||
def initialize(self):
|
||||
super(AsyncIOLoop, self).initialize(asyncio.new_event_loop(),
|
||||
close_loop=True)
|
||||
|
||||
|
||||
if hasattr(convert_yielded, 'register'):
|
||||
@convert_yielded.register(asyncio.Future)
|
||||
def _(af):
|
||||
tf = tornado.concurrent.Future()
|
||||
tornado.concurrent.chain_future(af, tf)
|
||||
return tf
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from tornado import gen
|
||||
from tornado.testing import AsyncTestCase, gen_test
|
||||
from tornado.test.util import unittest
|
||||
|
||||
try:
|
||||
from tornado.platform.asyncio import asyncio, AsyncIOLoop
|
||||
except ImportError:
|
||||
asyncio = None
|
||||
|
||||
skipIfNoSingleDispatch = unittest.skipIf(
|
||||
gen.singledispatch is None, "singledispatch module not present")
|
||||
|
||||
@unittest.skipIf(asyncio is None, "asyncio module not present")
|
||||
class AsyncIOLoopTest(AsyncTestCase):
|
||||
def get_new_ioloop(self):
|
||||
io_loop = AsyncIOLoop()
|
||||
asyncio.set_event_loop(io_loop.asyncio_loop)
|
||||
return io_loop
|
||||
|
||||
def test_asyncio_callback(self):
|
||||
# Basic test that the asyncio loop is set up correctly.
|
||||
asyncio.get_event_loop().call_soon(self.stop)
|
||||
self.wait()
|
||||
|
||||
@skipIfNoSingleDispatch
|
||||
@gen_test
|
||||
def test_asyncio_future(self):
|
||||
# Test that we can yield an asyncio future from a tornado coroutine.
|
||||
# Without 'yield from', we must wrap coroutines in asyncio.async.
|
||||
x = yield asyncio.async(
|
||||
asyncio.get_event_loop().run_in_executor(None, lambda: 42))
|
||||
self.assertEqual(x, 42)
|
||||
|
||||
@unittest.skipIf(sys.version_info < (3, 3),
|
||||
'PEP 380 not available')
|
||||
@skipIfNoSingleDispatch
|
||||
@gen_test
|
||||
def test_asyncio_yield_from(self):
|
||||
# Test that we can use asyncio coroutines with 'yield from'
|
||||
# instead of asyncio.async(). This requires python 3.3 syntax.
|
||||
global_namespace = dict(globals(), **locals())
|
||||
local_namespace = {}
|
||||
exec(textwrap.dedent("""
|
||||
@gen.coroutine
|
||||
def f():
|
||||
event_loop = asyncio.get_event_loop()
|
||||
x = yield from event_loop.run_in_executor(None, lambda: 42)
|
||||
return x
|
||||
"""), global_namespace, local_namespace)
|
||||
result = yield local_namespace['f']()
|
||||
self.assertEqual(result, 42)
|
|
@ -22,6 +22,7 @@ TEST_MODULES = [
|
|||
'tornado.httputil.doctests',
|
||||
'tornado.iostream.doctests',
|
||||
'tornado.util.doctests',
|
||||
'tornado.test.asyncio_test',
|
||||
'tornado.test.auth_test',
|
||||
'tornado.test.concurrent_test',
|
||||
'tornado.test.curl_httpclient_test',
|
||||
|
|
6
tox.ini
6
tox.ini
|
@ -30,8 +30,8 @@ envlist =
|
|||
{py2,py3}-select,
|
||||
{py2,py26,py3}-full-twisted,
|
||||
py2-twistedlayered,
|
||||
{py3,py33}-asyncio,
|
||||
{py26,py2}-trollius,
|
||||
{py3,py33}-full-asyncio,
|
||||
{py26,py2}-full-trollius,
|
||||
|
||||
# Alternate Resolvers.
|
||||
{py2,py3}-full-{threadedresolver},
|
||||
|
@ -81,6 +81,8 @@ deps =
|
|||
{py2,py26,py27,pypy}-full: futures
|
||||
# mock became standard in py33
|
||||
{py2,py26,py27,pypy,py3,py32,pypy3}-full: mock
|
||||
# singledispatch became standard in py34
|
||||
{py2,py26,py27,pypy,py3,py32,py33}-full: singledispatch
|
||||
py33-asyncio: asyncio
|
||||
trollius: trollius
|
||||
py2-monotonic: Monotime
|
||||
|
|
Loading…
Reference in New Issue