|
3 | 3 | from typing import Any |
4 | 4 |
|
5 | 5 | import pytest |
6 | | -from common_library.async_tools import make_async, maybe_await |
| 6 | +from common_library.async_tools import cancel_and_wait, make_async, maybe_await |
7 | 7 |
|
8 | 8 |
|
9 | 9 | @make_async() |
@@ -93,3 +93,57 @@ def fetchone(self) -> Any: # pylint: disable=no-self-use |
93 | 93 |
|
94 | 94 | sync_result = await maybe_await(SyncResultProxy().fetchone()) |
95 | 95 | assert sync_result == {"id": 2, "name": "test2"} |
| 96 | + |
| 97 | + |
| 98 | +async def test_cancel_and_wait(): |
| 99 | + state = {"started": False, "cancelled": False, "cleaned_up": False} |
| 100 | + |
| 101 | + async def coro(): |
| 102 | + try: |
| 103 | + state["started"] = True |
| 104 | + await asyncio.sleep(5) |
| 105 | + except asyncio.CancelledError: |
| 106 | + state["cancelled"] = True |
| 107 | + raise |
| 108 | + finally: |
| 109 | + state["cleaned_up"] = True |
| 110 | + |
| 111 | + task = asyncio.create_task(coro()) |
| 112 | + await asyncio.sleep(0.1) # Let coro start |
| 113 | + |
| 114 | + await cancel_and_wait(task) |
| 115 | + |
| 116 | + assert task.done() |
| 117 | + assert task.cancelled() |
| 118 | + assert state["started"] |
| 119 | + assert state["cancelled"] |
| 120 | + assert state["cleaned_up"] |
| 121 | + |
| 122 | + |
| 123 | +async def test_cancel_and_wait_propagates_external_cancel(): |
| 124 | + """ |
| 125 | + This test ensures that if the caller of cancel_and_wait is cancelled, |
| 126 | + the CancelledError is not swallowed. |
| 127 | + """ |
| 128 | + |
| 129 | + async def inner_coro(): |
| 130 | + try: |
| 131 | + await asyncio.sleep(10) |
| 132 | + except asyncio.CancelledError: |
| 133 | + await asyncio.sleep(0.1) # simulate cleanup |
| 134 | + raise |
| 135 | + |
| 136 | + task = asyncio.create_task(inner_coro()) |
| 137 | + |
| 138 | + async def outer_coro(): |
| 139 | + await cancel_and_wait(task) |
| 140 | + |
| 141 | + # Cancel the wrapper after a short delay |
| 142 | + outer_task = asyncio.create_task(outer_coro()) |
| 143 | + await asyncio.sleep(0.1) |
| 144 | + outer_task.cancel() |
| 145 | + |
| 146 | + with pytest.raises(asyncio.CancelledError): |
| 147 | + await outer_task |
| 148 | + |
| 149 | + assert task.cancelled() |
0 commit comments