Сообщить об ошибке.

Исключения ExceptionGroup и BaseExceptionGroup в Python

ExceptionGroup(msg, excs)
BaseExceptionGroup(msg, excs):

Новое в Python 3.11

Изменено в Python 3.12: Когда конструкция try-except* обрабатывает всю группу ExceptionGroup и вызывает еще одно исключение, это исключение больше не помещается в ExceptionGroup. Это поведение было изменено в версии 3.11.4. но не задокументировано (Предоставлено Ирит Катриэль.)

Оба типа исключений ExceptionGroup() и BaseExceptionGroup() заключают исключения в последовательность excs. Аргумент msg должен быть строкой. Разница между этими двумя классами заключается в том, что BaseExceptionGroup расширяет BaseException и может обертывать любое исключение, а ExceptionGroup расширяет Exception и может обертывать только подклассы Exception. Этот дизайн таков, что кроме Exception ловится ExceptionGroup, но не BaseExceptionGroup.

Конструктор BaseExceptionGroup возвращает ExceptionGroup, а не BaseExceptionGroup, если все содержащиеся исключения являются экземплярами Exception, поэтому его можно использовать для автоматического выбора. Конструктор ExceptionGroup, с другой стороны, вызывает TypeError, если какое-либо содержащееся исключение не является подклассом Exception.

Атрибуты и методы BaseExceptionGroup:

.message:

Атрибут .message - это аргумент msg для конструктора. Это атрибут только для чтения.

.exceptions:

Атрибут .exceptions - это Кортеж исключений в последовательности excs, передаваемой конструктору. Это атрибут только для чтения.

.subgroup(condition):

Метод .subgroup() возвращает группу исключений, содержащую только те исключения из текущей группы, которые соответствуют условию condition, или None, если результат пуст.

Условие condition может быть либо функцией, которая принимает исключение и возвращает True для тех, которые должны быть в подгруппе, либо может быть типом исключения или кортежем типов исключений, который используется для проверки на совпадение с помощью той же проверки, что и используется в предложении исключения.

Структура вложенности текущего исключения сохраняется в результате, как и значения его сообщения, полей __traceback__, __cause__, __context__ и __notes__. Пустые вложенные группы исключаются из результата.

Условие condition проверяется для всех исключений во вложенной группе исключений, включая группу исключений верхнего уровня и все вложенные группы исключений. Если условие истинно для такой группы исключений, то оно включается в результат полностью.

.split(condition):

Метод работает подобно .subgroup(), но возвращает пару (match, rest), где match - это подгруппа (условие), а rest - оставшаяся несовпадающая часть.

.derive(excs):

Возвращает группу исключений с тем же сообщением, __traceback__, __cause__, __context__ и __notes__, но заключает исключения в excs.

Этот метод используется методами .subgroup() и .split(). Подкласс должен переопределить его, чтобы .subgroup() и split() возвращали экземпляры подкласса, а не ExceptionGroup.

Методы .subgroup() и .split() копируют поля __traceback__, __cause__, __context__ и __notes__ из исходной группы исключений в группу, возвращенную методом .derive(), поэтому эти поля не нужно обновлять с помощью derive().

class MyGroup(ExceptionGroup):
    def derive(self, exc):
        return MyGroup(self.message, exc)

e = MyGroup("eg", [ValueError(1), TypeError(2)])
e.add_note("a note")
e.__context__ = Exception("context")
e.__cause__ = Exception("cause")
try:
   raise e
except Exception as e:
   exc = e

match, rest = exc.split(ValueError)
exc, exc.__context__, exc.__cause__, exc.__notes__
# (MyGroup('eg', [ValueError(1), TypeError(2)]), Exception('context'), Exception('cause'), ['a note'])
match, match.__context__, match.__cause__, match.__notes__
# (MyGroup('eg', [ValueError(1)]), Exception('context'), Exception('cause'), ['a note'])
rest, rest.__context__, rest.__cause__, rest.__notes__
# (MyGroup('eg', [TypeError(2)]), Exception('context'), Exception('cause'), ['a note'])
exc.__traceback__ is match.__traceback__ is rest.__traceback__
# True

Использование исключения BaseExceptionGroup.

Обратите внимание, что BaseExceptionGroup определяет метод __new__(), поэтому подклассы, которым нужна другая сигнатура конструктора, должны переопределить ее, а не __init__().

В примере определяется подкласс группы исключений, который принимает код выхода exit_code и создает из него сообщение группы.

class Errors(ExceptionGroup):
   def __new__(cls, errors, exit_code):
      self = super().__new__(Errors, f"exit code: {exit_code}", errors)
      self.exit_code = exit_code
      return self

   def derive(self, excs):
      return Errors(excs, self.exit_code)

Как и ExceptionGroup, любой подкласс BaseExceptionGroup, который также является подклассом Exception, может обертывать только экземпляры Exception.