Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32k
Description
Bug report
Bug description:
Consider the following code:
importasyncioasyncdefwait_and_raise():awaitasyncio.sleep(0.5)raiseRuntimeError(1)asyncdefwait_and_start(tg):try:awaitasyncio.sleep(1)finally:try:tg.create_task(asyncio.sleep(1))exceptRuntimeErrorase:print(f"wait_and_start() caught{e!r}")asyncdefmain():try:asyncwithasyncio.TaskGroup()astg:tg.create_task(wait_and_start(tg))tg.create_task(wait_and_raise())exceptExceptionase:print(f"main() caught{e!r}")try:tg.create_task(asyncio.sleep(1))exceptRuntimeErrorase:print(f"main() caught{e!r}")asyncio.run(main())
This gives the following output
wait_and_start() caught RuntimeError('TaskGroup <TaskGroup tasks=1 errors=1 cancelling> is shutting down')C:\code\taskgrouptest.py:16: RuntimeWarning: coroutine 'sleep' was never awaited print(f"wait_and_start() caught {e!r}")RuntimeWarning: Enable tracemalloc to get the object allocation tracebackmain() caught ExceptionGroup('unhandled errors in a TaskGroup', [RuntimeError(1)])main() caught RuntimeError('TaskGroup <TaskGroup cancelling> is finished')C:\code\taskgrouptest.py:29: RuntimeWarning: coroutine 'sleep' was never awaited print(f"main() caught {e!r}")RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Arguably, when you calltg.create_task()
on a task group that is shutting down or has finished, the calling code "knows" about the error because it gets aRuntimeError
exception (as you can see above), so there is no need to get a warning about a coroutine that was not awaited. So, when aTaskGroup
encounters this situation, it should close the coroutine before raising the error.
The other argument would be that this still represents a design mistake so should still get the warning. I can see both points of view but I'm raising this issue so a conscious decision can be made.
For comparison, when you do this on aTrio Nursery orAnyIO TaskGroup that has already closed, a coroutine never even gets created in the first place, because you use a different syntax (nursery.start_soon(foo, 1, 2)
rather thantg.create_task(foo(1, 2))
), so it's a lot like if asyncio were to close the coroutine. The situation is a bit different for a nursery that is shutting down: then it runs till the first (unshielded)await
and is cancelled at that point, which is possible because they use level-based cancellation rather than edge-based cancellation.
CPython versions tested on:
3.12
Operating systems tested on:
Windows
Linked PRs
Metadata
Metadata
Assignees
Projects
Status