Open
Description
Feature or enhancement
Add a flag to asyncio.TaskGroup
to control whether, if a child task crashes, other should be cancelled or not.
Pitch
Currently asyncio.TaskGroup
always cancels all child tasks if one fails. Even if that's the most common use case, I'd like to be able to switch from the current behaviour to prevent child tasks cancellation when one failure occurs and raise an ExceptionGroup
with all exceptions raised (only after other tasks completed).
- Excpetions raised in the
async with
block still have to cause child tasks to be canceled. SystemExit
andKeyboardInterrupt
raised in a child task still cancel other tasks.
Example usage:
async def zero_div():
1 / 0
async def wait_some(d):
await asyncio.sleep(d)
print(f'finished after {d}')
async def main():
async with asyncio.TaskGroup(abort_on_first_exception=False) as tg:
tg.create_task(wait_some(2))
tg.create_task(wait_some(3))
tg.create_task(zero_div())
asyncio.run(main())
# Should print out:
# finished after 2
# finished after 3
# and then raise an `ExceptionGroup` with a `ZeroDivisionError`
Looking at asyncio.TaskGroup
source code, it seems that it could be achieved by adding the abort_on_first_exception
(or whatever it should be named) flag to the __init__
method and then modify _on_task_done
as follow:
class TaskGroup:
def __init__(self, abort_on_first_exception=True):
...
self._abort_on_first_exception = abort_on_first_exception
...
def _on_task_done(self, task):
...
self._errors.append(exc)
is_base_error = self._is_base_error(exc)
if is_base_error and self._base_error is None:
self._base_error = exc
if self._parent_task.done():
# Not sure if this case is possible, but we want to handle
# it anyways.
self._loop.call_exception_handler({
'message': f'Task {task!r} has errored out but its parent '
f'task {self._parent_task} is already completed',
'exception': exc,
'task': task,
})
return
if not self._aborting and not self._parent_cancel_requested:
#comment skipped for brevity
if is_base_error or self._abort_on_first_exception:
self._abort()
self._parent_cancel_requested = True
self._parent_task.cancel()
If it's reasonable to have it in asyncio
, I'll be glad to submit a PR for it.
Previous discussion
Linked PRs
Metadata
Metadata
Assignees
Labels
Python modules in the Lib dirPython modules in the Lib dirA feature request or enhancementA feature request or enhancement
Projects
Status
Todo