Ваш существующий код в порядке, но если вы хотите сделать эквивалент цепочки исключений, вы можете.Если вы хотите перейти к тому, как это сделать, перейдите к пункту 3 в конце ответа.
Чтобы объяснить, как можно выполнять такие действия, как изменение распространяющегося исключения или выполнение эквивалента raise Something() from existing_exception
Во-первых, нам нужно объяснить, как работает состояние исключения на уровне C.
Распространяющееся исключение представлено на поток индикатор ошибки , состоящий из тип , значение и traceback .Это звучит очень похоже на sys.exc_info()
, но это не то же самое.sys.exc_info()
для исключений, которые были пойманы кодом уровня Python, а не для исключений, которые все еще распространяются.
Индикатор ошибки может быть ненормализован , который в основномозначает, что работа по созданию объекта исключения не была выполнена, и значение в индикаторе ошибки не является экземпляром исключения type .Это состояние существует для эффективности;если индикатор ошибки очищается на PyErr_Clear
до того, как потребуется нормализация, Python пропускает большую часть работы по созданию исключения.Нормализация исключений выполняется с помощью PyErr_NormalizeException
, с небольшим количеством дополнительной работы в PyException_SetTraceback
для установки атрибута __traceback__
объекта исключения.
PyErr_Clear
Это что-то вроде эквивалента блока C except
, но он просто очищает индикатор ошибки, не позволяя вам просматривать большую часть информации об исключениях.Чтобы поймать исключение и проверить его, вам нужно PyErr_Fetch
.PyErr_Fetch
похоже на перехват исключения и проверку sys.exc_info()
, но оно не устанавливает sys.exc_info()
и не нормализует исключение.Он очищает индикатор ошибки и выдает вам необработанное содержимое индикатора ошибки напрямую.
Явная цепочка исключений (raise Something() from existing_exception
) работает, выполняя операции PyException_SetCause
, чтобы установить новые исключения __cause__
к существующему исключению.Для этого требуются объекты исключений для обоих исключений, поэтому, если вы хотите сделать эквивалент из C, вам придется нормализовать исключения и вызвать PyException_SetCause
самостоятельно.
Неявное сцепление исключений (raise Something()
в *Блок 1060 *) работает через PyException_SetContext
, чтобы установить новое исключение __context__
в существующее исключение.Подобно PyException_SetCause
, для этого требуются объекты исключений и нормализация исключений.raise Something() from existing_exception
внутри блока except
фактически устанавливает и __cause__
, и __context__
, и если вы хотите выполнить явную цепочку исключений на уровне C, вы обычно должны делать то же самое.
- Технически, насколько я могу судить, не обязательно, но, возможно, в любом случае это хорошая идея.Похоже,
PyErr_Format
и другие функции, которые устанавливают индикатор ошибки, сначала очистят индикатор ошибки, если он уже установлен, но для большинства из них это не задокументировано. - В некотором роде, но, вероятно, это плохоидея.Вы можете нормализовать индикатор ошибки и установить атрибут
message
объекта исключения, но это не повлияет на args
или что-либо еще, что класс исключения может сделать со своими аргументами, и это может привести к странным проблемам.В качестве альтернативы вы можете получить индикатор ошибки с помощью PyErr_Fetch
и восстановить его с новой строкой для значения с PyErr_Restore
, но при этом объект существующего исключения будет отброшен, если он есть, и он делает предположения о сигнатуре класса исключения. Да, это возможно, но делать это с помощью общедоступных функций C API довольно неудобно и вручную.Вам придется вручную выполнить большую часть нормализации, поднятия и повышения исключений.
Там есть усилий , чтобы сделать цепочку исключений уровня C более удобной, нодо сих пор все более удобные функции считаются внутренними.Например, _PyErr_FormatFromCause
похож на PyErr_Format
, но он объединяет новое исключение из существующего распространяющегося исключения (через __context__
и __cause__
.
Я бы не рекомендовал звонить прямо сейчас;это очень новый (3.6+), и он очень вероятно изменится (в частности, я бы не удивился, увидев, что он потеряет свое ведущее подчеркивание в новой версии Python).Вместо этого, копирование реализации из _PyErr_FormatFromCause
/ _PyErr_FormatVFromCause
(и соблюдение лицензии ) - это хороший способ убедиться, что у вас есть правильные биты нормализации и правильной цепочки.
Это также полезная ссылка для работы, если вы хотите выполнить неявную (__context__
только) цепочку исключений на уровне C - просто удалите часть, которая обрабатывает __cause__
.