from tornado.log import app_log
from tornado.util import TimeoutError
+try:
+ import contextvars
+except ImportError:
+ contextvars = None # type: ignore
+
import typing
from typing import Union, Any, Callable, List, Type, Tuple, Awaitable, Dict, overload
return future
+def _fake_ctx_run(f: Callable[..., _T], *args: Any, **kw: Any) -> _T:
+ return f(*args, **kw)
+
+
@overload
def coroutine(
func: Callable[..., "Generator[Any, Any, _T]"]
# This function is type-annotated with a comment to work around
# https://bitbucket.org/pypy/pypy/issues/2868/segfault-with-args-type-annotation-in
future = _create_future()
+ if contextvars is not None:
+ ctx_run = contextvars.copy_context().run # type: Callable
+ else:
+ ctx_run = _fake_ctx_run
try:
- result = func(*args, **kwargs)
+ result = ctx_run(func, *args, **kwargs)
except (Return, StopIteration) as e:
result = _value_from_stopiteration(e)
except Exception:
# use "optional" coroutines in critical path code without
# performance penalty for the synchronous case.
try:
- yielded = next(result)
+ yielded = ctx_run(next, result)
except (StopIteration, Return) as e:
future_set_result_unless_cancelled(
future, _value_from_stopiteration(e)
# add_done_callback() instead of putting a private
# attribute on the Future.
# (GitHub issues #1769, #2229).
- runner = Runner(result, future, yielded)
+ runner = Runner(ctx_run, result, future, yielded)
future.add_done_callback(lambda _: runner)
yielded = None
try:
def __init__(
self,
+ ctx_run: Callable,
gen: "Generator[_Yieldable, Any, _T]",
result_future: "Future[_T]",
first_yielded: _Yieldable,
) -> None:
+ self.ctx_run = ctx_run
self.gen = gen
self.result_future = result_future
self.future = _null_future # type: Union[None, Future]
self.io_loop = IOLoop.current()
if self.handle_yield(first_yielded):
gen = result_future = first_yielded = None # type: ignore
- self.run()
+ self.ctx_run(self.run)
def run(self) -> None:
"""Starts or resumes the generator, running until it reaches a
future_set_exc_info(self.future, sys.exc_info())
if self.future is moment:
- self.io_loop.add_callback(self.run)
+ self.io_loop.add_callback(self.ctx_run, self.run)
return False
elif self.future is None:
raise Exception("no pending future")
def inner(f: Any) -> None:
# Break a reference cycle to speed GC.
f = None # noqa: F841
- self.run()
+ self.ctx_run(self.run)
self.io_loop.add_future(self.future, inner)
return False
if not self.running and not self.finished:
self.future = Future()
future_set_exc_info(self.future, (typ, value, tb))
- self.run()
+ self.ctx_run(self.run)
return True
else:
return False