from typing import Union, Any, Type, Optional, Callable, TypeVar, Tuple, Awaitable
if typing.TYPE_CHECKING:
- from typing import Dict, List, Set # noqa: F401
+ from typing import Dict, List, Set, TypedDict # noqa: F401
from typing_extensions import Protocol
else:
.. versionchanged:: 6.2
``tornado.util.TimeoutError`` is now an alias to ``asyncio.TimeoutError``.
"""
- future_cell = [None] # type: List[Optional[Future]]
+ if typing.TYPE_CHECKING:
+ FutureCell = TypedDict( # noqa: F841
+ "FutureCell", {"future": Optional[Future], "timeout_called": bool}
+ )
+ future_cell = {"future": None, "timeout_called": False} # type: FutureCell
def run() -> None:
try:
result = convert_yielded(result)
except Exception:
fut = Future() # type: Future[Any]
- future_cell[0] = fut
+ future_cell["future"] = fut
future_set_exc_info(fut, sys.exc_info())
else:
if is_future(result):
- future_cell[0] = result
+ future_cell["future"] = result
else:
fut = Future()
- future_cell[0] = fut
+ future_cell["future"] = fut
fut.set_result(result)
- assert future_cell[0] is not None
- self.add_future(future_cell[0], lambda future: self.stop())
+ assert future_cell["future"] is not None
+ self.add_future(future_cell["future"], lambda future: self.stop())
self.add_callback(run)
if timeout is not None:
def timeout_callback() -> None:
+ # signal that timeout is triggered
+ future_cell["timeout_called"] = True
# 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():
+ assert future_cell["future"] is not None
+ if not future_cell["future"].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()
+ assert future_cell["future"] is not None
+ if future_cell["future"].cancelled() or not future_cell["future"].done():
+ if future_cell["timeout_called"]:
+ raise TimeoutError("Operation timed out after %s seconds" % timeout)
+ else:
+ # timeout not called; maybe stop() was called explicitly
+ # or some other cancellation
+ raise RuntimeError("Event loop stopped before Future completed.")
+ return future_cell["future"].result()
def time(self) -> float:
"""Returns the current time according to the `IOLoop`'s clock.