(*, sleep: float, timeout: float)
| 598 | loop = asyncio.new_event_loop() |
| 599 | |
| 600 | async def make_request_with_timeout(*, sleep: float, timeout: float): |
| 601 | task = self.current_task() |
| 602 | loop = task.get_loop() |
| 603 | |
| 604 | timed_out = False |
| 605 | structured_block_finished = False |
| 606 | outer_code_reached = False |
| 607 | |
| 608 | def on_timeout(): |
| 609 | nonlocal timed_out |
| 610 | timed_out = True |
| 611 | task.cancel() |
| 612 | |
| 613 | timeout_handle = loop.call_later(timeout, on_timeout) |
| 614 | try: |
| 615 | try: |
| 616 | # Structured block affected by the timeout |
| 617 | await asyncio.sleep(sleep) |
| 618 | structured_block_finished = True |
| 619 | finally: |
| 620 | timeout_handle.cancel() |
| 621 | if ( |
| 622 | timed_out |
| 623 | and task.uncancel() == 0 |
| 624 | and type(sys.exception()) is asyncio.CancelledError |
| 625 | ): |
| 626 | # Note the five rules that are needed here to satisfy proper |
| 627 | # uncancellation: |
| 628 | # |
| 629 | # 1. handle uncancellation in a `finally:` block to allow for |
| 630 | # plain returns; |
| 631 | # 2. our `timed_out` flag is set, meaning that it was our event |
| 632 | # that triggered the need to uncancel the task, regardless of |
| 633 | # what exception is raised; |
| 634 | # 3. we can call `uncancel()` because *we* called `cancel()` |
| 635 | # before; |
| 636 | # 4. we call `uncancel()` but we only continue converting the |
| 637 | # CancelledError to TimeoutError if `uncancel()` caused the |
| 638 | # cancellation request count go down to 0. We need to look |
| 639 | # at the counter vs having a simple boolean flag because our |
| 640 | # code might have been nested (think multiple timeouts). See |
| 641 | # commit 7fce1063b6e5a366f8504e039a8ccdd6944625cd for |
| 642 | # details. |
| 643 | # 5. we only convert CancelledError to TimeoutError; for other |
| 644 | # exceptions raised due to the cancellation (like |
| 645 | # a ConnectionLostError from a database client), simply |
| 646 | # propagate them. |
| 647 | # |
| 648 | # Those checks need to take place in this exact order to make |
| 649 | # sure the `cancelling()` counter always stays in sync. |
| 650 | # |
| 651 | # Additionally, the original stimulus to `cancel()` the task |
| 652 | # needs to be unscheduled to avoid re-cancelling the task later. |
| 653 | # Here we do it by cancelling `timeout_handle` in the `finally:` |
| 654 | # block. |
| 655 | raise TimeoutError |
| 656 | except TimeoutError: |
| 657 | self.assertTrue(timed_out) |
nothing calls this directly
no test coverage detected