-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
Description
Bug report
Today I came across the following problem:
import asyncio
ready = asyncio.Event()
async def task_func():
try:
ready.set()
await asyncio.sleep(1)
except asyncio.CancelledError:
# this is required, otherwise a TimeoutError is not created below,
# instead, the timeout-generated CancelledError is just raised upwards.
asyncio.current_task().uncancel()
finally:
# Perform some cleanup, with a timeout
try:
async with asyncio.timeout(0):
await asyncio.sleep(1)
except asyncio.TimeoutError:
pass
async def main():
task = asyncio.create_task(task_func())
await ready.wait()
await asyncio.sleep(0.01)
# the task is now sleeping, lets send it an exception
task.cancel()
# We expect the task to finish cleanly.
await task
asyncio.run(main())The documentation mentions that sometimes an application may want to suppress CancelledError. What it fails
to mention is that unless the cancel state is subsequently cancelled with task.uncancel(), the task remains in a
cancelled state, and context managers such as asyncio.timeout will not work as designed. However, task.uncancel()
is not supposed to be called by user code.
I think the documentation should mention this use case for task.uncancel(), particularly in the context of catching (and choosing to ignore) CancelledError.
This could also be considered a bug in asyncio.timeout. It prevents the use of the Timeout context manager within cleanup code, even if the intention is not to ignore a CancelledError.
It should also be noted that the library asyncio_timeout on which the asyncio.timeout implementation is based, does not have this problem. Timeouts continue to work as designed, even if the task has been previously cancelled.
Your environment
- CPython versions tested on: 3.11
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status