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

338 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 

67import concurrent.futures 

68import contextvars 

69import datetime 

70import functools 

71import sys 

72import types 

73import typing 

74from collections.abc import Awaitable, Callable, Generator, Iterable, Mapping, Sequence 

75from functools import singledispatch 

76from inspect import isawaitable 

77from typing import ( 

78 Any, 

79 Dict, 

80 List, 

81 Tuple, 

82 Type, 

83 Union, 

84 overload, 

85) 

86 

87from tornado.concurrent import ( 

88 Future, 

89 chain_future, 

90 future_add_done_callback, 

91 future_set_exc_info, 

92 future_set_result_unless_cancelled, 

93 is_future, 

94) 

95from tornado.ioloop import IOLoop 

96from tornado.log import app_log 

97from tornado.util import TimeoutError # noqa: F401 (re-exported for compatibility) 

98 

99_T = typing.TypeVar("_T") 

100 

101_Yieldable = Union[ 

102 None, Awaitable, list[Awaitable], dict[Any, Awaitable], concurrent.futures.Future 

103] 

104 

105 

106class KeyReuseError(Exception): 

107 pass 

108 

109 

110class UnknownKeyError(Exception): 

111 pass 

112 

113 

114class LeakedCallbackError(Exception): 

115 pass 

116 

117 

118class BadYieldError(Exception): 

119 pass 

120 

121 

122class ReturnValueIgnoredError(Exception): 

123 pass 

124 

125 

126def _value_from_stopiteration(e: Union[StopIteration, "Return"]) -> Any: 

127 try: 

128 # StopIteration has a value attribute beginning in py33. 

129 # So does our Return class. 

130 return e.value 

131 except AttributeError: 

132 pass 

133 try: 

134 # Cython backports coroutine functionality by putting the value in 

135 # e.args[0]. 

136 return e.args[0] 

137 except (AttributeError, IndexError): 

138 return None 

139 

140 

141def _create_future() -> Future: 

142 future: Future = Future() 

143 # Fixup asyncio debug info by removing extraneous stack entries 

144 source_traceback = getattr(future, "_source_traceback", ()) 

145 while source_traceback: 

146 # Each traceback entry is equivalent to a 

147 # (filename, self.lineno, self.name, self.line) tuple 

148 filename = source_traceback[-1][0] 

149 if filename == __file__: 

150 del source_traceback[-1] 

151 else: 

152 break 

153 return future 

154 

155 

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

157 return f(*args, **kw) 

158 

159 

160@overload 

161def coroutine( 

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

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

164 

165 

166@overload 

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

168 

169 

170def coroutine( 

171 func: Callable[..., "Generator[Any, Any, _T]"] | Callable[..., _T], 

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

173 """Decorator for asynchronous generators. 

174 

175 For compatibility with older versions of Python, coroutines may 

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

177 <Return>`. 

178 

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

180 

181 .. warning:: 

182 

183 When exceptions occur inside a coroutine, the exception 

184 information will be stored in the `.Future` object. You must 

185 examine the result of the `.Future` object, or the exception 

186 may go unnoticed by your code. This means yielding the function 

187 if called from another coroutine, using something like 

188 `.IOLoop.run_sync` for top-level calls, or passing the `.Future` 

189 to `.IOLoop.add_future`. 

190 

191 .. versionchanged:: 6.0 

192 

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

194 awaitable object instead. 

195 

196 .. versionchanged:: 6.6 

197 

198 If the coroutine raises `asyncio.CancelledError` (including by 

199 yielding a cancelled `.Future` without catching the exception), the 

200 returned `.Future` is now cancelled. Previously the coroutine would 

201 never complete. 

202 

203 """ 

204 

205 @functools.wraps(func) 

206 def wrapper(*args: Any, **kwargs: Any) -> Future[_T]: 

207 # This function is type-annotated with a comment to work around 

208 # https://bitbucket.org/pypy/pypy/issues/2868/segfault-with-args-type-annotation-in 

209 future = _create_future() 

210 ctx_run: Callable = contextvars.copy_context().run 

211 try: 

212 result = ctx_run(func, *args, **kwargs) 

213 except (Return, StopIteration) as e: 

214 result = _value_from_stopiteration(e) 

215 except asyncio.CancelledError: 

216 # Like asyncio.Task, a coroutine that raises CancelledError 

217 # is treated as cancelled. 

218 future.cancel() 

219 return future 

220 except Exception: 

221 future_set_exc_info(future, sys.exc_info()) 

222 try: 

223 return future 

224 finally: 

225 # Avoid circular references 

226 future = None # type: ignore 

227 else: 

228 if isinstance(result, Generator): 

229 # Inline the first iteration of Runner.run. This lets us 

230 # avoid the cost of creating a Runner when the coroutine 

231 # never actually yields, which in turn allows us to 

232 # use "optional" coroutines in critical path code without 

233 # performance penalty for the synchronous case. 

234 try: 

235 yielded = ctx_run(next, result) 

236 except (StopIteration, Return) as e: 

237 future_set_result_unless_cancelled( 

238 future, _value_from_stopiteration(e) 

239 ) 

240 except asyncio.CancelledError: 

241 future.cancel() 

242 except Exception: 

243 future_set_exc_info(future, sys.exc_info()) 

244 else: 

245 # Provide strong references to Runner objects as long 

246 # as their result future objects also have strong 

247 # references (typically from the parent coroutine's 

248 # Runner). This keeps the coroutine's Runner alive. 

249 # We do this by exploiting the public API 

250 # add_done_callback() instead of putting a private 

251 # attribute on the Future. 

252 # (GitHub issues #1769, #2229). 

253 runner = Runner(ctx_run, result, future, yielded) 

254 future.add_done_callback(lambda _: runner) 

255 yielded = None 

256 try: 

257 return future 

258 finally: 

259 # Subtle memory optimization: if next() raised an exception, 

260 # the future's exc_info contains a traceback which 

261 # includes this stack frame. This creates a cycle, 

262 # which will be collected at the next full GC but has 

263 # been shown to greatly increase memory usage of 

264 # benchmarks (relative to the refcount-based scheme 

265 # used in the absence of cycles). We can avoid the 

266 # cycle by clearing the local variable after we return it. 

267 future = None # type: ignore 

268 future_set_result_unless_cancelled(future, result) 

269 return future 

270 

271 wrapper.__wrapped__ = func # type: ignore 

272 wrapper.__tornado_coroutine__ = True # type: ignore 

273 return wrapper 

274 

275 

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

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

278 wrapped with `~.gen.coroutine`. 

279 

280 .. versionadded:: 4.5 

281 """ 

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

283 

284 

285class Return(Exception): 

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

287 

288 This exception exists for compatibility with older versions of 

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

290 instead. 

291 

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

293 result of the coroutine:: 

294 

295 @gen.coroutine 

296 def fetch_json(url): 

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

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

299 

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

301 """ 

302 

303 def __init__(self, value: Any = None) -> None: 

304 super().__init__() 

305 self.value = value 

306 # Cython recognizes subclasses of StopIteration with a .args tuple. 

307 self.args = (value,) 

308 

309 

310class WaitIterator: 

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

312 

313 Yielding a set of awaitables like this: 

314 

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

316 

317 pauses the coroutine until both ``awaitable1`` and ``awaitable2`` 

318 return, and then restarts the coroutine with the results of both 

319 awaitables. If either awaitable raises an exception, the 

320 expression will raise that exception and all the results will be 

321 lost. 

322 

323 If you need to get the result of each awaitable as soon as possible, 

324 or if you need the result of some awaitables even if others produce 

325 errors, you can use ``WaitIterator``:: 

326 

327 wait_iterator = gen.WaitIterator(awaitable1, awaitable2) 

328 while not wait_iterator.done(): 

329 try: 

330 result = yield wait_iterator.next() 

331 except Exception as e: 

332 print("Error {} from {}".format(e, wait_iterator.current_future)) 

333 else: 

334 print("Result {} received from {} at {}".format( 

335 result, wait_iterator.current_future, 

336 wait_iterator.current_index)) 

337 

338 Because results are returned as soon as they are available the 

339 output from the iterator *will not be in the same order as the 

340 input arguments*. If you need to know which future produced the 

341 current result, you can use the attributes 

342 ``WaitIterator.current_future``, or ``WaitIterator.current_index`` 

343 to get the index of the awaitable from the input list. (if keyword 

344 arguments were used in the construction of the `WaitIterator`, 

345 ``current_index`` will use the corresponding keyword). 

346 

347 `WaitIterator` implements the async iterator 

348 protocol, so it can be used with the ``async for`` statement (note 

349 that in this version the entire iteration is aborted if any value 

350 raises an exception, while the previous example can continue past 

351 individual errors):: 

352 

353 async for result in gen.WaitIterator(future1, future2): 

354 print("Result {} received from {} at {}".format( 

355 result, wait_iterator.current_future, 

356 wait_iterator.current_index)) 

357 

358 .. versionadded:: 4.1 

359 

360 .. versionchanged:: 4.3 

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

362 

363 """ 

364 

365 _unfinished: dict[Future, collections.deque[int | str]] = {} 

366 

367 def __init__(self, *args: Future, **kwargs: Future) -> None: 

368 if args and kwargs: 

369 raise ValueError("You must provide args or kwargs, not both") 

370 

371 self._unfinished = {} 

372 if kwargs: 

373 futures: Sequence[Future] = list(kwargs.values()) 

374 for key, future in kwargs.items(): 

375 self._unfinished.setdefault(future, collections.deque()).append(key) 

376 else: 

377 futures = args 

378 for index, future in enumerate(args): 

379 self._unfinished.setdefault(future, collections.deque()).append(index) 

380 

381 self._finished: collections.deque[Future] = collections.deque() 

382 self.current_index: str | int | None = None 

383 self.current_future: Future | None = None 

384 self._running_future: Future | None = None 

385 

386 for future in futures: 

387 future_add_done_callback(future, self._done_callback) 

388 

389 def done(self) -> bool: 

390 """Returns True if this iterator has no more results.""" 

391 if self._finished or self._unfinished: 

392 return False 

393 # Clear the 'current' values when iteration is done. 

394 self.current_index = self.current_future = None 

395 return True 

396 

397 def next(self) -> Future: 

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

399 

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

401 the inputs. 

402 """ 

403 self._running_future = Future() 

404 

405 if self._finished: 

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

407 

408 return self._running_future 

409 

410 def _done_callback(self, done: Future) -> None: 

411 if self._running_future and not self._running_future.done(): 

412 self._return_result(done) 

413 else: 

414 self._finished.append(done) 

415 

416 def _return_result(self, done: Future) -> Future: 

417 """Called set the returned future's state that of the future 

418 we yielded, and set the current future for the iterator. 

419 """ 

420 if self._running_future is None: 

421 raise Exception("no future is running") 

422 chain_future(done, self._running_future) 

423 

424 res = self._running_future 

425 self._running_future = None 

426 self.current_future = done 

427 indices = self._unfinished[done] 

428 self.current_index = indices.popleft() 

429 if not indices: 

430 del self._unfinished[done] 

431 

432 return res 

433 

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

435 return self 

436 

437 def __anext__(self) -> Future: 

438 if self.done(): 

439 # Lookup by name to silence pyflakes on older versions. 

440 raise getattr(builtins, "StopAsyncIteration")() 

441 return self.next() 

442 

443 

444@overload 

445def multi( 

446 children: Sequence[_Yieldable], 

447 quiet_exceptions: type[Exception] | tuple[type[Exception], ...] = (), 

448) -> Future[list]: ... 

449 

450 

451@overload 

452def multi( 

453 children: Mapping[Any, _Yieldable], 

454 quiet_exceptions: type[Exception] | tuple[type[Exception], ...] = (), 

455) -> Future[dict]: ... 

456 

457 

458def multi( 

459 children: Sequence[_Yieldable] | Mapping[Any, _Yieldable], 

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

461) -> "Union[Future[List], Future[Dict]]": 

462 """Runs multiple asynchronous operations in parallel. 

463 

464 ``children`` may either be a list or a dict whose values are 

465 yieldable objects. ``multi()`` returns a new yieldable 

466 object that resolves to a parallel structure containing their 

467 results. If ``children`` is a list, the result is a list of 

468 results in the same order; if it is a dict, the result is a dict 

469 with the same keys. 

470 

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

472 to:: 

473 

474 results = [] 

475 for future in list_of_futures: 

476 results.append(yield future) 

477 

478 If any children raise exceptions, ``multi()`` will raise the first 

479 one. All others will be logged, unless they are of types 

480 contained in the ``quiet_exceptions`` argument. 

481 

482 In a ``yield``-based coroutine, it is not normally necessary to 

483 call this function directly, since the coroutine runner will 

484 do it automatically when a list or dict is yielded. However, 

485 it is necessary in ``await``-based coroutines, or to pass 

486 the ``quiet_exceptions`` argument. 

487 

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

489 for historical reasons. 

490 

491 Cancelling a `.Future` returned by ``multi()`` does not cancel its 

492 children. `asyncio.gather` is similar to ``multi()``, but it does 

493 cancel its children. 

494 

495 .. versionchanged:: 4.2 

496 If multiple yieldables fail, any exceptions after the first 

497 (which is raised) will be logged. Added the ``quiet_exceptions`` 

498 argument to suppress this logging for selected exception types. 

499 

500 .. versionchanged:: 4.3 

501 Replaced the class ``Multi`` and the function ``multi_future`` 

502 with a unified function ``multi``. Added support for yieldables 

503 other than ``YieldPoint`` and `.Future`. 

504 

505 .. versionchanged:: 6.6 

506 If a child is cancelled, ``multi()`` now raises `asyncio.CancelledError` 

507 (without logging it if it is not the first failure), matching 

508 `asyncio.gather`. Previously ``multi()`` would never complete. 

509 

510 """ 

511 return multi_future(children, quiet_exceptions=quiet_exceptions) 

512 

513 

514Multi = multi 

515 

516 

517def multi_future( 

518 children: Sequence[_Yieldable] | Mapping[Any, _Yieldable], 

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

520) -> "Union[Future[List], Future[Dict]]": 

521 """Wait for multiple asynchronous futures in parallel. 

522 

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

524 

525 .. versionadded:: 4.0 

526 

527 .. versionchanged:: 4.2 

528 If multiple ``Futures`` fail, any exceptions after the first (which is 

529 raised) will be logged. Added the ``quiet_exceptions`` 

530 argument to suppress this logging for selected exception types. 

531 

532 .. deprecated:: 4.3 

533 Use `multi` instead. 

534 """ 

535 if isinstance(children, dict): 

536 keys: list | None = list(children.keys()) 

537 children_seq: Iterable = children.values() 

538 else: 

539 keys = None 

540 children_seq = children 

541 children_futs = list(map(convert_yielded, children_seq)) 

542 assert all(is_future(i) or isinstance(i, _NullFuture) for i in children_futs) 

543 unfinished_children = set(children_futs) 

544 

545 future = _create_future() 

546 if not children_futs: 

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

548 

549 def callback(fut: Future) -> None: 

550 unfinished_children.remove(fut) 

551 if not unfinished_children: 

552 result_list = [] 

553 for f in children_futs: 

554 try: 

555 result_list.append(f.result()) 

556 except (Exception, asyncio.CancelledError) as e: 

557 # As in asyncio.gather, a cancelled child is treated as 

558 # an error, and does not cancel the combined future. 

559 if future.done(): 

560 if not isinstance( 

561 e, (asyncio.CancelledError, quiet_exceptions) 

562 ): 

563 app_log.error( 

564 "Multiple exceptions in yield list", exc_info=True 

565 ) 

566 else: 

567 future_set_exc_info(future, sys.exc_info()) 

568 if not future.done(): 

569 if keys is not None: 

570 future_set_result_unless_cancelled( 

571 future, dict(zip(keys, result_list)) 

572 ) 

573 else: 

574 future_set_result_unless_cancelled(future, result_list) 

575 

576 listening: set[Future] = set() 

577 for f in children_futs: 

578 if f not in listening: 

579 listening.add(f) 

580 future_add_done_callback(f, callback) 

581 return future 

582 

583 

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

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

586 

587 If ``x`` is already a `.Future`, it is simply returned; otherwise 

588 it is wrapped in a new `.Future`. This is suitable for use as 

589 ``result = yield gen.maybe_future(f())`` when you don't know whether 

590 ``f()`` returns a `.Future` or not. 

591 

592 .. deprecated:: 4.3 

593 This function only handles ``Futures``, not other yieldable objects. 

594 Instead of `maybe_future`, check for the non-future result types 

595 you expect (often just ``None``), and ``yield`` anything unknown. 

596 """ 

597 if is_future(x): 

598 return x 

599 else: 

600 fut = _create_future() 

601 fut.set_result(x) 

602 return fut 

603 

604 

605def with_timeout( 

606 timeout: float | datetime.timedelta, 

607 future: _Yieldable, 

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

609 *, 

610 message: str | Callable[[], str] | None = None, 

611) -> Future: 

612 """Wraps a `.Future` (or other yieldable object) in a timeout. 

613 

614 Raises `TimeoutError` if the input future does not 

615 complete before ``timeout``, which may be specified in any form 

616 allowed by `.IOLoop.add_timeout` (i.e. a `datetime.timedelta` or 

617 an absolute time relative to `.IOLoop.time`) 

618 

619 ``message`` is used as the message of the `TimeoutError`. It may be 

620 a string or a callable returning a string; a callable is only called 

621 if the timeout expires, so it may be used to defer the cost of 

622 formatting the message. 

623 

624 If the wrapped `.Future` fails after it has timed out, the exception 

625 will be logged unless it is either of a type contained in 

626 ``quiet_exceptions`` (which may be an exception type or a sequence of 

627 types), or an ``asyncio.CancelledError``. 

628 

629 The wrapped `.Future` is not canceled when the timeout expires, 

630 permitting it to be reused. `asyncio.wait_for` is similar to this 

631 function but it does cancel the wrapped `.Future` on timeout. 

632 

633 .. versionadded:: 4.0 

634 

635 .. versionchanged:: 4.1 

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

637 exceptions. 

638 

639 .. versionchanged:: 4.4 

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

641 

642 .. versionchanged:: 6.0.3 

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

644 

645 .. versionchanged:: 6.2 

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

647 

648 .. versionchanged:: 6.6 

649 Added the ``message`` argument. 

650 

651 """ 

652 # It's tempting to optimize this by cancelling the input future on timeout 

653 # instead of creating a new one, but A) we can't know if we are the only 

654 # one waiting on the input future, so cancelling it might disrupt other 

655 # callers and B) concurrent futures can only be cancelled while they are 

656 # in the queue, so cancellation cannot reliably bound our waiting time. 

657 future_converted = convert_yielded(future) 

658 result = _create_future() 

659 chain_future(future_converted, result) 

660 io_loop = IOLoop.current() 

661 

662 def error_callback(future: Future) -> None: 

663 try: 

664 future.result() 

665 except asyncio.CancelledError: 

666 pass 

667 except Exception as e: 

668 if not isinstance(e, quiet_exceptions): 

669 app_log.error( 

670 "Exception in Future %r after timeout", future, exc_info=True 

671 ) 

672 

673 def timeout_callback() -> None: 

674 if not result.done(): 

675 if message is None: 

676 msg = "Timeout" 

677 elif callable(message): 

678 msg = message() 

679 else: 

680 msg = message 

681 result.set_exception(TimeoutError(msg)) 

682 # In case the wrapped future goes on to fail, log it. 

683 future_add_done_callback(future_converted, error_callback) 

684 

685 timeout_handle = io_loop.add_timeout(timeout, timeout_callback) 

686 if isinstance(future_converted, Future): 

687 # We know this future will resolve on the IOLoop, so we don't 

688 # need the extra thread-safety of IOLoop.add_future (and we also 

689 # don't care about StackContext here. 

690 future_add_done_callback( 

691 future_converted, lambda future: io_loop.remove_timeout(timeout_handle) 

692 ) 

693 else: 

694 # concurrent.futures.Futures may resolve on any thread, so we 

695 # need to route them back to the IOLoop. 

696 io_loop.add_future( 

697 future_converted, lambda future: io_loop.remove_timeout(timeout_handle) 

698 ) 

699 return result 

700 

701 

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

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

704 

705 When used with ``yield`` in a coroutine, this is a non-blocking 

706 analogue to `time.sleep` (which should not be used in coroutines 

707 because it is blocking):: 

708 

709 yield gen.sleep(0.5) 

710 

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

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

713 

714 .. versionadded:: 4.1 

715 """ 

716 f = _create_future() 

717 IOLoop.current().call_later( 

718 duration, lambda: future_set_result_unless_cancelled(f, None) 

719 ) 

720 return f 

721 

722 

723class _NullFuture: 

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

725 

726 It's not actually a `Future` to avoid depending on a particular event loop. 

727 Handled as a special case in the coroutine runner. 

728 

729 We lie and tell the type checker that a _NullFuture is a Future so 

730 we don't have to leak _NullFuture into lots of public APIs. But 

731 this means that the type checker can't warn us when we're passing 

732 a _NullFuture into a code path that doesn't understand what to do 

733 with it. 

734 """ 

735 

736 def result(self) -> None: 

737 return None 

738 

739 def done(self) -> bool: 

740 return True 

741 

742 

743# _null_future is used as a dummy value in the coroutine runner. It differs 

744# from moment in that moment always adds a delay of one IOLoop iteration 

745# while _null_future is processed as soon as possible. 

746_null_future = typing.cast(Future, _NullFuture()) 

747 

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

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

750one iteration. 

751 

752This is not needed in normal use but it can be helpful in long-running 

753coroutines that are likely to yield Futures that are ready instantly. 

754 

755Usage: ``yield gen.moment`` 

756 

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

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

759 

760.. versionadded:: 4.0 

761 

762.. deprecated:: 4.5 

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

764 ``yield gen.moment``. 

765""" 

766 

767 

768class Runner: 

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

770 

771 Maintains information about pending callbacks and their results. 

772 

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

774 `.Future`) 

775 """ 

776 

777 def __init__( 

778 self, 

779 ctx_run: Callable, 

780 gen: "Generator[_Yieldable, Any, _T]", 

781 result_future: "Future[_T]", 

782 first_yielded: _Yieldable, 

783 ) -> None: 

784 self.ctx_run = ctx_run 

785 self.gen = gen 

786 self.result_future = result_future 

787 self.future: None | Future = _null_future 

788 self.running = False 

789 self.finished = False 

790 self.io_loop = IOLoop.current() 

791 if self.ctx_run(self.handle_yield, first_yielded): 

792 gen = result_future = first_yielded = None # type: ignore 

793 self.ctx_run(self.run) 

794 

795 def run(self) -> None: 

796 """Starts or resumes the generator, running until it reaches a 

797 yield point that is not ready. 

798 """ 

799 if self.running or self.finished: 

800 return 

801 try: 

802 self.running = True 

803 while True: 

804 future = self.future 

805 if future is None: 

806 raise Exception("No pending future") 

807 if not future.done(): 

808 return 

809 self.future = None 

810 try: 

811 try: 

812 value = future.result() 

813 except (Exception, asyncio.CancelledError) as e: 

814 # Save the exception for later. It's important that 

815 # gen.throw() not be called inside this try/except block 

816 # because that makes sys.exc_info behave unexpectedly. 

817 exc: BaseException | None = e 

818 else: 

819 exc = None 

820 finally: 

821 future = None 

822 

823 if exc is not None: 

824 try: 

825 yielded = self.gen.throw(exc) 

826 finally: 

827 # Break up a circular reference for faster GC on 

828 # CPython. 

829 del exc 

830 else: 

831 yielded = self.gen.send(value) 

832 

833 except (StopIteration, Return) as e: 

834 self.finished = True 

835 self.future = _null_future 

836 future_set_result_unless_cancelled( 

837 self.result_future, _value_from_stopiteration(e) 

838 ) 

839 self.result_future = None # type: ignore 

840 return 

841 except asyncio.CancelledError: 

842 self.finished = True 

843 self.future = _null_future 

844 self.result_future.cancel() 

845 self.result_future = None # type: ignore 

846 return 

847 except Exception: 

848 self.finished = True 

849 self.future = _null_future 

850 future_set_exc_info(self.result_future, sys.exc_info()) 

851 self.result_future = None # type: ignore 

852 return 

853 if not self.handle_yield(yielded): 

854 return 

855 yielded = None 

856 finally: 

857 self.running = False 

858 

859 def handle_yield(self, yielded: _Yieldable) -> bool: 

860 try: 

861 self.future = convert_yielded(yielded) 

862 except BadYieldError: 

863 self.future = Future() 

864 future_set_exc_info(self.future, sys.exc_info()) 

865 

866 if self.future is moment: 

867 self.io_loop.add_callback(self.ctx_run, self.run) 

868 return False 

869 elif self.future is None: 

870 raise Exception("no pending future") 

871 elif not self.future.done(): 

872 

873 def inner(f: Any) -> None: 

874 # Break a reference cycle to speed GC. 

875 f = None # noqa: F841 

876 self.ctx_run(self.run) 

877 

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

879 return False 

880 return True 

881 

882 def handle_exception( 

883 self, typ: type[Exception], value: Exception, tb: types.TracebackType 

884 ) -> bool: 

885 if not self.running and not self.finished: 

886 self.future = Future() 

887 future_set_exc_info(self.future, (typ, value, tb)) 

888 self.ctx_run(self.run) 

889 return True 

890 else: 

891 return False 

892 

893 

894def _wrap_awaitable(awaitable: Awaitable) -> Future: 

895 # Convert Awaitables into Futures. 

896 # Note that we use ensure_future, which handles both awaitables 

897 # and coroutines, rather than create_task, which only accepts 

898 # coroutines. (ensure_future calls create_task if given a coroutine) 

899 fut = asyncio.ensure_future(awaitable) 

900 # See comments on IOLoop._pending_tasks. 

901 loop = IOLoop.current() 

902 loop._register_task(fut) 

903 fut.add_done_callback(lambda f: loop._unregister_task(f)) 

904 return fut 

905 

906 

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

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

909 

910 The default implementation accepts lists, dictionaries, and 

911 Futures. This has the side effect of starting any coroutines that 

912 did not start themselves, similar to `asyncio.ensure_future`. 

913 

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

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

916 

917 @convert_yielded.register(asyncio.Future) 

918 def _(asyncio_future): 

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

920 

921 .. versionadded:: 4.1 

922 

923 """ 

924 if yielded is None or yielded is moment: 

925 return moment 

926 elif yielded is _null_future: 

927 return _null_future 

928 elif isinstance(yielded, (list, dict)): 

929 return multi(yielded) # type: ignore 

930 elif is_future(yielded): 

931 return typing.cast(Future, yielded) 

932 elif isawaitable(yielded): 

933 return _wrap_awaitable(yielded) # type: ignore 

934 else: 

935 raise BadYieldError(f"yielded unknown object {yielded!r}") 

936 

937 

938convert_yielded = singledispatch(convert_yielded)