Description
Problem
The documentation on asyncio.Task.cancel says
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. Therefore, unlike Future.cancel(), Task.cancel() does not guarantee that the Task will be cancelled, although suppressing cancellation completely is not common and is actively discouraged.
These lines appear to suggest that a CancelledError
will always be raised into the coroutine, which is not true -- if cancel()
is called before the Task starts running, the task is guaranteed to be cancelled -- the coroutine is not given a chance to run at all and no exception is thrown into it.
Example
The following program prints nothing on stdout and exits with code 1.
import asyncio
async def test():
try:
print("test() is called!")
await asyncio.sleep(1)
except asyncio.CancelledError:
print("test() is cancelled!")
async def main():
task = asyncio.create_task(test())
task.cancel()
await task
asyncio.run(main())
Suggestion
Clarify that cancelling a task that has not started is always "effective" and will not cause an exception to be thrown into the coroutine.
Specifically, the quoted paragraphs might be amended as follows (key changes in bold):
If the Task has not started, it will be done with CancelledError and will not be started.
If the Task has started, a CancelledError exception will 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. Therefore, unlike Future.cancel(), Task.cancel() does not guarantee that a started Task will be cancelled, although suppressing cancellation completely is not common and is actively discouraged.
Metadata
Metadata
Assignees
Projects
Status