Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tornado/gen.py: 25%

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

323 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 

440@overload 

441def multi( 

442 children: Sequence[_Yieldable], 

443 quiet_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = (), 

444) -> Future[List]: ... 

445 

446 

447@overload 

448def multi( 

449 children: Mapping[Any, _Yieldable], 

450 quiet_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = (), 

451) -> Future[Dict]: ... 

452 

453 

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. 

459 

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. 

466 

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

468 to:: 

469 

470 results = [] 

471 for future in list_of_futures: 

472 results.append(yield future) 

473 

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. 

477 

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. 

483 

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

485 for historical reasons. 

486 

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. 

490 

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. 

495 

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

500 

501 """ 

502 return multi_future(children, quiet_exceptions=quiet_exceptions) 

503 

504 

505Multi = multi 

506 

507 

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. 

513 

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

515 

516 .. versionadded:: 4.0 

517 

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. 

522 

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) 

535 

536 future = _create_future() 

537 if not children_futs: 

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

539 

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) 

562 

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 

569 

570 

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

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

573 

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. 

578 

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 

590 

591 

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. 

598 

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

603 

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

608 

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. 

612 

613 .. versionadded:: 4.0 

614 

615 .. versionchanged:: 4.1 

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

617 exceptions. 

618 

619 .. versionchanged:: 4.4 

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

621 

622 .. versionchanged:: 6.0.3 

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

624 

625 .. versionchanged:: 6.2 

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

627 

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

638 

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 ) 

649 

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) 

655 

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 

671 

672 

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

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

675 

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

679 

680 yield gen.sleep(0.5) 

681 

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

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

684 

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 

692 

693 

694class _NullFuture: 

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

696 

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. 

699 

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

706 

707 def result(self) -> None: 

708 return None 

709 

710 def done(self) -> bool: 

711 return True 

712 

713 

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

718 

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

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

721one iteration. 

722 

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. 

725 

726Usage: ``yield gen.moment`` 

727 

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

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

730 

731.. versionadded:: 4.0 

732 

733.. deprecated:: 4.5 

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

735 ``yield gen.moment``. 

736""" 

737 

738 

739class Runner: 

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

741 

742 Maintains information about pending callbacks and their results. 

743 

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

745 `.Future`) 

746 """ 

747 

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) 

765 

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 

793 

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) 

803 

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 

823 

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

830 

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

837 

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) 

842 

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

844 return False 

845 return True 

846 

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 

857 

858 

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 

870 

871 

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

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

874 

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

878 

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

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

881 

882 @convert_yielded.register(asyncio.Future) 

883 def _(asyncio_future): 

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

885 

886 .. versionadded:: 4.1 

887 

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

901 

902 

903convert_yielded = singledispatch(convert_yielded)