Python

Quand une tâche échoue, laisser les autres nettoyer avant de remonter l’erreur

Éviter les tâches orphelines : voir comment un groupe annule ses tâches sœurs, attend leur nettoyage et remonte les erreurs ensemble.

TaskGroup annule les tâches sœurs après une erreur, attend leur nettoyage finally puis remonte le groupe d’erreurs.

Un traitement parallèle échoue alors que ses tâches sœurs détiennent encore des ressources. Si elles continuent sans supervision, le programme peut laisser des opérations inachevées derrière lui. asyncio.TaskGroup permet de superviser ces tâches ensemble. Quand une tâche lève une exception autre que asyncio.CancelledError, le groupe annule les tâches restantes et attend leur fin avant de propager les erreurs. Ce tutoriel s’adresse aux développeurs Python qui veulent placer le nettoyage dans ce parcours.

Vérifier la version puis lancer le programme

TaskGroup et except* nécessitent Python 3.11 ou supérieur. Vérifiez l’interpréteur utilisé :

python3 --version

Enregistrez le bloc suivant dans taskgroup_cleanup.py, puis lancez-le depuis le répertoire qui contient ce fichier :

python3 taskgroup_cleanup.py

Ce script n’utilise que la bibliothèque standard. La première tâche signale qu’elle a commencé puis attend indéfiniment. La seconde attend ce signal avant de lever ValueError. Le bloc except* traite le groupe d’exceptions après le nettoyage de la tâche annulée.

import asyncio


async def wait_for_cancellation(started: asyncio.Event) -> None:
    print("worker:start")
    started.set()
    try:
        await asyncio.Event().wait()
    finally:
        print("worker:cleanup")


async def fail_after_start(started: asyncio.Event) -> None:
    await started.wait()
    print("failing:raise ValueError")
    raise ValueError("synthetic failure")


async def main() -> None:
    started = asyncio.Event()
    try:
        async with asyncio.TaskGroup() as group:
            group.create_task(wait_for_cancellation(started))
            group.create_task(fail_after_start(started))
    except* ValueError as errors:
        print(f"handled: {len(errors.exceptions)} ValueError")


asyncio.run(main())

La sortie suivante est celle de l’essai historique du 28 septembre 2026 sous Python 3.14.4 :

worker:start
failing:raise ValueError
worker:cleanup
handled: 1 ValueError

L’événement synchronise le début des deux tâches pour rendre la séquence intelligible. Quand ValueError est levée, TaskGroup annule la tâche en attente; son bloc finally s’exécute avant que except* ne traite le groupe. Pour votre exécution, vérifiez que worker:cleanup apparaît avant handled: 1 ValueError : ce placement montre que le nettoyage de la tâche annulée précède le traitement du groupe. Une erreur de syntaxe sur except* appelle d’abord un contrôle de version de l’interpréteur. L’ordre historique rapporté porte sur ce programme; aucune exécution sous Python 3.11 n’est revendiquée.

Propager l’annulation après le nettoyage

asyncio.CancelledError hérite de BaseException et non de Exception. Un gestionnaire général except Exception ne la capture donc pas comme une erreur ordinaire. Si le code intercepte explicitement l’annulation pour nettoyer, il la propage généralement ensuite :

try:
    await operation()
finally:
    await release_resource()

Un bloc finally couvre aussi la propagation normale de l’annulation sans l’avaler. Évitez de supprimer CancelledError sans raison : TaskGroup et asyncio.timeout() s’appuient sur le mécanisme d’annulation. La suppression complète est un cas particulier qui demande de traiter correctement l’état d’annulation. Une annulation ne peut pas non plus interrompre instantanément du code synchrone bloquant.

Une tâche annulée avec CancelledError ne déclenche pas à elle seule le traitement réservé à l’échec ordinaire décrit ici. KeyboardInterrupt et SystemExit ont également un traitement particulier : le groupe annule les autres tâches et attend leur fin, puis relance l’exception initiale au lieu de la regrouper dans un ExceptionGroup. Ces règles figurent dans la documentation de TaskGroup pour Python 3.14; la référence Python 3.11 conservée dans les sources documente la version d’introduction.

Sources techniques

  1. Coroutines and Tasks — Python 3.11.16 documentationPython Software Foundation