I am having a hard time understanding how asyncio.CancelledError is propagating. I have the following code:
import asyncio
import traceback
async def uselessTask():
return await asyncio.sleep(10)
async def mainAsync():
task = asyncio.create_task(uselessTask())
asyncio.get_running_loop().call_later(1, lambda: task.cancel("CANCELLING USELESS TASK"))
try:
results = await task
except asyncio.CancelledError as exc:
print(f"Caught exception {type(exc).__name__}: {exc}")
print(f"Task: {task}")
traceback.print_exc()
return
asyncio.run(mainAsync())
The execution of the above yields the following (both for python 3.9.6 and 3.10.7):
Caught exception CancelledError:
Task: <Task cancelled name='Task-2' coro=<uselessTask() done, defined at C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py:5>>
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 6, in uselessTask
return await asyncio.sleep(10)
File "C:\Users\<user>\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 605, in sleep
return await future
asyncio.exceptions.CancelledError: CANCELLING USELESS TASK
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 14, in mainAsync
results = await task
asyncio.exceptions.CancelledError
Process finished with exit code 0
however my expectation is that the exception caught during execution of results = await task is the same exact one that happened in the task and therefore present the same cancellation message "CANCELLING USELESS TASK".
However as you can see there is no cancellation message on the asyncio.CancelledError caused by consuming the task.
the other odd thing that I am not understanding is, if i break in the exception block and inspect the task variable, i see that the task is cancelled, however the _cancel_message is empty.
I am not exactly understanding why the exceptions are bubbling that way or why the cancellation message is not seen other than in the task (for instance if i try:except: the uselessTask() function, the caught exception has the message). Also, the traceback message "During handling of the above exception, another exception occurred:" seems to indicate that no exception chaining happened as chaining yields "The above exception was the direct cause of the following exception:"
I am browsing the python documentation but i cannot seem to correlate what i am seeing to what is written.
EDIT:
Just to show better my expectation:
import asyncio
import traceback
async def uselessTask():
try:
await asyncio.sleep(10)
except asyncio.CancelledError as exc:
raise RuntimeError(*exc.args)
return
async def mainAsync():
task = asyncio.create_task(uselessTask())
asyncio.get_running_loop().call_later(1, lambda: task.cancel("CANCELLING USELESS TASK"))
try:
results = await task
except (asyncio.CancelledError, RuntimeError) as exc:
print(f"Caught exception {type(exc).__name__}: {exc}")
print(f"Task: {task}")
traceback.print_exc()
return
asyncio.run(mainAsync())
This code returns
Caught exception RuntimeError: CANCELLING USELESS TASK
Task: <Task finished name='Task-2' coro=<uselessTask() done, defined at C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py:5> exception=RuntimeError('CANCELLING USELESS TASK')>
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 7, in uselessTask
await asyncio.sleep(10)
File "C:\Users\<user>\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 605, in sleep
return await future
asyncio.exceptions.CancelledError: CANCELLING USELESS TASK
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 20, in mainAsync
results = await task
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 9, in uselessTask
raise RuntimeError(*exc.args)
RuntimeError: CANCELLING USELESS TASK
Process finished with exit code 0
This code on the other hand
import asyncio
import traceback
async def uselessTask():
try:
await asyncio.sleep(10)
except asyncio.CancelledError as exc:
raise asyncio.CancelledError(*exc.args)
return
async def mainAsync():
task = asyncio.create_task(uselessTask())
asyncio.get_running_loop().call_later(1, lambda: task.cancel("CANCELLING USELESS TASK"))
try:
results = await task
except (asyncio.CancelledError, RuntimeError) as exc:
print(f"Caught exception {type(exc).__name__}: {exc}")
print(f"Task: {task}")
traceback.print_exc()
return
asyncio.run(mainAsync())
Returns this:
Caught exception CancelledError:
Task: <Task cancelled name='Task-2' coro=<uselessTask() done, defined at C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py:5>>
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 7, in uselessTask
await asyncio.sleep(10)
File "C:\Users\<user>\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 605, in sleep
return await future
asyncio.exceptions.CancelledError: CANCELLING USELESS TASK
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 9, in uselessTask
raise asyncio.CancelledError(*exc.args)
asyncio.exceptions.CancelledError: CANCELLING USELESS TASK
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Roaming\JetBrains\PyCharmCE2022.2\scratches\scratch_22.py", line 20, in mainAsync
results = await task
asyncio.exceptions.CancelledError
Process finished with exit code 0
as you can see, unwrapping the task result raises the asyncio.CancelledError but the message is now gone, while when raising any other exception, the unwrapping preserves the Exception arguments
