'Python asyncio: Race condition for canceling tasks

I the python examples (https://docs.python.org/3/library/asyncio-task.html#asyncio.Task.cancel), a task can be canceled like in the following snippet:

task.cancel()
try:
   await task
except asyncio.CancelledError:
   print("main(): cancel_me is cancelled now")

But this can cause a race condition when the task itself is canceled at the same time as canceling the other task. I have added an example which causes this problem by one addition await statement await asyncio.sleep(0)

import asyncio


async def subtask(i: int):
    try:
        print(f"        subtask {i}: start")
        await asyncio.sleep(2)
    except asyncio.CancelledError:
        print(f"        subtask {i}: Clean Up")
        raise


async def handling():
    i = 0
    while True:
        print(f"    handling {i}: start")
        sub_task = asyncio.create_task(subtask(i))
        await asyncio.sleep(1)
        print(f"    handling {i}: cancel subtask")
        sub_task.cancel()  # (1) There is a reason why subtask must be canceled, but another must be restarted.
        try:
            await sub_task  # (2)
        except asyncio.CancelledError:
            print(f"    handling {i}: subtask canceled {sub_task.cancelled()}")
        print(f"    handling {i}: end")
        i += 1


async def main():
    print("main: start")
    handling_task = asyncio.create_task(handling())
    await asyncio.sleep(1)
    await asyncio.sleep(0)  # (3) This addition await call forces that handling_task is cancel at the same as calling (2)
    print("main: cancel")
    handling_task.cancel()  # handling_task must be canceled by a reason at the same time as the other task was canceled by a reason (1)
    try:
        await handling_task
    except asyncio.CancelledError:
        print(f"main: CancelledError. Task canceled {handling_task.cancelled()}.")
        pass  # Task was cancelled correctly
    print("main: end")


asyncio.run(main())

So my guess is, it is never allowed to to catch a CancelledError of a canceled task?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source