]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
ioloop,platform: Add type annotations
authorBen Darnell <ben@bendarnell.com>
Sun, 12 Aug 2018 03:00:41 +0000 (23:00 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 12 Aug 2018 03:15:43 +0000 (23:15 -0400)
setup.cfg
tornado/ioloop.py
tornado/netutil.py
tornado/platform/asyncio.py
tornado/platform/caresresolver.py
tornado/platform/interface.py
tornado/platform/posix.py
tornado/platform/twisted.py
tornado/platform/windows.py
tornado/test/ioloop_test.py
tornado/testing.py

index 433b6d0d3a9a42a7ea58a76547185ed0d201e281..cde5d24f27b7b9f0b701a08bc815c2ca033cb37e 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -19,6 +19,9 @@ disallow_untyped_defs = True
 [mypy-tornado.gen]
 disallow_untyped_defs = True
 
+[mypy-tornado.ioloop]
+disallow_untyped_defs = True
+
 [mypy-tornado.locale]
 disallow_untyped_defs = True
 
@@ -31,6 +34,9 @@ disallow_untyped_defs = True
 [mypy-tornado.options]
 disallow_untyped_defs = True
 
+[mypy-tornado.platform.*]
+disallow_untyped_defs = True
+
 [mypy-tornado.testing]
 disallow_untyped_defs = True
 
@@ -45,12 +51,18 @@ check_untyped_defs = True
 [mypy-tornado.test.escape_test]
 check_untyped_defs = True
 
+[mypy-tornado.test.asyncio_test]
+check_untyped_defs = True
+
 [mypy-tornado.test.concurrent_test]
 check_untyped_defs = True
 
 [mypy-tornado.test.gen_test]
 check_untyped_defs = True
 
+[mypy-tornado.test.ioloop_test]
+check_untyped_defs = True
+
 [mypy-tornado.test.locale_test]
 check_untyped_defs = True
 
index 51b5dd1e4c1472c4dba62567299ef796337f6bf6..c9dfbedb8c312533a68e339d75febd3ce7a72051 100644 (file)
@@ -31,7 +31,7 @@ events. `IOLoop.add_timeout` is a non-blocking alternative to
 """
 
 import asyncio
-from concurrent.futures import ThreadPoolExecutor
+import concurrent.futures
 import datetime
 import logging
 import numbers
@@ -43,9 +43,28 @@ import random
 
 from tornado.concurrent import Future, is_future, chain_future, future_set_exc_info, future_add_done_callback  # noqa: E501
 from tornado.log import app_log
-from tornado.util import Configurable, TimeoutError, unicode_type, import_object
+from tornado.util import Configurable, TimeoutError, import_object
 
-import typing  # noqa
+import typing
+from typing import Union, Any, Type, Optional, Callable, TypeVar, Tuple, Awaitable
+if typing.TYPE_CHECKING:
+    from typing import Dict, List  # noqa: F401
+
+    from typing_extensions import Protocol
+else:
+    Protocol = object
+
+
+class _Selectable(Protocol):
+    def fileno(self) -> int:
+        pass
+
+    def close(self) -> None:
+        pass
+
+
+_T = TypeVar('_T')
+_S = TypeVar('_S', bound=_Selectable)
 
 
 class IOLoop(Configurable):
@@ -137,22 +156,22 @@ class IOLoop(Configurable):
     ERROR = 0x018
 
     # In Python 3, _ioloop_for_asyncio maps from asyncio loops to IOLoops.
-    _ioloop_for_asyncio = dict()  # type: typing.Dict[typing.Any, typing.Any]
+    _ioloop_for_asyncio = dict()  # type: Dict[asyncio.AbstractEventLoop, IOLoop]
 
     @classmethod
-    def configure(cls, impl, **kwargs):
+    def configure(cls, impl: Union[None, str, Type[Configurable]], **kwargs: Any) -> None:
         if asyncio is not None:
             from tornado.platform.asyncio import BaseAsyncIOLoop
 
-            if isinstance(impl, (str, unicode_type)):
+            if isinstance(impl, str):
                 impl = import_object(impl)
-            if not issubclass(impl, BaseAsyncIOLoop):
+            if isinstance(impl, type) and not issubclass(impl, BaseAsyncIOLoop):
                 raise RuntimeError(
                     "only AsyncIOLoop is allowed when asyncio is available")
         super(IOLoop, cls).configure(impl, **kwargs)
 
     @staticmethod
-    def instance():
+    def instance() -> 'IOLoop':
         """Deprecated alias for `IOLoop.current()`.
 
         .. versionchanged:: 5.0
@@ -173,7 +192,7 @@ class IOLoop(Configurable):
         """
         return IOLoop.current()
 
-    def install(self):
+    def install(self) -> None:
         """Deprecated alias for `make_current()`.
 
         .. versionchanged:: 5.0
@@ -188,7 +207,7 @@ class IOLoop(Configurable):
         self.make_current()
 
     @staticmethod
-    def clear_instance():
+    def clear_instance() -> None:
         """Deprecated alias for `clear_current()`.
 
         .. versionchanged:: 5.0
@@ -203,8 +222,18 @@ class IOLoop(Configurable):
         """
         IOLoop.clear_current()
 
+    @typing.overload
+    @staticmethod
+    def current() -> 'IOLoop':
+        pass
+
+    @typing.overload  # noqa: F811
     @staticmethod
-    def current(instance=True):
+    def current(instance: bool=True) -> Optional['IOLoop']:
+        pass
+
+    @staticmethod  # noqa: F811
+    def current(instance: bool=True) -> Optional['IOLoop']:
         """Returns the current thread's `IOLoop`.
 
         If an `IOLoop` is currently running or has been marked as
@@ -235,12 +264,12 @@ class IOLoop(Configurable):
         except KeyError:
             if instance:
                 from tornado.platform.asyncio import AsyncIOMainLoop
-                current = AsyncIOMainLoop(make_current=True)
+                current = AsyncIOMainLoop(make_current=True)  # type: Optional[IOLoop]
             else:
                 current = None
         return current
 
-    def make_current(self):
+    def make_current(self) -> None:
         """Makes this the `IOLoop` for the current thread.
 
         An `IOLoop` automatically becomes current for its thread
@@ -260,7 +289,7 @@ class IOLoop(Configurable):
         raise NotImplementedError()
 
     @staticmethod
-    def clear_current():
+    def clear_current() -> None:
         """Clears the `IOLoop` for the current thread.
 
         Intended primarily for use by test frameworks in between tests.
@@ -274,7 +303,7 @@ class IOLoop(Configurable):
         if asyncio is None:
             IOLoop._current.instance = None
 
-    def _clear_current_hook(self):
+    def _clear_current_hook(self) -> None:
         """Instance method called when an IOLoop ceases to be current.
 
         May be overridden by subclasses as a counterpart to make_current.
@@ -282,15 +311,15 @@ class IOLoop(Configurable):
         pass
 
     @classmethod
-    def configurable_base(cls):
+    def configurable_base(cls) -> Type[Configurable]:
         return IOLoop
 
     @classmethod
-    def configurable_default(cls):
+    def configurable_default(cls) -> Type[Configurable]:
         from tornado.platform.asyncio import AsyncIOLoop
         return AsyncIOLoop
 
-    def initialize(self, make_current=None):
+    def initialize(self, make_current: bool=None) -> None:
         if make_current is None:
             if IOLoop.current(instance=False) is None:
                 self.make_current()
@@ -301,7 +330,7 @@ class IOLoop(Configurable):
                 raise RuntimeError("current IOLoop already exists")
             self.make_current()
 
-    def close(self, all_fds=False):
+    def close(self, all_fds: bool=False) -> None:
         """Closes the `IOLoop`, freeing any resources used.
 
         If ``all_fds`` is true, all file descriptors registered on the
@@ -328,13 +357,20 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def add_handler(self, fd, handler, events):
+    @typing.overload
+    def add_handler(self, fd: int, handler: Callable[[int, int], None], events: int) -> None:
+        pass
+
+    @typing.overload  # noqa: F811
+    def add_handler(self, fd: _S, handler: Callable[[_S, int], None], events: int) -> None:
+        pass
+
+    def add_handler(self, fd: Union[int, _Selectable],  # noqa: F811
+                    handler: Callable[..., None], events: int) -> None:
         """Registers the given handler to receive the given events for ``fd``.
 
         The ``fd`` argument may either be an integer file descriptor or
-        a file-like object with a ``fileno()`` method (and optionally a
-        ``close()`` method, which may be called when the `IOLoop` is shut
-        down).
+        a file-like object with a ``fileno()`` and ``close()`` method.
 
         The ``events`` argument is a bitwise or of the constants
         ``IOLoop.READ``, ``IOLoop.WRITE``, and ``IOLoop.ERROR``.
@@ -347,7 +383,7 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def update_handler(self, fd, events):
+    def update_handler(self, fd: Union[int, _Selectable], events: int) -> None:
         """Changes the events we listen for ``fd``.
 
         .. versionchanged:: 4.0
@@ -356,7 +392,7 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def remove_handler(self, fd):
+    def remove_handler(self, fd: Union[int, _Selectable]) -> None:
         """Stop listening for events on ``fd``.
 
         .. versionchanged:: 4.0
@@ -365,7 +401,7 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def start(self):
+    def start(self) -> None:
         """Starts the I/O loop.
 
         The loop will run until one of the callbacks calls `stop()`, which
@@ -373,7 +409,7 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def _setup_logging(self):
+    def _setup_logging(self) -> None:
         """The IOLoop catches and logs exceptions, so it's
         important that log output be visible.  However, python's
         default behavior for non-root loggers (prior to python
@@ -389,7 +425,7 @@ class IOLoop(Configurable):
                     logging.getLogger('tornado.application').handlers]):
             logging.basicConfig()
 
-    def stop(self):
+    def stop(self) -> None:
         """Stop the I/O loop.
 
         If the event loop is not currently running, the next call to `start()`
@@ -402,7 +438,7 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def run_sync(self, func, timeout=None):
+    def run_sync(self, func: Callable, timeout: float=None) -> Any:
         """Starts the `IOLoop`, runs the given function, and stops the loop.
 
         The function must return either an awaitable object or
@@ -432,42 +468,47 @@ class IOLoop(Configurable):
            If a timeout occurs, the ``func`` coroutine will be cancelled.
 
         """
-        future_cell = [None]
+        future_cell = [None]  # type: List[Optional[Future]]
 
-        def run():
+        def run() -> None:
             try:
                 result = func()
                 if result is not None:
                     from tornado.gen import convert_yielded
                     result = convert_yielded(result)
             except Exception:
-                future_cell[0] = Future()
-                future_set_exc_info(future_cell[0], sys.exc_info())
+                fut = Future()  # type: Future[Any]
+                future_cell[0] = fut
+                future_set_exc_info(fut, sys.exc_info())
             else:
                 if is_future(result):
                     future_cell[0] = result
                 else:
-                    future_cell[0] = Future()
-                    future_cell[0].set_result(result)
+                    fut = Future()
+                    future_cell[0] = fut
+                    fut.set_result(result)
+            assert future_cell[0] is not None
             self.add_future(future_cell[0], lambda future: self.stop())
         self.add_callback(run)
         if timeout is not None:
-            def timeout_callback():
+            def timeout_callback() -> None:
                 # If we can cancel the future, do so and wait on it. If not,
                 # Just stop the loop and return with the task still pending.
                 # (If we neither cancel nor wait for the task, a warning
                 # will be logged).
+                assert future_cell[0] is not None
                 if not future_cell[0].cancel():
                     self.stop()
             timeout_handle = self.add_timeout(self.time() + timeout, timeout_callback)
         self.start()
         if timeout is not None:
             self.remove_timeout(timeout_handle)
+        assert future_cell[0] is not None
         if future_cell[0].cancelled() or not future_cell[0].done():
             raise TimeoutError('Operation timed out after %s seconds' % timeout)
         return future_cell[0].result()
 
-    def time(self):
+    def time(self) -> float:
         """Returns the current time according to the `IOLoop`'s clock.
 
         The return value is a floating-point number relative to an
@@ -482,7 +523,9 @@ class IOLoop(Configurable):
         """
         return time.time()
 
-    def add_timeout(self, deadline, callback, *args, **kwargs):
+    def add_timeout(self, deadline: Union[float, datetime.timedelta],
+                    callback: Callable[..., None],
+                    *args: Any, **kwargs: Any) -> object:
         """Runs the ``callback`` at the time ``deadline`` from the I/O loop.
 
         Returns an opaque handle that may be passed to
@@ -516,7 +559,8 @@ class IOLoop(Configurable):
         else:
             raise TypeError("Unsupported deadline %r" % deadline)
 
-    def call_later(self, delay, callback, *args, **kwargs):
+    def call_later(self, delay: float, callback: Callable[..., None],
+                   *args: Any, **kwargs: Any) -> object:
         """Runs the ``callback`` after ``delay`` seconds have passed.
 
         Returns an opaque handle that may be passed to `remove_timeout`
@@ -529,7 +573,8 @@ class IOLoop(Configurable):
         """
         return self.call_at(self.time() + delay, callback, *args, **kwargs)
 
-    def call_at(self, when, callback, *args, **kwargs):
+    def call_at(self, when: float, callback: Callable[..., None],
+                *args: Any, **kwargs: Any) -> object:
         """Runs the ``callback`` at the absolute time designated by ``when``.
 
         ``when`` must be a number using the same reference point as
@@ -545,7 +590,7 @@ class IOLoop(Configurable):
         """
         return self.add_timeout(when, callback, *args, **kwargs)
 
-    def remove_timeout(self, timeout):
+    def remove_timeout(self, timeout: object) -> None:
         """Cancels a pending timeout.
 
         The argument is a handle as returned by `add_timeout`.  It is
@@ -554,7 +599,8 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def add_callback(self, callback, *args, **kwargs):
+    def add_callback(self, callback: Callable,
+                     *args: Any, **kwargs: Any) -> None:
         """Calls the given callback on the next I/O loop iteration.
 
         It is safe to call this method from any thread at any time,
@@ -569,7 +615,8 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def add_callback_from_signal(self, callback, *args, **kwargs):
+    def add_callback_from_signal(self, callback: Callable,
+                                 *args: Any, **kwargs: Any) -> None:
         """Calls the given callback on the next I/O loop iteration.
 
         Safe for use from a Python signal handler; should not be used
@@ -577,7 +624,8 @@ class IOLoop(Configurable):
         """
         raise NotImplementedError()
 
-    def spawn_callback(self, callback, *args, **kwargs):
+    def spawn_callback(self, callback: Callable,
+                       *args: Any, **kwargs: Any) -> None:
         """Calls the given callback on the next IOLoop iteration.
 
         As of Tornado 6.0, this method is equivalent to `add_callback`.
@@ -586,7 +634,8 @@ class IOLoop(Configurable):
         """
         self.add_callback(callback, *args, **kwargs)
 
-    def add_future(self, future, callback):
+    def add_future(self, future: Union['Future[_T]', 'concurrent.futures.Future[_T]'],
+                   callback: Callable[['Future[_T]'], None]) -> None:
         """Schedules a callback on the ``IOLoop`` when the given
         `.Future` is finished.
 
@@ -601,7 +650,8 @@ class IOLoop(Configurable):
         future_add_done_callback(
             future, lambda future: self.add_callback(callback, future))
 
-    def run_in_executor(self, executor, func, *args):
+    def run_in_executor(self, executor: Optional[concurrent.futures.Executor],
+                        func: Callable[..., _T], *args: Any) -> Awaitable[_T]:
         """Runs a function in a ``concurrent.futures.Executor``. If
         ``executor`` is ``None``, the IO loop's default executor will be used.
 
@@ -612,23 +662,24 @@ class IOLoop(Configurable):
         if executor is None:
             if not hasattr(self, '_executor'):
                 from tornado.process import cpu_count
-                self._executor = ThreadPoolExecutor(max_workers=(cpu_count() * 5))
+                self._executor = concurrent.futures.ThreadPoolExecutor(
+                    max_workers=(cpu_count() * 5))  # type: concurrent.futures.Executor
             executor = self._executor
         c_future = executor.submit(func, *args)
         # Concurrent Futures are not usable with await. Wrap this in a
         # Tornado Future instead, using self.add_future for thread-safety.
-        t_future = Future()
+        t_future = Future()  # type: Future[_T]
         self.add_future(c_future, lambda f: chain_future(f, t_future))
         return t_future
 
-    def set_default_executor(self, executor):
+    def set_default_executor(self, executor: concurrent.futures.Executor) -> None:
         """Sets the default executor to use with :meth:`run_in_executor`.
 
         .. versionadded:: 5.0
         """
         self._executor = executor
 
-    def _run_callback(self, callback):
+    def _run_callback(self, callback: Callable[[], Any]) -> None:
         """Runs a callback with error handling.
 
         For use in subclasses.
@@ -653,11 +704,11 @@ class IOLoop(Configurable):
         except Exception:
             app_log.error("Exception in callback %r", callback, exc_info=True)
 
-    def _discard_future_result(self, future):
+    def _discard_future_result(self, future: Future) -> None:
         """Avoid unhandled-exception warnings from spawned coroutines."""
         future.result()
 
-    def split_fd(self, fd):
+    def split_fd(self, fd: Union[int, _Selectable]) -> Tuple[int, Union[int, _Selectable]]:
         """Returns an (fd, obj) pair from an ``fd`` parameter.
 
         We accept both raw file descriptors and file-like objects as
@@ -673,12 +724,11 @@ class IOLoop(Configurable):
 
         .. versionadded:: 4.0
         """
-        try:
-            return fd.fileno(), fd
-        except AttributeError:
+        if isinstance(fd, int):
             return fd, fd
+        return fd.fileno(), fd
 
-    def close_fd(self, fd):
+    def close_fd(self, fd: Union[int, _Selectable]) -> None:
         """Utility method to close an ``fd``.
 
         If ``fd`` is a file-like object, we close it directly; otherwise
@@ -691,10 +741,10 @@ class IOLoop(Configurable):
         .. versionadded:: 4.0
         """
         try:
-            try:
-                fd.close()
-            except AttributeError:
+            if isinstance(fd, int):
                 os.close(fd)
+            else:
+                fd.close()
         except OSError:
             pass
 
@@ -705,21 +755,22 @@ class _Timeout(object):
     # Reduce memory overhead when there are lots of pending callbacks
     __slots__ = ['deadline', 'callback', 'tdeadline']
 
-    def __init__(self, deadline, callback, io_loop):
+    def __init__(self, deadline: float, callback: Callable[[], None],
+                 io_loop: IOLoop) -> None:
         if not isinstance(deadline, numbers.Real):
             raise TypeError("Unsupported deadline %r" % deadline)
         self.deadline = deadline
         self.callback = callback
-        self.tdeadline = (deadline, next(io_loop._timeout_counter))
+        self.tdeadline = (deadline, next(io_loop._timeout_counter))  # type: Tuple[float, int]
 
     # Comparison methods to sort by deadline, with object id as a tiebreaker
     # to guarantee a consistent ordering.  The heapq module uses __le__
     # in python2.5, and __lt__ in 2.6+ (sort() and most other comparisons
     # use __lt__).
-    def __lt__(self, other):
+    def __lt__(self, other: '_Timeout') -> bool:
         return self.tdeadline < other.tdeadline
 
-    def __le__(self, other):
+    def __le__(self, other: '_Timeout') -> bool:
         return self.tdeadline <= other.tdeadline
 
 
@@ -749,16 +800,17 @@ class PeriodicCallback(object):
     .. versionchanged:: 5.1
        The ``jitter`` argument is added.
     """
-    def __init__(self, callback, callback_time, jitter=0):
+    def __init__(self, callback: Callable[[], None],
+                 callback_time: float, jitter: float=0) -> None:
         self.callback = callback
         if callback_time <= 0:
             raise ValueError("Periodic callback must have a positive callback_time")
         self.callback_time = callback_time
         self.jitter = jitter
         self._running = False
-        self._timeout = None
+        self._timeout = None  # type: object
 
-    def start(self):
+    def start(self) -> None:
         """Starts the timer."""
         # Looking up the IOLoop here allows to first instantiate the
         # PeriodicCallback in another thread, then start it using
@@ -768,21 +820,21 @@ class PeriodicCallback(object):
         self._next_timeout = self.io_loop.time()
         self._schedule_next()
 
-    def stop(self):
+    def stop(self) -> None:
         """Stops the timer."""
         self._running = False
         if self._timeout is not None:
             self.io_loop.remove_timeout(self._timeout)
             self._timeout = None
 
-    def is_running(self):
+    def is_running(self) -> bool:
         """Return True if this `.PeriodicCallback` has been started.
 
         .. versionadded:: 4.1
         """
         return self._running
 
-    def _run(self):
+    def _run(self) -> None:
         if not self._running:
             return
         try:
@@ -792,12 +844,12 @@ class PeriodicCallback(object):
         finally:
             self._schedule_next()
 
-    def _schedule_next(self):
+    def _schedule_next(self) -> None:
         if self._running:
             self._update_next(self.io_loop.time())
             self._timeout = self.io_loop.add_timeout(self._next_timeout, self._run)
 
-    def _update_next(self, current_time):
+    def _update_next(self, current_time: float) -> None:
         callback_time_sec = self.callback_time / 1000.0
         if self.jitter:
             # apply jitter fraction
index e844facfeb58f02b46163a690f11833478743d20..57e2b2b81161c6d30a40f2fda299163592a449c5 100644 (file)
@@ -231,7 +231,7 @@ def add_accept_handler(sock: socket.socket,
     io_loop = IOLoop.current()
     removed = [False]
 
-    def accept_handler(fd: int, events: int) -> None:
+    def accept_handler(fd: socket.socket, events: int) -> None:
         # More connections may come in while we're handling callbacks;
         # to prevent starvation of other tasks we must limit the number
         # of connections we accept at a time.  Ideally we would accept
index 9211e214611bd4fe6328a0fc21faadf85bdc82f3..f1b072c7b4ae9e663c0be8019216f7a56740e4b8 100644 (file)
@@ -19,22 +19,31 @@ the same event loop.
    Windows. Use the `~asyncio.SelectorEventLoop` instead.
 """
 
+import concurrent.futures
 import functools
 
 from tornado.gen import convert_yielded
-from tornado.ioloop import IOLoop
+from tornado.ioloop import IOLoop, _Selectable
 
 import asyncio
 
+import typing
+from typing import Any, TypeVar, Awaitable, Callable, Union, Optional
+if typing.TYPE_CHECKING:
+    from typing import Set, Dict, Tuple  # noqa: F401
+
+_T = TypeVar('_T')
+
 
 class BaseAsyncIOLoop(IOLoop):
-    def initialize(self, asyncio_loop, **kwargs):
+    def initialize(self, asyncio_loop: asyncio.AbstractEventLoop,  # type: ignore
+                   **kwargs: Any) -> None:
         self.asyncio_loop = asyncio_loop
         # Maps fd to (fileobj, handler function) pair (as in IOLoop.add_handler)
-        self.handlers = {}
+        self.handlers = {}  # type: Dict[int, Tuple[Union[int, _Selectable], Callable]]
         # Set of fds listening for reads/writes
-        self.readers = set()
-        self.writers = set()
+        self.readers = set()  # type: Set[int]
+        self.writers = set()  # type: Set[int]
         self.closing = False
         # If an asyncio loop was closed through an asyncio interface
         # instead of IOLoop.close(), we'd never hear about it and may
@@ -53,7 +62,7 @@ class BaseAsyncIOLoop(IOLoop):
         IOLoop._ioloop_for_asyncio[asyncio_loop] = self
         super(BaseAsyncIOLoop, self).initialize(**kwargs)
 
-    def close(self, all_fds=False):
+    def close(self, all_fds: bool=False) -> None:
         self.closing = True
         for fd in list(self.handlers):
             fileobj, handler_func = self.handlers[fd]
@@ -68,7 +77,8 @@ class BaseAsyncIOLoop(IOLoop):
         del IOLoop._ioloop_for_asyncio[self.asyncio_loop]
         self.asyncio_loop.close()
 
-    def add_handler(self, fd, handler, events):
+    def add_handler(self, fd: Union[int, _Selectable],
+                    handler: Callable[..., None], events: int) -> None:
         fd, fileobj = self.split_fd(fd)
         if fd in self.handlers:
             raise ValueError("fd %s added twice" % fd)
@@ -82,7 +92,7 @@ class BaseAsyncIOLoop(IOLoop):
                 fd, self._handle_events, fd, IOLoop.WRITE)
             self.writers.add(fd)
 
-    def update_handler(self, fd, events):
+    def update_handler(self, fd: Union[int, _Selectable], events: int) -> None:
         fd, fileobj = self.split_fd(fd)
         if events & IOLoop.READ:
             if fd not in self.readers:
@@ -103,7 +113,7 @@ class BaseAsyncIOLoop(IOLoop):
                 self.asyncio_loop.remove_writer(fd)
                 self.writers.remove(fd)
 
-    def remove_handler(self, fd):
+    def remove_handler(self, fd: Union[int, _Selectable]) -> None:
         fd, fileobj = self.split_fd(fd)
         if fd not in self.handlers:
             return
@@ -115,15 +125,15 @@ class BaseAsyncIOLoop(IOLoop):
             self.writers.remove(fd)
         del self.handlers[fd]
 
-    def _handle_events(self, fd, events):
+    def _handle_events(self, fd: int, events: int) -> None:
         fileobj, handler_func = self.handlers[fd]
         handler_func(fileobj, events)
 
-    def start(self):
+    def start(self) -> None:
         try:
             old_loop = asyncio.get_event_loop()
         except (RuntimeError, AssertionError):
-            old_loop = None
+            old_loop = None  # type: ignore
         try:
             self._setup_logging()
             asyncio.set_event_loop(self.asyncio_loop)
@@ -131,10 +141,11 @@ class BaseAsyncIOLoop(IOLoop):
         finally:
             asyncio.set_event_loop(old_loop)
 
-    def stop(self):
+    def stop(self) -> None:
         self.asyncio_loop.stop()
 
-    def call_at(self, when, callback, *args, **kwargs):
+    def call_at(self, when: float, callback: Callable[..., None],
+                *args: Any, **kwargs: Any) -> object:
         # asyncio.call_at supports *args but not **kwargs, so bind them here.
         # We do not synchronize self.time and asyncio_loop.time, so
         # convert from absolute to relative.
@@ -142,10 +153,10 @@ class BaseAsyncIOLoop(IOLoop):
             max(0, when - self.time()), self._run_callback,
             functools.partial(callback, *args, **kwargs))
 
-    def remove_timeout(self, timeout):
-        timeout.cancel()
+    def remove_timeout(self, timeout: object) -> None:
+        timeout.cancel()  # type: ignore
 
-    def add_callback(self, callback, *args, **kwargs):
+    def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
         try:
             self.asyncio_loop.call_soon_threadsafe(
                 self._run_callback,
@@ -160,10 +171,11 @@ class BaseAsyncIOLoop(IOLoop):
 
     add_callback_from_signal = add_callback
 
-    def run_in_executor(self, executor, func, *args):
+    def run_in_executor(self, executor: Optional[concurrent.futures.Executor],
+                        func: Callable[..., _T], *args: Any) -> Awaitable[_T]:
         return self.asyncio_loop.run_in_executor(executor, func, *args)
 
-    def set_default_executor(self, executor):
+    def set_default_executor(self, executor: concurrent.futures.Executor) -> None:
         return self.asyncio_loop.set_default_executor(executor)
 
 
@@ -181,10 +193,10 @@ class AsyncIOMainLoop(BaseAsyncIOLoop):
 
        Closing an `AsyncIOMainLoop` now closes the underlying asyncio loop.
     """
-    def initialize(self, **kwargs):
+    def initialize(self, **kwargs: Any) -> None:  # type: ignore
         super(AsyncIOMainLoop, self).initialize(asyncio.get_event_loop(), **kwargs)
 
-    def make_current(self):
+    def make_current(self) -> None:
         # AsyncIOMainLoop already refers to the current asyncio loop so
         # nothing to do here.
         pass
@@ -209,7 +221,7 @@ class AsyncIOLoop(BaseAsyncIOLoop):
        Now used automatically when appropriate; it is no longer necessary
        to refer to this class directly.
     """
-    def initialize(self, **kwargs):
+    def initialize(self, **kwargs: Any) -> None:  # type: ignore
         self.is_current = False
         loop = asyncio.new_event_loop()
         try:
@@ -220,27 +232,27 @@ class AsyncIOLoop(BaseAsyncIOLoop):
             loop.close()
             raise
 
-    def close(self, all_fds=False):
+    def close(self, all_fds: bool=False) -> None:
         if self.is_current:
             self.clear_current()
         super(AsyncIOLoop, self).close(all_fds=all_fds)
 
-    def make_current(self):
+    def make_current(self) -> None:
         if not self.is_current:
             try:
                 self.old_asyncio = asyncio.get_event_loop()
             except (RuntimeError, AssertionError):
-                self.old_asyncio = None
+                self.old_asyncio = None  # type: ignore
             self.is_current = True
         asyncio.set_event_loop(self.asyncio_loop)
 
-    def _clear_current_hook(self):
+    def _clear_current_hook(self) -> None:
         if self.is_current:
             asyncio.set_event_loop(self.old_asyncio)
             self.is_current = False
 
 
-def to_tornado_future(asyncio_future):
+def to_tornado_future(asyncio_future: asyncio.Future) -> asyncio.Future:
     """Convert an `asyncio.Future` to a `tornado.concurrent.Future`.
 
     .. versionadded:: 4.1
@@ -252,7 +264,7 @@ def to_tornado_future(asyncio_future):
     return asyncio_future
 
 
-def to_asyncio_future(tornado_future):
+def to_asyncio_future(tornado_future: asyncio.Future) -> asyncio.Future:
     """Convert a Tornado yieldable object to an `asyncio.Future`.
 
     .. versionadded:: 4.1
@@ -285,7 +297,7 @@ class AnyThreadEventLoopPolicy(asyncio.DefaultEventLoopPolicy):  # type: ignore
     .. versionadded:: 5.0
 
     """
-    def get_event_loop(self):
+    def get_event_loop(self) -> asyncio.AbstractEventLoop:
         try:
             return super().get_event_loop()
         except (RuntimeError, AssertionError):
index f1e379b2298b580496ef25df338155de81e012c6..b23614b6711177ca70eb70ade68a8b055395317f 100644 (file)
@@ -6,6 +6,10 @@ from tornado import gen
 from tornado.ioloop import IOLoop
 from tornado.netutil import Resolver, is_valid_ip
 
+import typing
+if typing.TYPE_CHECKING:
+    from typing import Generator, Any, List, Tuple, Dict  # noqa: F401
+
 
 class CaresResolver(Resolver):
     """Name resolver based on the c-ares library.
@@ -22,12 +26,12 @@ class CaresResolver(Resolver):
     .. versionchanged:: 5.0
        The ``io_loop`` argument (deprecated since version 4.1) has been removed.
     """
-    def initialize(self):
+    def initialize(self) -> None:
         self.io_loop = IOLoop.current()
         self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb)
-        self.fds = {}
+        self.fds = {}  # type: Dict[int, int]
 
-    def _sock_state_cb(self, fd, readable, writable):
+    def _sock_state_cb(self, fd: int, readable: bool, writable: bool) -> None:
         state = ((IOLoop.READ if readable else 0) |
                  (IOLoop.WRITE if writable else 0))
         if not state:
@@ -40,7 +44,7 @@ class CaresResolver(Resolver):
             self.io_loop.add_handler(fd, self._handle_events, state)
             self.fds[fd] = state
 
-    def _handle_events(self, fd, events):
+    def _handle_events(self, fd: int, events: int) -> None:
         read_fd = pycares.ARES_SOCKET_BAD
         write_fd = pycares.ARES_SOCKET_BAD
         if events & IOLoop.READ:
@@ -50,12 +54,14 @@ class CaresResolver(Resolver):
         self.channel.process_fd(read_fd, write_fd)
 
     @gen.coroutine
-    def resolve(self, host, port, family=0):
+    def resolve(
+            self, host: str, port: int, family: int=0,
+    ) -> 'Generator[Any, Any, List[Tuple[int, Any]]]':
         if is_valid_ip(host):
             addresses = [host]
         else:
             # gethostbyname doesn't take callback as a kwarg
-            fut = Future()
+            fut = Future()  # type: Future[Tuple[Any, Any]]
             self.channel.gethostbyname(host, family,
                                        lambda result, error: fut.set_result((result, error)))
             result, error = yield fut
@@ -74,5 +80,5 @@ class CaresResolver(Resolver):
             if family != socket.AF_UNSPEC and family != address_family:
                 raise IOError('Requested socket family %d but got %d' %
                               (family, address_family))
-            addrinfo.append((address_family, (address, port)))
-        raise gen.Return(addrinfo)
+            addrinfo.append((typing.cast(int, address_family), (address, port)))
+        return addrinfo
index d7a6d76ee46146f52361b23437dd12f95a851ebe..11afdb1860e25db785f6dc1fe37e03efdaaf0d22 100644 (file)
@@ -21,6 +21,6 @@ implementation from `tornado.platform.auto`.
 """
 
 
-def set_close_exec(fd):
+def set_close_exec(fd: int) -> None:
     """Sets the close-on-exec bit (``FD_CLOEXEC``)for a file descriptor."""
     raise NotImplementedError()
index 54327746c6bf437e79e5f375b3d9371317da9be5..0ab27ca851d0c8bbec26fca0ba2fd631fa684a54 100644 (file)
@@ -19,11 +19,11 @@ import fcntl
 import os
 
 
-def set_close_exec(fd):
+def set_close_exec(fd: int) -> None:
     flags = fcntl.fcntl(fd, fcntl.F_GETFD)
     fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
 
 
-def _set_nonblocking(fd):
+def _set_nonblocking(fd: int) -> None:
     flags = fcntl.fcntl(fd, fcntl.F_GETFL)
     fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
index 68646b300f13c2ed6f5c4f9c1fa0355ddfa7bd8a..bf73e4aabe75d86ccd031507ea052a5648f2706e 100644 (file)
@@ -39,6 +39,10 @@ from tornado.escape import utf8
 from tornado import gen
 from tornado.netutil import Resolver
 
+import typing
+if typing.TYPE_CHECKING:
+    from typing import Generator, Any, List, Tuple  # noqa: F401
+
 
 class TwistedResolver(Resolver):
     """Twisted-based asynchronous resolver.
@@ -57,7 +61,7 @@ class TwistedResolver(Resolver):
     .. versionchanged:: 5.0
        The ``io_loop`` argument (deprecated since version 4.1) has been removed.
     """
-    def initialize(self):
+    def initialize(self) -> None:
         # partial copy of twisted.names.client.createResolver, which doesn't
         # allow for a reactor to be passed in.
         self.reactor = twisted.internet.asyncioreactor.AsyncioSelectorReactor()
@@ -70,7 +74,9 @@ class TwistedResolver(Resolver):
             [host_resolver, cache_resolver, real_resolver])
 
     @gen.coroutine
-    def resolve(self, host, port, family=0):
+    def resolve(
+            self, host: str, port: int, family: int=0,
+    ) -> 'Generator[Any, Any, List[Tuple[int, Any]]]':
         # getHostByName doesn't accept IP addresses, so if the input
         # looks like an IP address just return it immediately.
         if twisted.internet.abstract.isIPAddress(host):
@@ -81,7 +87,7 @@ class TwistedResolver(Resolver):
             resolved_family = socket.AF_INET6
         else:
             deferred = self.resolver.getHostByName(utf8(host))
-            fut = Future()
+            fut = Future()  # type: Future[Any]
             deferred.addBoth(fut.set_result)
             resolved = yield fut
             if isinstance(resolved, failure.Failure):
@@ -99,17 +105,17 @@ class TwistedResolver(Resolver):
             raise Exception('Requested socket family %d but got %d' %
                             (family, resolved_family))
         result = [
-            (resolved_family, (resolved, port)),
+            (typing.cast(int, resolved_family), (resolved, port)),
         ]
-        raise gen.Return(result)
+        return result
 
 
 if hasattr(gen.convert_yielded, 'register'):
     @gen.convert_yielded.register(Deferred)  # type: ignore
-    def _(d):
-        f = Future()
+    def _(d: Deferred) -> Future:
+        f = Future()  # type: Future[Any]
 
-        def errback(failure):
+        def errback(failure: failure.Failure) -> None:
             try:
                 failure.raiseException()
                 # Should never happen, but just in case
index 86293a908a9e4fdc6d45709dcd879152fb1190a9..6d6ebaf048eaf72b277b8f59f3aae27d319f5ce2 100644 (file)
@@ -12,7 +12,7 @@ SetHandleInformation.restype = ctypes.wintypes.BOOL
 HANDLE_FLAG_INHERIT = 0x00000001
 
 
-def set_close_exec(fd):
+def set_close_exec(fd: int) -> None:
     success = SetHandleInformation(fd, HANDLE_FLAG_INHERIT, 0)
     if not success:
-        raise ctypes.WinError()
+        raise ctypes.WinError()  # type: ignore
index fc0011cf4bfbeee8691404eb45b87c887ea5b5cc..b6badde786b7093511d18c0673ea92fda12bc5af 100644 (file)
@@ -19,6 +19,10 @@ from tornado.log import app_log
 from tornado.testing import AsyncTestCase, bind_unused_port, ExpectLog, gen_test
 from tornado.test.util import skipIfNonUnix, skipOnTravis
 
+import typing
+if typing.TYPE_CHECKING:
+    from typing import List  # noqa: F401
+
 
 class TestIOLoop(AsyncTestCase):
     def test_add_callback_return_sequence(self):
@@ -196,7 +200,7 @@ class TestIOLoop(AsyncTestCase):
 
     def test_timeout_with_arguments(self):
         # This tests that all the timeout methods pass through *args correctly.
-        results = []
+        results = []  # type: List[int]
         self.io_loop.add_timeout(self.io_loop.time(), results.append, 1)
         self.io_loop.add_timeout(datetime.timedelta(seconds=0),
                                  results.append, 2)
@@ -447,7 +451,9 @@ class TestIOLoopCurrentAsync(AsyncTestCase):
 class TestIOLoopFutures(AsyncTestCase):
     def test_add_future_threads(self):
         with futures.ThreadPoolExecutor(1) as pool:
-            self.io_loop.add_future(pool.submit(lambda: None),
+            def dummy():
+                pass
+            self.io_loop.add_future(pool.submit(dummy),
                                     lambda future: self.stop(future))
             future = self.wait()
             self.assertTrue(future.done())
@@ -590,8 +596,11 @@ class TestPeriodicCallbackMath(unittest.TestCase):
             now = pc._next_timeout + d
         return calls
 
+    def dummy(self):
+        pass
+
     def test_basic(self):
-        pc = PeriodicCallback(None, 10000)
+        pc = PeriodicCallback(self.dummy, 10000)
         self.assertEqual(self.simulate_calls(pc, [0] * 5),
                          [1010, 1020, 1030, 1040, 1050])
 
@@ -607,12 +616,12 @@ class TestPeriodicCallbackMath(unittest.TestCase):
             1220, 1230,  # then back on schedule.
         ]
 
-        pc = PeriodicCallback(None, 10000)
+        pc = PeriodicCallback(self.dummy, 10000)
         self.assertEqual(self.simulate_calls(pc, call_durations),
                          expected)
 
     def test_clock_backwards(self):
-        pc = PeriodicCallback(None, 10000)
+        pc = PeriodicCallback(self.dummy, 10000)
         # Backwards jumps are ignored, potentially resulting in a
         # slightly slow schedule (although we assume that when
         # time.time() and time.monotonic() are different, time.time()
@@ -632,7 +641,7 @@ class TestPeriodicCallbackMath(unittest.TestCase):
         random_times = [0.5, 1, 0, 0.75]
         expected = [1010, 1022.5, 1030, 1041.25]
         call_durations = [0] * len(random_times)
-        pc = PeriodicCallback(None, 10000, jitter=0.5)
+        pc = PeriodicCallback(self.dummy, 10000, jitter=0.5)
 
         def mock_random():
             return random_times.pop(0)
@@ -643,11 +652,11 @@ class TestPeriodicCallbackMath(unittest.TestCase):
 
 class TestIOLoopConfiguration(unittest.TestCase):
     def run_python(self, *statements):
-        statements = [
+        stmt_list = [
             'from tornado.ioloop import IOLoop',
             'classname = lambda x: x.__class__.__name__',
         ] + list(statements)
-        args = [sys.executable, '-c', '; '.join(statements)]
+        args = [sys.executable, '-c', '; '.join(stmt_list)]
         return native_str(subprocess.check_output(args)).strip()
 
     def test_default(self):
index 8c6e79ffeb0c7cb263b281cc763dca450e59ecbd..2c0f246478b63973ffce26df2b1f13ec6315055c 100644 (file)
@@ -156,7 +156,7 @@ class AsyncTestCase(unittest.TestCase):
         self.__running = False
         self.__failure = None  # type: Optional[_ExcInfoTuple]
         self.__stop_args = None  # type: Any
-        self.__timeout = None
+        self.__timeout = None  # type: Optional[object]
 
         # It's easy to forget the @gen_test decorator, but if you do
         # the test will silently be ignored because nothing will consume