Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tornado/gen.py: 24%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""``tornado.gen`` implements generator-based coroutines.
3.. note::
5 The "decorator and generator" approach in this module is a
6 precursor to native coroutines (using ``async def`` and ``await``)
7 which were introduced in Python 3.5. Applications that do not
8 require compatibility with older versions of Python should use
9 native coroutines instead. Some parts of this module are still
10 useful with native coroutines, notably `multi`, `sleep`,
11 `WaitIterator`, and `with_timeout`. Some of these functions have
12 counterparts in the `asyncio` module which may be used as well,
13 although the two may not necessarily be 100% compatible.
15Coroutines provide an easier way to work in an asynchronous
16environment than chaining callbacks. Code using coroutines is
17technically asynchronous, but it is written as a single generator
18instead of a collection of separate functions.
20For example, here's a coroutine-based handler:
22.. testcode::
24 class GenAsyncHandler(RequestHandler):
25 @gen.coroutine
26 def get(self):
27 http_client = AsyncHTTPClient()
28 response = yield http_client.fetch("http://example.com")
29 do_something_with_response(response)
30 self.render("template.html")
32Asynchronous functions in Tornado return an ``Awaitable`` or `.Future`;
33yielding this object returns its result.
35You can also yield a list or dict of other yieldable objects, which
36will be started at the same time and run in parallel; a list or dict
37of results will be returned when they are all finished:
39.. testcode::
41 @gen.coroutine
42 def get(self):
43 http_client = AsyncHTTPClient()
44 response1, response2 = yield [http_client.fetch(url1),
45 http_client.fetch(url2)]
46 response_dict = yield dict(response3=http_client.fetch(url3),
47 response4=http_client.fetch(url4))
48 response3 = response_dict['response3']
49 response4 = response_dict['response4']
51If ``tornado.platform.twisted`` is imported, it is also possible to
52yield Twisted's ``Deferred`` objects. See the `convert_yielded`
53function to extend this mechanism.
55.. versionchanged:: 3.2
56 Dict support added.
58.. versionchanged:: 4.1
59 Support added for yielding ``asyncio`` Futures and Twisted Deferreds
60 via ``singledispatch``.
62"""
64import asyncio
65import builtins
66import collections
67from collections.abc import Generator
68import concurrent.futures
69import datetime
70import functools
71from functools import singledispatch
72from inspect import isawaitable
73import sys
74import types
76from tornado.concurrent import (
77 Future,
78 is_future,
79 chain_future,
80 future_set_exc_info,
81 future_add_done_callback,
82 future_set_result_unless_cancelled,
83)
84from tornado.ioloop import IOLoop
85from tornado.log import app_log
86from tornado.util import TimeoutError
88try:
89 import contextvars
90except ImportError:
91 contextvars = None # type: ignore
93import typing
94from typing import (
95 Mapping,
96 Union,
97 Any,
98 Callable,
99 List,
100 Type,
101 Tuple,
102 Awaitable,
103 Dict,
104 Sequence,
105 overload,
106)
108if typing.TYPE_CHECKING:
109 from typing import Deque, Optional, Set, Iterable # noqa: F401
111_T = typing.TypeVar("_T")
113_Yieldable = Union[
114 None, Awaitable, List[Awaitable], Dict[Any, Awaitable], concurrent.futures.Future
115]
118class KeyReuseError(Exception):
119 pass
122class UnknownKeyError(Exception):
123 pass
126class LeakedCallbackError(Exception):
127 pass
130class BadYieldError(Exception):
131 pass
134class ReturnValueIgnoredError(Exception):
135 pass
138def _value_from_stopiteration(e: Union[StopIteration, "Return"]) -> Any:
139 try:
140 # StopIteration has a value attribute beginning in py33.
141 # So does our Return class.
142 return e.value
143 except AttributeError:
144 pass
145 try:
146 # Cython backports coroutine functionality by putting the value in
147 # e.args[0].
148 return e.args[0]
149 except (AttributeError, IndexError):
150 return None
153def _create_future() -> Future:
154 future = Future() # type: Future
155 # Fixup asyncio debug info by removing extraneous stack entries
156 source_traceback = getattr(future, "_source_traceback", ())
157 while source_traceback:
158 # Each traceback entry is equivalent to a
159 # (filename, self.lineno, self.name, self.line) tuple
160 filename = source_traceback[-1][0]
161 if filename == __file__:
162 del source_traceback[-1]
163 else:
164 break
165 return future
168def _fake_ctx_run(f: Callable[..., _T], *args: Any, **kw: Any) -> _T:
169 return f(*args, **kw)
172@overload
173def coroutine(
174 func: Callable[..., "Generator[Any, Any, _T]"]
175) -> Callable[..., "Future[_T]"]: ...
178@overload
179def coroutine(func: Callable[..., _T]) -> Callable[..., "Future[_T]"]: ...
182def coroutine(
183 func: Union[Callable[..., "Generator[Any, Any, _T]"], Callable[..., _T]]
184) -> Callable[..., "Future[_T]"]:
185 """Decorator for asynchronous generators.
187 For compatibility with older versions of Python, coroutines may
188 also "return" by raising the special exception `Return(value)
189 <Return>`.
191 Functions with this decorator return a `.Future`.
193 .. warning::
195 When exceptions occur inside a coroutine, the exception
196 information will be stored in the `.Future` object. You must
197 examine the result of the `.Future` object, or the exception
198 may go unnoticed by your code. This means yielding the function
199 if called from another coroutine, using something like
200 `.IOLoop.run_sync` for top-level calls, or passing the `.Future`
201 to `.IOLoop.add_future`.
203 .. versionchanged:: 6.0
205 The ``callback`` argument was removed. Use the returned
206 awaitable object instead.
208 """
210 @functools.wraps(func)
211 def wrapper(*args, **kwargs):
212 # type: (*Any, **Any) -> Future[_T]
213 # This function is type-annotated with a comment to work around
214 # https://bitbucket.org/pypy/pypy/issues/2868/segfault-with-args-type-annotation-in
215 future = _create_future()
216 if contextvars is not None:
217 ctx_run = contextvars.copy_context().run # type: Callable
218 else:
219 ctx_run = _fake_ctx_run
220 try:
221 result = ctx_run(func, *args, **kwargs)
222 except (Return, StopIteration) as e:
223 result = _value_from_stopiteration(e)
224 except Exception:
225 future_set_exc_info(future, sys.exc_info())
226 try:
227 return future
228 finally:
229 # Avoid circular references
230 future = None # type: ignore
231 else:
232 if isinstance(result, Generator):
233 # Inline the first iteration of Runner.run. This lets us
234 # avoid the cost of creating a Runner when the coroutine
235 # never actually yields, which in turn allows us to
236 # use "optional" coroutines in critical path code without
237 # performance penalty for the synchronous case.
238 try:
239 yielded = ctx_run(next, result)
240 except (StopIteration, Return) as e:
241 future_set_result_unless_cancelled(
242 future, _value_from_stopiteration(e)
243 )
244 except Exception:
245 future_set_exc_info(future, sys.exc_info())
246 else:
247 # Provide strong references to Runner objects as long
248 # as their result future objects also have strong
249 # references (typically from the parent coroutine's
250 # Runner). This keeps the coroutine's Runner alive.
251 # We do this by exploiting the public API
252 # add_done_callback() instead of putting a private
253 # attribute on the Future.
254 # (GitHub issues #1769, #2229).
255 runner = Runner(ctx_run, result, future, yielded)
256 future.add_done_callback(lambda _: runner)
257 yielded = None
258 try:
259 return future
260 finally:
261 # Subtle memory optimization: if next() raised an exception,
262 # the future's exc_info contains a traceback which
263 # includes this stack frame. This creates a cycle,
264 # which will be collected at the next full GC but has
265 # been shown to greatly increase memory usage of
266 # benchmarks (relative to the refcount-based scheme
267 # used in the absence of cycles). We can avoid the
268 # cycle by clearing the local variable after we return it.
269 future = None # type: ignore
270 future_set_result_unless_cancelled(future, result)
271 return future
273 wrapper.__wrapped__ = func # type: ignore
274 wrapper.__tornado_coroutine__ = True # type: ignore
275 return wrapper
278def is_coroutine_function(func: Any) -> bool:
279 """Return whether *func* is a coroutine function, i.e. a function
280 wrapped with `~.gen.coroutine`.
282 .. versionadded:: 4.5
283 """
284 return getattr(func, "__tornado_coroutine__", False)
287class Return(Exception):
288 """Special exception to return a value from a `coroutine`.
290 This exception exists for compatibility with older versions of
291 Python (before 3.3). In newer code use the ``return`` statement
292 instead.
294 If this exception is raised, its value argument is used as the
295 result of the coroutine::
297 @gen.coroutine
298 def fetch_json(url):
299 response = yield AsyncHTTPClient().fetch(url)
300 raise gen.Return(json_decode(response.body))
302 By analogy with the return statement, the value argument is optional.
303 """
305 def __init__(self, value: Any = None) -> None:
306 super().__init__()
307 self.value = value
308 # Cython recognizes subclasses of StopIteration with a .args tuple.
309 self.args = (value,)
312class WaitIterator:
313 """Provides an iterator to yield the results of awaitables as they finish.
315 Yielding a set of awaitables like this:
317 ``results = yield [awaitable1, awaitable2]``
319 pauses the coroutine until both ``awaitable1`` and ``awaitable2``
320 return, and then restarts the coroutine with the results of both
321 awaitables. If either awaitable raises an exception, the
322 expression will raise that exception and all the results will be
323 lost.
325 If you need to get the result of each awaitable as soon as possible,
326 or if you need the result of some awaitables even if others produce
327 errors, you can use ``WaitIterator``::
329 wait_iterator = gen.WaitIterator(awaitable1, awaitable2)
330 while not wait_iterator.done():
331 try:
332 result = yield wait_iterator.next()
333 except Exception as e:
334 print("Error {} from {}".format(e, wait_iterator.current_future))
335 else:
336 print("Result {} received from {} at {}".format(
337 result, wait_iterator.current_future,
338 wait_iterator.current_index))
340 Because results are returned as soon as they are available the
341 output from the iterator *will not be in the same order as the
342 input arguments*. If you need to know which future produced the
343 current result, you can use the attributes
344 ``WaitIterator.current_future``, or ``WaitIterator.current_index``
345 to get the index of the awaitable from the input list. (if keyword
346 arguments were used in the construction of the `WaitIterator`,
347 ``current_index`` will use the corresponding keyword).
349 `WaitIterator` implements the async iterator
350 protocol, so it can be used with the ``async for`` statement (note
351 that in this version the entire iteration is aborted if any value
352 raises an exception, while the previous example can continue past
353 individual errors)::
355 async for result in gen.WaitIterator(future1, future2):
356 print("Result {} received from {} at {}".format(
357 result, wait_iterator.current_future,
358 wait_iterator.current_index))
360 .. versionadded:: 4.1
362 .. versionchanged:: 4.3
363 Added ``async for`` support in Python 3.5.
365 """
367 _unfinished = {} # type: Dict[Future, Union[int, str]]
369 def __init__(self, *args: Future, **kwargs: Future) -> None:
370 if args and kwargs:
371 raise ValueError("You must provide args or kwargs, not both")
373 if kwargs:
374 self._unfinished = {f: k for (k, f) in kwargs.items()}
375 futures = list(kwargs.values()) # type: Sequence[Future]
376 else:
377 self._unfinished = {f: i for (i, f) in enumerate(args)}
378 futures = args
380 self._finished = collections.deque() # type: Deque[Future]
381 self.current_index = None # type: Optional[Union[str, int]]
382 self.current_future = None # type: Optional[Future]
383 self._running_future = None # type: Optional[Future]
385 for future in futures:
386 future_add_done_callback(future, self._done_callback)
388 def done(self) -> bool:
389 """Returns True if this iterator has no more results."""
390 if self._finished or self._unfinished:
391 return False
392 # Clear the 'current' values when iteration is done.
393 self.current_index = self.current_future = None
394 return True
396 def next(self) -> Future:
397 """Returns a `.Future` that will yield the next available result.
399 Note that this `.Future` will not be the same object as any of
400 the inputs.
401 """
402 self._running_future = Future()
404 if self._finished:
405 return self._return_result(self._finished.popleft())
407 return self._running_future
409 def _done_callback(self, done: Future) -> None:
410 if self._running_future and not self._running_future.done():
411 self._return_result(done)
412 else:
413 self._finished.append(done)
415 def _return_result(self, done: Future) -> Future:
416 """Called set the returned future's state that of the future
417 we yielded, and set the current future for the iterator.
418 """
419 if self._running_future is None:
420 raise Exception("no future is running")
421 chain_future(done, self._running_future)
423 res = self._running_future
424 self._running_future = None
425 self.current_future = done
426 self.current_index = self._unfinished.pop(done)
428 return res
430 def __aiter__(self) -> typing.AsyncIterator:
431 return self
433 def __anext__(self) -> Future:
434 if self.done():
435 # Lookup by name to silence pyflakes on older versions.
436 raise getattr(builtins, "StopAsyncIteration")()
437 return self.next()
440@overload
441def multi(
442 children: Sequence[_Yieldable],
443 quiet_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = (),
444) -> Future[List]: ...
447@overload
448def multi(
449 children: Mapping[Any, _Yieldable],
450 quiet_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = (),
451) -> Future[Dict]: ...
454def multi(
455 children: Union[Sequence[_Yieldable], Mapping[Any, _Yieldable]],
456 quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (),
457) -> "Union[Future[List], Future[Dict]]":
458 """Runs multiple asynchronous operations in parallel.
460 ``children`` may either be a list or a dict whose values are
461 yieldable objects. ``multi()`` returns a new yieldable
462 object that resolves to a parallel structure containing their
463 results. If ``children`` is a list, the result is a list of
464 results in the same order; if it is a dict, the result is a dict
465 with the same keys.
467 That is, ``results = yield multi(list_of_futures)`` is equivalent
468 to::
470 results = []
471 for future in list_of_futures:
472 results.append(yield future)
474 If any children raise exceptions, ``multi()`` will raise the first
475 one. All others will be logged, unless they are of types
476 contained in the ``quiet_exceptions`` argument.
478 In a ``yield``-based coroutine, it is not normally necessary to
479 call this function directly, since the coroutine runner will
480 do it automatically when a list or dict is yielded. However,
481 it is necessary in ``await``-based coroutines, or to pass
482 the ``quiet_exceptions`` argument.
484 This function is available under the names ``multi()`` and ``Multi()``
485 for historical reasons.
487 Cancelling a `.Future` returned by ``multi()`` does not cancel its
488 children. `asyncio.gather` is similar to ``multi()``, but it does
489 cancel its children.
491 .. versionchanged:: 4.2
492 If multiple yieldables fail, any exceptions after the first
493 (which is raised) will be logged. Added the ``quiet_exceptions``
494 argument to suppress this logging for selected exception types.
496 .. versionchanged:: 4.3
497 Replaced the class ``Multi`` and the function ``multi_future``
498 with a unified function ``multi``. Added support for yieldables
499 other than ``YieldPoint`` and `.Future`.
501 """
502 return multi_future(children, quiet_exceptions=quiet_exceptions)
505Multi = multi
508def multi_future(
509 children: Union[Sequence[_Yieldable], Mapping[Any, _Yieldable]],
510 quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (),
511) -> "Union[Future[List], Future[Dict]]":
512 """Wait for multiple asynchronous futures in parallel.
514 Since Tornado 6.0, this function is exactly the same as `multi`.
516 .. versionadded:: 4.0
518 .. versionchanged:: 4.2
519 If multiple ``Futures`` fail, any exceptions after the first (which is
520 raised) will be logged. Added the ``quiet_exceptions``
521 argument to suppress this logging for selected exception types.
523 .. deprecated:: 4.3
524 Use `multi` instead.
525 """
526 if isinstance(children, dict):
527 keys = list(children.keys()) # type: Optional[List]
528 children_seq = children.values() # type: Iterable
529 else:
530 keys = None
531 children_seq = children
532 children_futs = list(map(convert_yielded, children_seq))
533 assert all(is_future(i) or isinstance(i, _NullFuture) for i in children_futs)
534 unfinished_children = set(children_futs)
536 future = _create_future()
537 if not children_futs:
538 future_set_result_unless_cancelled(future, {} if keys is not None else [])
540 def callback(fut: Future) -> None:
541 unfinished_children.remove(fut)
542 if not unfinished_children:
543 result_list = []
544 for f in children_futs:
545 try:
546 result_list.append(f.result())
547 except Exception as e:
548 if future.done():
549 if not isinstance(e, quiet_exceptions):
550 app_log.error(
551 "Multiple exceptions in yield list", exc_info=True
552 )
553 else:
554 future_set_exc_info(future, sys.exc_info())
555 if not future.done():
556 if keys is not None:
557 future_set_result_unless_cancelled(
558 future, dict(zip(keys, result_list))
559 )
560 else:
561 future_set_result_unless_cancelled(future, result_list)
563 listening = set() # type: Set[Future]
564 for f in children_futs:
565 if f not in listening:
566 listening.add(f)
567 future_add_done_callback(f, callback)
568 return future
571def maybe_future(x: Any) -> Future:
572 """Converts ``x`` into a `.Future`.
574 If ``x`` is already a `.Future`, it is simply returned; otherwise
575 it is wrapped in a new `.Future`. This is suitable for use as
576 ``result = yield gen.maybe_future(f())`` when you don't know whether
577 ``f()`` returns a `.Future` or not.
579 .. deprecated:: 4.3
580 This function only handles ``Futures``, not other yieldable objects.
581 Instead of `maybe_future`, check for the non-future result types
582 you expect (often just ``None``), and ``yield`` anything unknown.
583 """
584 if is_future(x):
585 return x
586 else:
587 fut = _create_future()
588 fut.set_result(x)
589 return fut
592def with_timeout(
593 timeout: Union[float, datetime.timedelta],
594 future: _Yieldable,
595 quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (),
596) -> Future:
597 """Wraps a `.Future` (or other yieldable object) in a timeout.
599 Raises `tornado.util.TimeoutError` if the input future does not
600 complete before ``timeout``, which may be specified in any form
601 allowed by `.IOLoop.add_timeout` (i.e. a `datetime.timedelta` or
602 an absolute time relative to `.IOLoop.time`)
604 If the wrapped `.Future` fails after it has timed out, the exception
605 will be logged unless it is either of a type contained in
606 ``quiet_exceptions`` (which may be an exception type or a sequence of
607 types), or an ``asyncio.CancelledError``.
609 The wrapped `.Future` is not canceled when the timeout expires,
610 permitting it to be reused. `asyncio.wait_for` is similar to this
611 function but it does cancel the wrapped `.Future` on timeout.
613 .. versionadded:: 4.0
615 .. versionchanged:: 4.1
616 Added the ``quiet_exceptions`` argument and the logging of unhandled
617 exceptions.
619 .. versionchanged:: 4.4
620 Added support for yieldable objects other than `.Future`.
622 .. versionchanged:: 6.0.3
623 ``asyncio.CancelledError`` is now always considered "quiet".
625 .. versionchanged:: 6.2
626 ``tornado.util.TimeoutError`` is now an alias to ``asyncio.TimeoutError``.
628 """
629 # It's tempting to optimize this by cancelling the input future on timeout
630 # instead of creating a new one, but A) we can't know if we are the only
631 # one waiting on the input future, so cancelling it might disrupt other
632 # callers and B) concurrent futures can only be cancelled while they are
633 # in the queue, so cancellation cannot reliably bound our waiting time.
634 future_converted = convert_yielded(future)
635 result = _create_future()
636 chain_future(future_converted, result)
637 io_loop = IOLoop.current()
639 def error_callback(future: Future) -> None:
640 try:
641 future.result()
642 except asyncio.CancelledError:
643 pass
644 except Exception as e:
645 if not isinstance(e, quiet_exceptions):
646 app_log.error(
647 "Exception in Future %r after timeout", future, exc_info=True
648 )
650 def timeout_callback() -> None:
651 if not result.done():
652 result.set_exception(TimeoutError("Timeout"))
653 # In case the wrapped future goes on to fail, log it.
654 future_add_done_callback(future_converted, error_callback)
656 timeout_handle = io_loop.add_timeout(timeout, timeout_callback)
657 if isinstance(future_converted, Future):
658 # We know this future will resolve on the IOLoop, so we don't
659 # need the extra thread-safety of IOLoop.add_future (and we also
660 # don't care about StackContext here.
661 future_add_done_callback(
662 future_converted, lambda future: io_loop.remove_timeout(timeout_handle)
663 )
664 else:
665 # concurrent.futures.Futures may resolve on any thread, so we
666 # need to route them back to the IOLoop.
667 io_loop.add_future(
668 future_converted, lambda future: io_loop.remove_timeout(timeout_handle)
669 )
670 return result
673def sleep(duration: float) -> "Future[None]":
674 """Return a `.Future` that resolves after the given number of seconds.
676 When used with ``yield`` in a coroutine, this is a non-blocking
677 analogue to `time.sleep` (which should not be used in coroutines
678 because it is blocking)::
680 yield gen.sleep(0.5)
682 Note that calling this function on its own does nothing; you must
683 wait on the `.Future` it returns (usually by yielding it).
685 .. versionadded:: 4.1
686 """
687 f = _create_future()
688 IOLoop.current().call_later(
689 duration, lambda: future_set_result_unless_cancelled(f, None)
690 )
691 return f
694class _NullFuture:
695 """_NullFuture resembles a Future that finished with a result of None.
697 It's not actually a `Future` to avoid depending on a particular event loop.
698 Handled as a special case in the coroutine runner.
700 We lie and tell the type checker that a _NullFuture is a Future so
701 we don't have to leak _NullFuture into lots of public APIs. But
702 this means that the type checker can't warn us when we're passing
703 a _NullFuture into a code path that doesn't understand what to do
704 with it.
705 """
707 def result(self) -> None:
708 return None
710 def done(self) -> bool:
711 return True
714# _null_future is used as a dummy value in the coroutine runner. It differs
715# from moment in that moment always adds a delay of one IOLoop iteration
716# while _null_future is processed as soon as possible.
717_null_future = typing.cast(Future, _NullFuture())
719moment = typing.cast(Future, _NullFuture())
720moment.__doc__ = """A special object which may be yielded to allow the IOLoop to run for
721one iteration.
723This is not needed in normal use but it can be helpful in long-running
724coroutines that are likely to yield Futures that are ready instantly.
726Usage: ``yield gen.moment``
728In native coroutines, the equivalent of ``yield gen.moment`` is
729``await asyncio.sleep(0)``.
731.. versionadded:: 4.0
733.. deprecated:: 4.5
734 ``yield None`` (or ``yield`` with no argument) is now equivalent to
735 ``yield gen.moment``.
736"""
739class Runner:
740 """Internal implementation of `tornado.gen.coroutine`.
742 Maintains information about pending callbacks and their results.
744 The results of the generator are stored in ``result_future`` (a
745 `.Future`)
746 """
748 def __init__(
749 self,
750 ctx_run: Callable,
751 gen: "Generator[_Yieldable, Any, _T]",
752 result_future: "Future[_T]",
753 first_yielded: _Yieldable,
754 ) -> None:
755 self.ctx_run = ctx_run
756 self.gen = gen
757 self.result_future = result_future
758 self.future = _null_future # type: Union[None, Future]
759 self.running = False
760 self.finished = False
761 self.io_loop = IOLoop.current()
762 if self.ctx_run(self.handle_yield, first_yielded):
763 gen = result_future = first_yielded = None # type: ignore
764 self.ctx_run(self.run)
766 def run(self) -> None:
767 """Starts or resumes the generator, running until it reaches a
768 yield point that is not ready.
769 """
770 if self.running or self.finished:
771 return
772 try:
773 self.running = True
774 while True:
775 future = self.future
776 if future is None:
777 raise Exception("No pending future")
778 if not future.done():
779 return
780 self.future = None
781 try:
782 try:
783 value = future.result()
784 except Exception as e:
785 # Save the exception for later. It's important that
786 # gen.throw() not be called inside this try/except block
787 # because that makes sys.exc_info behave unexpectedly.
788 exc: Optional[Exception] = e
789 else:
790 exc = None
791 finally:
792 future = None
794 if exc is not None:
795 try:
796 yielded = self.gen.throw(exc)
797 finally:
798 # Break up a circular reference for faster GC on
799 # CPython.
800 del exc
801 else:
802 yielded = self.gen.send(value)
804 except (StopIteration, Return) as e:
805 self.finished = True
806 self.future = _null_future
807 future_set_result_unless_cancelled(
808 self.result_future, _value_from_stopiteration(e)
809 )
810 self.result_future = None # type: ignore
811 return
812 except Exception:
813 self.finished = True
814 self.future = _null_future
815 future_set_exc_info(self.result_future, sys.exc_info())
816 self.result_future = None # type: ignore
817 return
818 if not self.handle_yield(yielded):
819 return
820 yielded = None
821 finally:
822 self.running = False
824 def handle_yield(self, yielded: _Yieldable) -> bool:
825 try:
826 self.future = convert_yielded(yielded)
827 except BadYieldError:
828 self.future = Future()
829 future_set_exc_info(self.future, sys.exc_info())
831 if self.future is moment:
832 self.io_loop.add_callback(self.ctx_run, self.run)
833 return False
834 elif self.future is None:
835 raise Exception("no pending future")
836 elif not self.future.done():
838 def inner(f: Any) -> None:
839 # Break a reference cycle to speed GC.
840 f = None # noqa: F841
841 self.ctx_run(self.run)
843 self.io_loop.add_future(self.future, inner)
844 return False
845 return True
847 def handle_exception(
848 self, typ: Type[Exception], value: Exception, tb: types.TracebackType
849 ) -> bool:
850 if not self.running and not self.finished:
851 self.future = Future()
852 future_set_exc_info(self.future, (typ, value, tb))
853 self.ctx_run(self.run)
854 return True
855 else:
856 return False
859def _wrap_awaitable(awaitable: Awaitable) -> Future:
860 # Convert Awaitables into Futures.
861 # Note that we use ensure_future, which handles both awaitables
862 # and coroutines, rather than create_task, which only accepts
863 # coroutines. (ensure_future calls create_task if given a coroutine)
864 fut = asyncio.ensure_future(awaitable)
865 # See comments on IOLoop._pending_tasks.
866 loop = IOLoop.current()
867 loop._register_task(fut)
868 fut.add_done_callback(lambda f: loop._unregister_task(f))
869 return fut
872def convert_yielded(yielded: _Yieldable) -> Future:
873 """Convert a yielded object into a `.Future`.
875 The default implementation accepts lists, dictionaries, and
876 Futures. This has the side effect of starting any coroutines that
877 did not start themselves, similar to `asyncio.ensure_future`.
879 If the `~functools.singledispatch` library is available, this function
880 may be extended to support additional types. For example::
882 @convert_yielded.register(asyncio.Future)
883 def _(asyncio_future):
884 return tornado.platform.asyncio.to_tornado_future(asyncio_future)
886 .. versionadded:: 4.1
888 """
889 if yielded is None or yielded is moment:
890 return moment
891 elif yielded is _null_future:
892 return _null_future
893 elif isinstance(yielded, (list, dict)):
894 return multi(yielded) # type: ignore
895 elif is_future(yielded):
896 return typing.cast(Future, yielded)
897 elif isawaitable(yielded):
898 return _wrap_awaitable(yielded) # type: ignore
899 else:
900 raise BadYieldError(f"yielded unknown object {yielded!r}")
903convert_yielded = singledispatch(convert_yielded)