From: Ben Darnell Date: Sun, 29 Jul 2018 17:59:39 +0000 (-0400) Subject: concurrent: Type-annotate module X-Git-Tag: v6.0.0b1~35^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4db70791b83e042c7f0e22dd182335ce328959dc;p=thirdparty%2Ftornado.git concurrent: Type-annotate module --- diff --git a/setup.cfg b/setup.cfg index 4097ecf48..9b302ac13 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,6 +10,9 @@ disallow_untyped_defs = True [mypy-tornado.escape] disallow_untyped_defs = True +[mypy-tornado.concurrent] +disallow_untyped_defs = True + # It's generally too tedious to require type annotations in tests, but # we do want to type check them as much as type inference allows. [mypy-tornado.test.util_test] @@ -20,3 +23,6 @@ check_untyped_defs = True [mypy-tornado.test.escape_test] check_untyped_defs = True + +[mypy-tornado.test.concurrent_test] +check_untyped_defs = True diff --git a/tornado/concurrent.py b/tornado/concurrent.py index 7c2679dd2..d003ce915 100644 --- a/tornado/concurrent.py +++ b/tornado/concurrent.py @@ -31,6 +31,12 @@ import asyncio from concurrent import futures import functools import sys +import types + +import typing +from typing import Any, Callable, Optional, Tuple + +_T = typing.TypeVar('_T') class ReturnValueIgnoredError(Exception): @@ -43,27 +49,27 @@ Future = asyncio.Future # noqa FUTURES = (futures.Future, Future) -def is_future(x): +def is_future(x: Any) -> bool: return isinstance(x, FUTURES) class DummyExecutor(object): - def submit(self, fn, *args, **kwargs): - future = Future() + def submit(self, fn: Callable[..., _T], *args: Any, **kwargs: Any) -> 'Future[_T]': + future = Future() # type: Future try: future_set_result_unless_cancelled(future, fn(*args, **kwargs)) except Exception: future_set_exc_info(future, sys.exc_info()) return future - def shutdown(self, wait=True): + def shutdown(self, wait: bool=True) -> None: pass dummy_executor = DummyExecutor() -def run_on_executor(*args, **kwargs): +def run_on_executor(*args: Any, **kwargs: Any) -> Callable: """Decorator to run a synchronous method asynchronously on an executor. The decorated method may be called with a ``callback`` keyword @@ -104,12 +110,14 @@ def run_on_executor(*args, **kwargs): The ``callback`` argument was removed. """ - def run_on_executor_decorator(fn): + # Fully type-checking decorators is tricky, and this one is + # discouraged anyway so it doesn't have all the generic magic. + def run_on_executor_decorator(fn: Callable) -> Callable[..., Future]: executor = kwargs.get("executor", "executor") @functools.wraps(fn) - def wrapper(self, *args, **kwargs): - async_future = Future() + def wrapper(self: Any, *args: Any, **kwargs: Any) -> Future: + async_future = Future() # type: Future conc_future = getattr(self, executor).submit(fn, self, *args, **kwargs) chain_future(conc_future, async_future) return async_future @@ -126,7 +134,7 @@ def run_on_executor(*args, **kwargs): _NO_RESULT = object() -def chain_future(a, b): +def chain_future(a: 'Future[_T]', b: 'Future[_T]') -> None: """Chain two futures together so that when one completes, so does the other. The result (success or failure) of ``a`` will be copied to ``b``, unless @@ -138,13 +146,13 @@ def chain_future(a, b): `concurrent.futures.Future`. """ - def copy(future): + def copy(future: 'Future[_T]') -> None: assert future is a if b.done(): return if (hasattr(a, 'exc_info') and - a.exc_info() is not None): - future_set_exc_info(b, a.exc_info()) + a.exc_info() is not None): # type: ignore + future_set_exc_info(b, a.exc_info()) # type: ignore elif a.exception() is not None: b.set_exception(a.exception()) else: @@ -157,7 +165,7 @@ def chain_future(a, b): IOLoop.current().add_future(a, copy) -def future_set_result_unless_cancelled(future, value): +def future_set_result_unless_cancelled(future: 'Future[_T]', value: _T) -> None: """Set the given ``value`` as the `Future`'s result, if not cancelled. Avoids asyncio.InvalidStateError when calling set_result() on @@ -169,7 +177,9 @@ def future_set_result_unless_cancelled(future, value): future.set_result(value) -def future_set_exc_info(future, exc_info): +def future_set_exc_info(future: 'Future[_T]', + exc_info: Tuple[Optional[type], Optional[BaseException], + Optional[types.TracebackType]]) -> None: """Set the given ``exc_info`` as the `Future`'s exception. Understands both `asyncio.Future` and Tornado's extensions to @@ -179,13 +189,16 @@ def future_set_exc_info(future, exc_info): """ if hasattr(future, 'set_exc_info'): # Tornado's Future - future.set_exc_info(exc_info) + future.set_exc_info(exc_info) # type: ignore else: # asyncio.Future + if exc_info[1] is None: + raise Exception("future_set_exc_info called with no exception") future.set_exception(exc_info[1]) -def future_add_done_callback(future, callback): +def future_add_done_callback(future: 'Future[_T]', + callback: Callable[['Future[_T]'], None]) -> None: """Arrange to call ``callback`` when ``future`` is complete. ``callback`` is invoked with one argument, the ``future``. diff --git a/tornado/test/concurrent_test.py b/tornado/test/concurrent_test.py index 9dbccbeee..db263d621 100644 --- a/tornado/test/concurrent_test.py +++ b/tornado/test/concurrent_test.py @@ -29,7 +29,7 @@ from tornado.testing import AsyncTestCase, bind_unused_port, gen_test class MiscFutureTest(AsyncTestCase): def test_future_set_result_unless_cancelled(self): - fut = Future() + fut = Future() # type: Future[int] future_set_result_unless_cancelled(fut, 42) self.assertEqual(fut.result(), 42) self.assertFalse(fut.cancelled()) @@ -69,7 +69,10 @@ class BaseCapClient(object): self.port = port def process_response(self, data): - status, message = re.match('(.*)\t(.*)\n', to_unicode(data)).groups() + m = re.match('(.*)\t(.*)\n', to_unicode(data)) + if m is None: + raise Exception("did not match") + status, message = m.groups() if status == 'ok': return message else: