Reputation: 6811
Looking at the asyncio docs, I came across this example
async def main():
# Create a "cancel_me" Task
task = asyncio.create_task(cancel_me())
# Wait for 1 second
await asyncio.sleep(1)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("main(): cancel_me is cancelled now")
asyncio.run(main())
After task.cancel()
, what is the purpose of doing await task
? Is this to wait for the future to be finished if it was ever shielded from cancellation?
In other words, why not:
async def main():
# Create a "cancel_me" Task
task = asyncio.create_task(cancel_me())
# Wait for 1 second
await asyncio.sleep(1)
task.cancel()
asyncio.run(main())
Upvotes: 1
Views: 922
Reputation: 106
From the documentation of cancel()
(under asyncio.Task
):
This arranges for a
CancelledError
exception to be thrown into the wrapped coroutine on the next cycle of the event loop.The coroutine then has a chance to clean up or even deny the request by suppressing the exception with a
try
… …except CancelledError
…finally
block.
When asyncio.CancelledError
is thrown into cancel_me()
, execution resumes in the except asyncio.CancelledError
block. For the snippet provided with the documentation, it does not in fact make any difference whether cancel_me()
is awaited or not after cancelling, because the exception handling block executes synchronously.
On the other hand, if the exception handling block did perform asynchronous operations, the difference would become visible:
async def cancel_me():
print('cancel_me(): before sleep')
try:
# Wait for 1 hour
await asyncio.sleep(3600)
except asyncio.CancelledError:
await asyncio.sleep(1)
print('cancel_me(): cancel sleep, this never gets printed')
raise
finally:
print('cancel_me(): after sleep')
async def main():
# Create a "cancel_me" Task
task = asyncio.create_task(cancel_me())
# Wait for 1 second
await asyncio.sleep(1)
task.cancel()
print("main(): cancel_me is cancelled now")
asyncio.run(main())
# Expected output:
#
# cancel_me(): before sleep
# main(): cancel_me is cancelled now
# cancel_me(): after sleep
The last, surprising print takes place because of the following:
main()
returns after its last printasyncio.run()
tries to cancel all pending taskscancel_me()
, albeit already cancelled, is still pending, awaiting on the exception block sleepfinally
clause in cancel_me()
is executed and the even loop terminatesAlso worth noting: given that asyncio.run()
throws a CancelledError
into all the tasks that are still pending, if cancel_me()
had not been cancelled already, the except asyncio.CancelledError
block would execute in its entirety.
Upvotes: 2