mirror of
https://github.com/python/cpython.git
synced 2025-08-27 20:25:18 +00:00
gh-116720: Fix corner cases of taskgroups (#117407)
This prevents external cancellations of a task group's parent task to be dropped when an internal cancellation happens at the same time. Also strengthen the semantics of uncancel() to clear self._must_cancel when the cancellation count reaches zero. Co-Authored-By: Tin Tvrtković <tinchester@gmail.com> Co-Authored-By: Arthur Tacca
This commit is contained in:
parent
22b25d1eba
commit
fa58e75a86
8 changed files with 183 additions and 13 deletions
|
@ -833,6 +833,72 @@ class TestTaskGroup(unittest.IsolatedAsyncioTestCase):
|
|||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(run_coro_after_tg_closes())
|
||||
|
||||
async def test_cancelling_level_preserved(self):
|
||||
async def raise_after(t, e):
|
||||
await asyncio.sleep(t)
|
||||
raise e()
|
||||
|
||||
try:
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
tg.create_task(raise_after(0.0, RuntimeError))
|
||||
except* RuntimeError:
|
||||
pass
|
||||
self.assertEqual(asyncio.current_task().cancelling(), 0)
|
||||
|
||||
async def test_nested_groups_both_cancelled(self):
|
||||
async def raise_after(t, e):
|
||||
await asyncio.sleep(t)
|
||||
raise e()
|
||||
|
||||
try:
|
||||
async with asyncio.TaskGroup() as outer_tg:
|
||||
try:
|
||||
async with asyncio.TaskGroup() as inner_tg:
|
||||
inner_tg.create_task(raise_after(0, RuntimeError))
|
||||
outer_tg.create_task(raise_after(0, ValueError))
|
||||
except* RuntimeError:
|
||||
pass
|
||||
else:
|
||||
self.fail("RuntimeError not raised")
|
||||
self.assertEqual(asyncio.current_task().cancelling(), 1)
|
||||
except* ValueError:
|
||||
pass
|
||||
else:
|
||||
self.fail("ValueError not raised")
|
||||
self.assertEqual(asyncio.current_task().cancelling(), 0)
|
||||
|
||||
async def test_error_and_cancel(self):
|
||||
event = asyncio.Event()
|
||||
|
||||
async def raise_error():
|
||||
event.set()
|
||||
await asyncio.sleep(0)
|
||||
raise RuntimeError()
|
||||
|
||||
async def inner():
|
||||
try:
|
||||
async with taskgroups.TaskGroup() as tg:
|
||||
tg.create_task(raise_error())
|
||||
await asyncio.sleep(1)
|
||||
self.fail("Sleep in group should have been cancelled")
|
||||
except* RuntimeError:
|
||||
self.assertEqual(asyncio.current_task().cancelling(), 1)
|
||||
self.assertEqual(asyncio.current_task().cancelling(), 1)
|
||||
await asyncio.sleep(1)
|
||||
self.fail("Sleep after group should have been cancelled")
|
||||
|
||||
async def outer():
|
||||
t = asyncio.create_task(inner())
|
||||
await event.wait()
|
||||
self.assertEqual(t.cancelling(), 0)
|
||||
t.cancel()
|
||||
self.assertEqual(t.cancelling(), 1)
|
||||
with self.assertRaises(asyncio.CancelledError):
|
||||
await t
|
||||
self.assertTrue(t.cancelled())
|
||||
|
||||
await outer()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue