Пакетная проверка вернула несколько ошибок: разбираем ExceptionGroup
Проверка пакета документов находит несколько независимых проблем. Если выбрасывать первое исключение, пользователю придётся исправлять их по одной и повторять запуск. Если собрать только строки, вызывающий код потеряет типы ошибок и не сможет отделить проблемы ввода от недоступного ресурса. Нужен способ перенести несколько исключений вместе.
В Python 3.11 появился ExceptionGroup, а except* умеет выбирать внутри группы ошибки указанного типа. Обработчик получает подгруппу, а необработанные части продолжают распространяться. Это удобно, когда один слой исправляет или оформляет только свою категорию отказов, не объявляя успешным весь пакет.
Несколько независимых ошибок
В примере группа содержит ValueError и OSError. Внутренний слой собирает ошибки значений, внешний проверяет, что системный отказ не исчез. Здесь группа создаётся явно; такой же формат может прийти от механизма конкурентного запуска. Логика обработки должна понимать, что одновременно могут существовать несколько причин отказа.
handled = []
remaining = []
try:
try:
raise ExceptionGroup("batch failed", [
ValueError("invalid row"),
OSError("disk unavailable"),
])
except* ValueError as group:
handled.extend(str(error) for error in group.exceptions)
except ExceptionGroup as group:
remaining.extend(str(error) for error in group.exceptions)
assert handled == ["invalid row"]
assert remaining == ["disk unavailable"]
print(*handled, *remaining, sep="; ")
Частичная обработка группы
Запустите файл на Python 3.12: появятся invalid row и disk unavailable. Замените except* ValueError на обычный except ValueError — он не станет искать ValueError внутри ExceptionGroup. Это различие важно при миграции старых обработчиков: наличие знакомого типа среди вложенных исключений не означает, что старый except уже его обработает.
except* получает группу даже тогда, когда выбран только один элемент. Не считайте, что caught — исходный ValueError. Для вложенных групп структура также может сохраняться, поэтому прямой проход по exceptions подходит только для известной плоской формы, как в примере. При библиотечном интерфейсе определите, допускаете ли вложенность и как формируете пользовательский отчёт.
Не собирайте в одну группу всё подряд, если после первого сбоя дальнейшая работа уже недостоверна. Пакетное накопление оправдано для независимых проверок. Если чтение заголовка не удалось, проверки полей из этого заголовка могут породить лишь каскадные ошибки. Лучше отделить этапы и продолжать только там, где результаты остаются осмысленными.
Проверьте три сценария: только обработанные ошибки, только необработанные и смешанную группу. Для смешанной особенно важно, чтобы знакомая проблема ввода не спрятала отказ инфраструктуры. Дальнейший интерфейс может показать человеку список исправляемых строк, а оператору — причину недоступности ресурса. Типы исключений позволяют сохранить это различие без разбора свободного текста сообщений.
Когда локальный пример работает, полезно разобрать соседние механизмы языка и их взаимодействие. В курсе «Python для профессионалов» есть практика по итераторам, генераторам, декораторам, asyncio, метапрограммированию и профилированию. Эти темы помогают понимать время жизни объектов, управление выполнением и причины поведения прикладной программы.
Посмотреть программу и практику курса.
Документация: официальный источник по теме.
Другие задачи по теме: Типы, тесты и поведение программы.
Продолжить на практике
- Python для профессионалов ↗
Python для профессионалов — курс для людей, стремящихся углубить свои знания языка. Вы изучите итераторы, генераторы, декораторы, современные возможности Python (3.8+), освоите метапрограммирование, асинхронность, внутреннее устройство интерпретатора, инструменты оптимизации и профилирования, а также получите уверенность в использовании продвинутых возможностей стандартной библиотеки.
