Grupos de Excepciones y Sintaxis except*
En sistemas concurrentes o en tareas que ejecutan múltiples operaciones paralelas (como corrutinas en asyncio o procesamiento en lotes), es muy común que varias excepciones ocurran al mismo tiempo.
Para abordar este reto, Python 3.11 (PEP 654) introdujo los Grupos de Excepciones (ExceptionGroup) y la sentencia especializada except*, permitiendo capturar y gestionar múltiples fallos de forma granular.
1. ¿Qué es un ExceptionGroup? #
Un ExceptionGroup es un contenedor que agrupa una o más instancias de excepciones bajo un único objeto de error jerárquico.
1# Creación manual de un grupo de excepciones 2errores = ExceptionGroup( 3 "Problemas durante la validación del sistema", 4 [ 5 ValueError("El puerto debe ser un número entero"), 6 TypeError("El host debe ser una cadena de texto"), 7 FileNotFoundError("No se encontró el archivo de configuración .env"), 8 ] 9)
2. Capturar grupos con except* (Python 3.11+) #
La sintaxis tradicional try ... except Exception: solo captura una excepción a la vez o trata al grupo completo como un único error opaco.
La nueva sintaxis try ... except* permite hacer coincidir y manejar tipos específicos de excepciones dentro del grupo, permitiendo que las no capturadas se sigan propagando:
1def tarea_compleja(): 2 raise ExceptionGroup( 3 "Fallo en operaciones concurrentes", 4 [ 5 ValueError("Dato numérico inválido"), 6 KeyError("Falta la clave 'id_usuario'"), 7 ValueError("Dato fuera de rango"), 8 ] 9 ) 10 11try: 12 tarea_compleja() 13except* ValueError as eg_val: 14 print(f"Se capturaron {len(eg_val.exceptions)} errores de tipo ValueError:") 15 for err in eg_val.exceptions: 16 print(f" - {err}") 17except* KeyError as eg_key: 18 print(f"Se capturó KeyError: {eg_key.exceptions[0]}")Salida de consola Se capturaron 2 errores de tipo ValueError: - Dato numérico inválido - Dato fuera de rango Se capturó KeyError: Falta la clave 'id_usuario'
3. Diferencia clave entre except y except* #
| Característica | except tradicional | except* (Python 3.11+) |
|---|---|---|
| Flujo de ejecución | Ejecuta el primer bloque coincidente y finaliza el try. | Puede ejecutar múltiples bloques except* si el grupo contiene errores de diferentes tipos. |
| Propagación | Oculta todos los errores no especificados si se usa except Exception. | Propaga automáticamente las excepciones restantes del grupo que no coincidieron con ningún bloque except*. |
| Uso principal | Flujo secuencial y código tradicional. | Concurrencia (asyncio.TaskGroup), hilos y operaciones por lotes. |
4. Trazas de Error Mejoradas (Enhanced Tracebacks) #
Desde Python 3.11, el intérprete muestra indicadores visuales precisos (^^^^^^) que señalan el lugar exacto de la línea donde ocurrió el error, simplificando la depuración de expresiones anidadas:
1Traceback (most recent call last): 2 File "calculo.py", line 12, in <module> 3 resultado = datos["usuarios"][0]["perfil"]["edad"] 4 ~~~~~~~~~~~~~~~~~^^^ 5KeyError: 'perfil'
Resumen del tema
Conceptos clave #
ExceptionGroup: clase de excepción que encapsula una colección de excepciones que ocurrieron simultáneamente.- Sintaxis
except* TipoError:evalúa y desglosa el grupo, ejecutando los manejadores correspondientes para cada tipo de error individual. - Manejo concurrente: esencial para orquestar fallos en
asyncio.TaskGroupsin perder información de errores paralelos. - Trazas enriquecidas: localización exacta del segmento causante del error en Python 3.11+.
Qué debes recordar #
Usa except* para capturar excepciones concurrentes cuando trabajes con grupos de tareas asíncronas o procesamiento paralelo en Python 3.11+.