Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tornado/gen.py: 23%
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()
440def multi(
441 children: Union[Sequence[_Yieldable], Mapping[Any, _Yieldable]],
442 quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (),
443) -> "Union[Future[List], Future[Dict]]":
444 """Runs multiple asynchronous operations in parallel.
446 ``children`` may either be a list or a dict whose values are
447 yieldable objects. ``multi()`` returns a new yieldable
448 object that resolves to a parallel structure containing their
449 results. If ``children`` is a list, the result is a list of
450 results in the same order; if it is a dict, the result is a dict
451 with the same keys.
453 That is, ``results = yield multi(list_of_futures)`` is equivalent
454 to::
456 results = []
457 for future in list_of_futures:
458 results.append(yield future)
460 If any children raise exceptions, ``multi()`` will raise the first
461 one. All others will be logged, unless they are of types
462 contained in the ``quiet_exceptions`` argument.
464 In a ``yield``-based coroutine, it is not normally necessary to
465 call this function directly, since the coroutine runner will
466 do it automatically when a list or dict is yielded. However,
467 it is necessary in ``await``-based coroutines, or to pass
468 the ``quiet_exceptions`` argument.
470 This function is available under the names ``multi()`` and ``Multi()``
471 for historical reasons.
473 Cancelling a `.Future` returned by ``multi()`` does not cancel its
474 children. `asyncio.gather` is similar to ``multi()``, but it does
475 cancel its children.
477 .. versionchanged:: 4.2
478 If multiple yieldables fail, any exceptions after the first
479 (which is raised) will be logged. Added the ``quiet_exceptions``
480 argument to suppress this logging for selected exception types.
482 .. versionchanged:: 4.3
483 Replaced the class ``Multi`` and the function ``multi_future``
484 with a unified function ``multi``. Added support for yieldables
485 other than ``YieldPoint`` and `.Future`.
487 """
488 return multi_future(children, quiet_exceptions=quiet_exceptions)
491Multi = multi
494def multi_future(
495 children: Union[Sequence[_Yieldable], Mapping[Any, _Yieldable]],
496 quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (),
497) -> "Union[Future[List], Future[Dict]]":
498 """Wait for multiple asynchronous futures in parallel.
500 Since Tornado 6.0, this function is exactly the same as `multi`.
502 .. versionadded:: 4.0
504 .. versionchanged:: 4.2
505 If multiple ``Futures`` fail, any exceptions after the first (which is
506 raised) will be logged. Added the ``quiet_exceptions``
507 argument to suppress this logging for selected exception types.
509 .. deprecated:: 4.3
510 Use `multi` instead.
511 """
512 if isinstance(children, dict):
513 keys = list(children.keys()) # type: Optional[List]
514 children_seq = children.values() # type: Iterable
515 else:
516 keys = None
517 children_seq = children
518 children_futs = list(map(convert_yielded, children_seq))
519 assert all(is_future(i) or isinstance(i, _NullFuture) for i in children_futs)
520 unfinished_children = set(children_futs)
522 future = _create_future()
523 if not children_futs:
524 future_set_result_unless_cancelled(future, {} if keys is not None else [])
526 def callback(fut: Future) -> None:
527 unfinished_children.remove(fut)
528 if not unfinished_children:
529 result_list = []
530 for f in children_futs:
531 try:
532 result_list.append(f.result())
533 except Exception as e:
534 if future.done():
535 if not isinstance(e, quiet_exceptions):
536 app_log.error(
537 "Multiple exceptions in yield list", exc_info=True
538 )
539 else:
540 future_set_exc_info(future, sys.exc_info())
541 if not future.done():
542 if keys is not None:
543 future_set_result_unless_cancelled(
544 future, dict(zip(keys, result_list))
545 )
546 else:
547 future_set_result_unless_cancelled(future, result_list)
549 listening = set() # type: Set[Future]
550 for f in children_futs:
551 if f not in listening:
552 listening.add(f)
553 future_add_done_callback(f, callback)
554 return future
557def maybe_future(x: Any) -> Future:
558 """Converts ``x`` into a `.Future`.
560 If ``x`` is already a `.Future`, it is simply returned; otherwise
561 it is wrapped in a new `.Future`. This is suitable for use as
562 ``result = yield gen.maybe_future(f())`` when you don't know whether
563 ``f()`` returns a `.Future` or not.
565 .. deprecated:: 4.3
566 This function only handles ``Futures``, not other yieldable objects.
567 Instead of `maybe_future`, check for the non-future result types
568 you expect (often just ``None``), and ``yield`` anything unknown.
569 """
570 if is_future(x):
571 return x
572 else:
573 fut = _create_future()
574 fut.set_result(x)
575 return fut
578def with_timeout(
579 timeout: Union[float, datetime.timedelta],
580 future: _Yieldable,
581 quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (),
582) -> Future:
583 """Wraps a `.Future` (or other yieldable object) in a timeout.
585 Raises `tornado.util.TimeoutError` if the input future does not
586 complete before ``timeout``, which may be specified in any form
587 allowed by `.IOLoop.add_timeout` (i.e. a `datetime.timedelta` or
588 an absolute time relative to `.IOLoop.time`)
590 If the wrapped `.Future` fails after it has timed out, the exception
591 will be logged unless it is either of a type contained in
592 ``quiet_exceptions`` (which may be an exception type or a sequence of
593 types), or an ``asyncio.CancelledError``.
595 The wrapped `.Future` is not canceled when the timeout expires,
596 permitting it to be reused. `asyncio.wait_for` is similar to this
597 function but it does cancel the wrapped `.Future` on timeout.
599 .. versionadded:: 4.0
601 .. versionchanged:: 4.1
602 Added the ``quiet_exceptions`` argument and the logging of unhandled
603 exceptions.
605 .. versionchanged:: 4.4
606 Added support for yieldable objects other than `.Future`.
608 .. versionchanged:: 6.0.3
609 ``asyncio.CancelledError`` is now always considered "quiet".
611 .. versionchanged:: 6.2
612 ``tornado.util.TimeoutError`` is now an alias to ``asyncio.TimeoutError``.
614 """
615 # It's tempting to optimize this by cancelling the input future on timeout
616 # instead of creating a new one, but A) we can't know if we are the only
617 # one waiting on the input future, so cancelling it might disrupt other
618 # callers and B) concurrent futures can only be cancelled while they are
619 # in the queue, so cancellation cannot reliably bound our waiting time.
620 future_converted = convert_yielded(future)
621 result = _create_future()
622 chain_future(future_converted, result)
623 io_loop = IOLoop.current()
625 def error_callback(future: Future) -> None:
626 try:
627 future.result()
628 except asyncio.CancelledError:
629 pass
630 except Exception as e:
631 if not isinstance(e, quiet_exceptions):
632 app_log.error(
633 "Exception in Future %r after timeout", future, exc_info=True
634 )
636 def timeout_callback() -> None:
637 if not result.done():
638 result.set_exception(TimeoutError("Timeout"))
639 # In case the wrapped future goes on to fail, log it.
640 future_add_done_callback(future_converted, error_callback)
642 timeout_handle = io_loop.add_timeout(timeout, timeout_callback)
643 if isinstance(future_converted, Future):
644 # We know this future will resolve on the IOLoop, so we don't
645 # need the extra thread-safety of IOLoop.add_future (and we also
646 # don't care about StackContext here.
647 future_add_done_callback(
648 future_converted, lambda future: io_loop.remove_timeout(timeout_handle)
649 )
650 else:
651 # concurrent.futures.Futures may resolve on any thread, so we
652 # need to route them back to the IOLoop.
653 io_loop.add_future(
654 future_converted, lambda future: io_loop.remove_timeout(timeout_handle)
655 )
656 return result
659def sleep(duration: float) -> "Future[None]":
660 """Return a `.Future` that resolves after the given number of seconds.
662 When used with ``yield`` in a coroutine, this is a non-blocking
663 analogue to `time.sleep` (which should not be used in coroutines
664 because it is blocking)::
666 yield gen.sleep(0.5)
668 Note that calling this function on its own does nothing; you must
669 wait on the `.Future` it returns (usually by yielding it).
671 .. versionadded:: 4.1
672 """
673 f = _create_future()
674 IOLoop.current().call_later(
675 duration, lambda: future_set_result_unless_cancelled(f, None)
676 )
677 return f
680class _NullFuture:
681 """_NullFuture resembles a Future that finished with a result of None.
683 It's not actually a `Future` to avoid depending on a particular event loop.
684 Handled as a special case in the coroutine runner.
686 We lie and tell the type checker that a _NullFuture is a Future so
687 we don't have to leak _NullFuture into lots of public APIs. But
688 this means that the type checker can't warn us when we're passing
689 a _NullFuture into a code path that doesn't understand what to do
690 with it.
691 """
693 def result(self) -> None:
694 return None
696 def done(self) -> bool:
697 return True
700# _null_future is used as a dummy value in the coroutine runner. It differs
701# from moment in that moment always adds a delay of one IOLoop iteration
702# while _null_future is processed as soon as possible.
703_null_future = typing.cast(Future, _NullFuture())
705moment = typing.cast(Future, _NullFuture())
706moment.__doc__ = """A special object which may be yielded to allow the IOLoop to run for
707one iteration.
709This is not needed in normal use but it can be helpful in long-running
710coroutines that are likely to yield Futures that are ready instantly.
712Usage: ``yield gen.moment``
714In native coroutines, the equivalent of ``yield gen.moment`` is
715``await asyncio.sleep(0)``.
717.. versionadded:: 4.0
719.. deprecated:: 4.5
720 ``yield None`` (or ``yield`` with no argument) is now equivalent to
721 ``yield gen.moment``.
722"""
725class Runner:
726 """Internal implementation of `tornado.gen.coroutine`.
728 Maintains information about pending callbacks and their results.
730 The results of the generator are stored in ``result_future`` (a
731 `.Future`)
732 """
734 def __init__(
735 self,
736 ctx_run: Callable,
737 gen: "Generator[_Yieldable, Any, _T]",
738 result_future: "Future[_T]",
739 first_yielded: _Yieldable,
740 ) -> None:
741 self.ctx_run = ctx_run
742 self.gen = gen
743 self.result_future = result_future
744 self.future = _null_future # type: Union[None, Future]
745 self.running = False
746 self.finished = False
747 self.io_loop = IOLoop.current()
748 if self.ctx_run(self.handle_yield, first_yielded):
749 gen = result_future = first_yielded = None # type: ignore
750 self.ctx_run(self.run)
752 def run(self) -> None:
753 """Starts or resumes the generator, running until it reaches a
754 yield point that is not ready.
755 """
756 if self.running or self.finished:
757 return
758 try:
759 self.running = True
760 while True:
761 future = self.future
762 if future is None:
763 raise Exception("No pending future")
764 if not future.done():
765 return
766 self.future = None
767 try:
768 try:
769 value = future.result()
770 except Exception as e:
771 # Save the exception for later. It's important that
772 # gen.throw() not be called inside this try/except block
773 # because that makes sys.exc_info behave unexpectedly.
774 exc: Optional[Exception] = e
775 else:
776 exc = None
777 finally:
778 future = None
780 if exc is not None:
781 try:
782 yielded = self.gen.throw(exc)
783 finally:
784 # Break up a circular reference for faster GC on
785 # CPython.
786 del exc
787 else:
788 yielded = self.gen.send(value)
790 except (StopIteration, Return) as e:
791 self.finished = True
792 self.future = _null_future
793 future_set_result_unless_cancelled(
794 self.result_future, _value_from_stopiteration(e)
795 )
796 self.result_future = None # type: ignore
797 return
798 except Exception:
799 self.finished = True
800 self.future = _null_future
801 future_set_exc_info(self.result_future, sys.exc_info())
802 self.result_future = None # type: ignore
803 return
804 if not self.handle_yield(yielded):
805 return
806 yielded = None
807 finally:
808 self.running = False
810 def handle_yield(self, yielded: _Yieldable) -> bool:
811 try:
812 self.future = convert_yielded(yielded)
813 except BadYieldError:
814 self.future = Future()
815 future_set_exc_info(self.future, sys.exc_info())
817 if self.future is moment:
818 self.io_loop.add_callback(self.ctx_run, self.run)
819 return False
820 elif self.future is None:
821 raise Exception("no pending future")
822 elif not self.future.done():
824 def inner(f: Any) -> None:
825 # Break a reference cycle to speed GC.
826 f = None # noqa: F841
827 self.ctx_run(self.run)
829 self.io_loop.add_future(self.future, inner)
830 return False
831 return True
833 def handle_exception(
834 self, typ: Type[Exception], value: Exception, tb: types.TracebackType
835 ) -> bool:
836 if not self.running and not self.finished:
837 self.future = Future()
838 future_set_exc_info(self.future, (typ, value, tb))
839 self.ctx_run(self.run)
840 return True
841 else:
842 return False
845def _wrap_awaitable(awaitable: Awaitable) -> Future:
846 # Convert Awaitables into Futures.
847 # Note that we use ensure_future, which handles both awaitables
848 # and coroutines, rather than create_task, which only accepts
849 # coroutines. (ensure_future calls create_task if given a coroutine)
850 fut = asyncio.ensure_future(awaitable)
851 # See comments on IOLoop._pending_tasks.
852 loop = IOLoop.current()
853 loop._register_task(fut)
854 fut.add_done_callback(lambda f: loop._unregister_task(f))
855 return fut
858def convert_yielded(yielded: _Yieldable) -> Future:
859 """Convert a yielded object into a `.Future`.
861 The default implementation accepts lists, dictionaries, and
862 Futures. This has the side effect of starting any coroutines that
863 did not start themselves, similar to `asyncio.ensure_future`.
865 If the `~functools.singledispatch` library is available, this function
866 may be extended to support additional types. For example::
868 @convert_yielded.register(asyncio.Future)
869 def _(asyncio_future):
870 return tornado.platform.asyncio.to_tornado_future(asyncio_future)
872 .. versionadded:: 4.1
874 """
875 if yielded is None or yielded is moment:
876 return moment
877 elif yielded is _null_future:
878 return _null_future
879 elif isinstance(yielded, (list, dict)):
880 return multi(yielded) # type: ignore
881 elif is_future(yielded):
882 return typing.cast(Future, yielded)
883 elif isawaitable(yielded):
884 return _wrap_awaitable(yielded) # type: ignore
885 else:
886 raise BadYieldError(f"yielded unknown object {yielded!r}")
889convert_yielded = singledispatch(convert_yielded)