Optionally prevent child tasks from being cancelled in `asyncio.TaskGroup`
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 77.2k
- Forks
- 36k
- Ø Merge
- 1 T. 9 Std.
- Gemergte PRs (30 T.)
- 558
Beschreibung
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 withblock still have to cause child tasks to be canceled. SystemExitandKeyboardInterruptraised 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
- gh-101648
- gh-105011
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne mit asyncio.TaskGroup und seinem _on_task_done-Pfad, prüfe anschließend die verknüpften PRs gh-101648 und gh-105011 sowie die vorherige Diskussion, bevor du entscheidest, ob diese API in asyncio gehört. Das Issue definiert das erwartete Verhalten für gewöhnliche Ausnahmen, Ausnahmen im async-with-Body, SystemExit und KeyboardInterrupt; abgeschlossen ist die Aufgabe, wenn ein abgestimmtes Design und entsprechende Implementierungstests vorhanden sind.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- backend
- Issue-Typ
- Feature
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 20/100