Optionally prevent child tasks from being cancelled in `asyncio.TaskGroup`
Ninguém assumiu esta issue ainda.
- Linguagem predominante
- Python
- Estrelas
- 77.2k
- Forks
- 36k
- Métricas de merge de PRs
- Métricas de PR pendentes
Descrição
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
Guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Direção de pesquisa
Comece com asyncio.TaskGroup e seu caminho _on_task_done; depois, revise os PRs vinculados gh-101648 e gh-105011 e a discussão anterior antes de decidir se esta API pertence ao asyncio. A issue define o comportamento esperado para exceções comuns, exceções no corpo de async-with, SystemExit e KeyboardInterrupt; o trabalho estará concluído quando houver um design acordado e os testes de implementação correspondentes.
Escrita pelo modelo de indexação a partir do texto da issue.
Avaliação
- Stack de tecnologia
- python
- Domínio
- backend
- Tipo de issue
- Funcionalidade
- Dificuldade
- 5/5
- Tempo estimado
- Mais de uma semana
- Status de atividade
- Estagnada
- Clareza
- Razoavelmente clara
- Facilidade para iniciantes
- 20/100