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

319 statements  

1"""``tornado.gen`` implements generator-based coroutines. 

2 

3.. note:: 

4 

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. 

14 

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. 

19 

20For example, here's a coroutine-based handler: 

21 

22.. testcode:: 

23 

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") 

31 

32Asynchronous functions in Tornado return an ``Awaitable`` or `.Future`; 

33yielding this object returns its result. 

34 

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: 

38 

39.. testcode:: 

40 

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'] 

50 

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. 

54 

55.. versionchanged:: 3.2 

56 Dict support added. 

57 

58.. versionchanged:: 4.1 

59 Support added for yielding ``asyncio`` Futures and Twisted Deferreds 

60 via ``singledispatch``. 

61 

62""" 

63 

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 

75 

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 

87 

88try: 

89 import contextvars 

90except ImportError: 

91 contextvars = None # type: ignore 

92 

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) 

107 

108if typing.TYPE_CHECKING: 

109 from typing import Deque, Optional, Set, Iterable # noqa: F401 

110 

111_T = typing.TypeVar("_T") 

112 

113_Yieldable = Union[ 

114 None, Awaitable, List[Awaitable], Dict[Any, Awaitable], concurrent.futures.Future 

115] 

116 

117 

118class KeyReuseError(Exception): 

119 pass 

120 

121 

122class UnknownKeyError(Exception): 

123 pass 

124 

125 

126class LeakedCallbackError(Exception): 

127 pass 

128 

129 

130class BadYieldError(Exception): 

131 pass 

132 

133 

134class ReturnValueIgnoredError(Exception): 

135 pass 

136 

137 

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 

151 

152 

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 

166 

167 

168def _fake_ctx_run(f: Callable[..., _T], *args: Any, **kw: Any) -> _T: 

169 return f(*args, **kw) 

170 

171 

172@overload 

173def coroutine( 

174 func: Callable[..., "Generator[Any, Any, _T]"] 

175) -> Callable[..., "Future[_T]"]: ... 

176 

177 

178@overload 

179def coroutine(func: Callable[..., _T]) -> Callable[..., "Future[_T]"]: ... 

180 

181 

182def coroutine( 

183 func: Union[Callable[..., "Generator[Any, Any, _T]"], Callable[..., _T]] 

184) -> Callable[..., "Future[_T]"]: 

185 """Decorator for asynchronous generators. 

186 

187 For compatibility with older versions of Python, coroutines may 

188 also "return" by raising the special exception `Return(value) 

189 <Return>`. 

190 

191 Functions with this decorator return a `.Future`. 

192 

193 .. warning:: 

194 

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`. 

202 

203 .. versionchanged:: 6.0 

204 

205 The ``callback`` argument was removed. Use the returned 

206 awaitable object instead. 

207 

208 """ 

209 

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 

272 

273 wrapper.__wrapped__ = func # type: ignore 

274 wrapper.__tornado_coroutine__ = True # type: ignore 

275 return wrapper 

276 

277 

278def is_coroutine_function(func: Any) -> bool: 

279 """Return whether *func* is a coroutine function, i.e. a function 

280 wrapped with `~.gen.coroutine`. 

281 

282 .. versionadded:: 4.5 

283 """ 

284 return getattr(func, "__tornado_coroutine__", False) 

285 

286 

287class Return(Exception): 

288 """Special exception to return a value from a `coroutine`. 

289 

290 This exception exists for compatibility with older versions of 

291 Python (before 3.3). In newer code use the ``return`` statement 

292 instead. 

293 

294 If this exception is raised, its value argument is used as the 

295 result of the coroutine:: 

296 

297 @gen.coroutine 

298 def fetch_json(url): 

299 response = yield AsyncHTTPClient().fetch(url) 

300 raise gen.Return(json_decode(response.body)) 

301 

302 By analogy with the return statement, the value argument is optional. 

303 """ 

304 

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,) 

310 

311 

312class WaitIterator: 

313 """Provides an iterator to yield the results of awaitables as they finish. 

314 

315 Yielding a set of awaitables like this: 

316 

317 ``results = yield [awaitable1, awaitable2]`` 

318 

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. 

324 

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``:: 

328 

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)) 

339 

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). 

348 

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):: 

354 

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)) 

359 

360 .. versionadded:: 4.1 

361 

362 .. versionchanged:: 4.3 

363 Added ``async for`` support in Python 3.5. 

364 

365 """ 

366 

367 _unfinished = {} # type: Dict[Future, Union[int, str]] 

368 

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") 

372 

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 

379 

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] 

384 

385 for future in futures: 

386 future_add_done_callback(future, self._done_callback) 

387 

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 

395 

396 def next(self) -> Future: 

397 """Returns a `.Future` that will yield the next available result. 

398 

399 Note that this `.Future` will not be the same object as any of 

400 the inputs. 

401 """ 

402 self._running_future = Future() 

403 

404 if self._finished: 

405 return self._return_result(self._finished.popleft()) 

406 

407 return self._running_future 

408 

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) 

414 

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) 

422 

423 res = self._running_future 

424 self._running_future = None 

425 self.current_future = done 

426 self.current_index = self._unfinished.pop(done) 

427 

428 return res 

429 

430 def __aiter__(self) -> typing.AsyncIterator: 

431 return self 

432 

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() 

438 

439 

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. 

445 

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. 

452 

453 That is, ``results = yield multi(list_of_futures)`` is equivalent 

454 to:: 

455 

456 results = [] 

457 for future in list_of_futures: 

458 results.append(yield future) 

459 

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. 

463 

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. 

469 

470 This function is available under the names ``multi()`` and ``Multi()`` 

471 for historical reasons. 

472 

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. 

476 

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. 

481 

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`. 

486 

487 """ 

488 return multi_future(children, quiet_exceptions=quiet_exceptions) 

489 

490 

491Multi = multi 

492 

493 

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. 

499 

500 Since Tornado 6.0, this function is exactly the same as `multi`. 

501 

502 .. versionadded:: 4.0 

503 

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. 

508 

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) 

521 

522 future = _create_future() 

523 if not children_futs: 

524 future_set_result_unless_cancelled(future, {} if keys is not None else []) 

525 

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) 

548 

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 

555 

556 

557def maybe_future(x: Any) -> Future: 

558 """Converts ``x`` into a `.Future`. 

559 

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. 

564 

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 

576 

577 

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. 

584 

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`) 

589 

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``. 

594 

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. 

598 

599 .. versionadded:: 4.0 

600 

601 .. versionchanged:: 4.1 

602 Added the ``quiet_exceptions`` argument and the logging of unhandled 

603 exceptions. 

604 

605 .. versionchanged:: 4.4 

606 Added support for yieldable objects other than `.Future`. 

607 

608 .. versionchanged:: 6.0.3 

609 ``asyncio.CancelledError`` is now always considered "quiet". 

610 

611 .. versionchanged:: 6.2 

612 ``tornado.util.TimeoutError`` is now an alias to ``asyncio.TimeoutError``. 

613 

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() 

624 

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 ) 

635 

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) 

641 

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 

657 

658 

659def sleep(duration: float) -> "Future[None]": 

660 """Return a `.Future` that resolves after the given number of seconds. 

661 

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):: 

665 

666 yield gen.sleep(0.5) 

667 

668 Note that calling this function on its own does nothing; you must 

669 wait on the `.Future` it returns (usually by yielding it). 

670 

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 

678 

679 

680class _NullFuture: 

681 """_NullFuture resembles a Future that finished with a result of None. 

682 

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. 

685 

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 """ 

692 

693 def result(self) -> None: 

694 return None 

695 

696 def done(self) -> bool: 

697 return True 

698 

699 

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()) 

704 

705moment = typing.cast(Future, _NullFuture()) 

706moment.__doc__ = """A special object which may be yielded to allow the IOLoop to run for 

707one iteration. 

708 

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. 

711 

712Usage: ``yield gen.moment`` 

713 

714In native coroutines, the equivalent of ``yield gen.moment`` is 

715``await asyncio.sleep(0)``. 

716 

717.. versionadded:: 4.0 

718 

719.. deprecated:: 4.5 

720 ``yield None`` (or ``yield`` with no argument) is now equivalent to 

721 ``yield gen.moment``. 

722""" 

723 

724 

725class Runner: 

726 """Internal implementation of `tornado.gen.coroutine`. 

727 

728 Maintains information about pending callbacks and their results. 

729 

730 The results of the generator are stored in ``result_future`` (a 

731 `.Future`) 

732 """ 

733 

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) 

751 

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 

779 

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) 

789 

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 

809 

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()) 

816 

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(): 

823 

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) 

828 

829 self.io_loop.add_future(self.future, inner) 

830 return False 

831 return True 

832 

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 

843 

844 

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 

856 

857 

858def convert_yielded(yielded: _Yieldable) -> Future: 

859 """Convert a yielded object into a `.Future`. 

860 

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`. 

864 

865 If the `~functools.singledispatch` library is available, this function 

866 may be extended to support additional types. For example:: 

867 

868 @convert_yielded.register(asyncio.Future) 

869 def _(asyncio_future): 

870 return tornado.platform.asyncio.to_tornado_future(asyncio_future) 

871 

872 .. versionadded:: 4.1 

873 

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}") 

887 

888 

889convert_yielded = singledispatch(convert_yielded)