Одна из открытых проблем со времён PEP 3134 — подавление контекста, так как на тот момент решения не было найдено. Данный PEP исправляет это упущение.
Причина
Есть два способа создать исключение:
- Python сам делает это (ошибки кода, отсутствующие ресурсы, завершение циклов и т.д.)
- ручками (с помощью выражения raise)
try: value = int(value) except Exception: raise DbfError(...)По любому начальное исключение (ValueError, TypeError или что-то в этом роде) уже не релевантно. С этого момента нам нужно только DbfError, однако, если это исключение будет выведено, то мы увидим информацию об обоих исключениях.
Альтернативы
Были предложены следующие возможности решения:
- raise as NewException()
Используется ключевое слово as. Может ввести в заблуждение, так как на самом деле мы не перевозбуждаем начальное исключение - raise NewException() from None
В русле текущего синтаксиса объявления предыдущего исключения - exc=NewException(); exc.__context__=None; raise exc
Более многословный способ предыдущего варианта - raise NewException.no_context(...)
Создаем метод класса для подавления контекста
Я предлагаю использовать второй вариант:
raise NewException from NoneВ данном случае мы имеем преимущество в том, что используем существующий шаблон для установки причины возбуждения исключения:
raise KeyError() from NameError()но поскольку причиной будет None, предыдущий контекст не будет отображаться стандартной процедурой отображения исключения.
Обсуждение реализации
На данный момент None является значением по умолчанию для __context__ и __cause__. Для обеспечения поддержки raise ... from None (что должно установить значение __cause__ в None) нам нужно другое значение по умолчанию для __cause__. Было предложено несколько идей, как это реализовать на уроне языка:
- Переписать информацию в имеющемся исключении (пошагово и оставить значение None для __cause__)
Отклонено, так как это может серьезно помешать отладке из-за плохого сообщения об ошибке - Использовать одно из булевых значений в
__cause__: False может быть значением по умолчанию и при использовании from ... будет заменено либо на соответствующее исключение, либо на None
Отклонено, так как поощряет использование двух разных типов объектов для __cause__, причем для одного из этих типов (булева) не возможно использовать полный спектр значений (True не будет использовано) - Создать специальный класс исключений __NoException__
Отклонено, так как возможно будет вводить в заблуждение или будет по ошибке возбуждено пользователями и не является уникальным значением, как None, False и True. - Использовать Ellipsis (многоточие) для значения по умолчанию
Принято.
Ellipsis так же не является исключением, так что не может быть возбуждено.
Есть только Ellipsis, так что не будет неиспользованных значений
Информация об ошибке не выбрасывается, то что пользовательский код может провести трассировку цепи исключений при том, что по умолчанию это не происходит.
Детали языка
Для поддержки raise Exception from None, __context__ останется каким и был, а
__cause__ получит значение по умолчанию Ellipsis и будет заменено на None при использовании выражения
Exception from None.
Процедура вывода исключения по умолчанию будет работать так:
Поскольку значение по умолчанию для __cause__ теперь Ellipsis, a raise Exception form Cause лишь более короткая форма для:
форма | __context__ | __cause__ |
---|---|---|
raise | None | Ellipsis |
повторное raise | предыдущее исключение | Ellipsis |
повторное raise from None | ChainedException | предыдущее исключение | None | цепь исключений |
- если __cause__ равно Ellipsis, __context__ (если есть) будет выведен
- если __cause__ равно None, __context__ не будет выведен
- если __cause__ содержит любое другое значение, будет отображено __cause__
Поскольку значение по умолчанию для __cause__ теперь Ellipsis, a raise Exception form Cause лишь более короткая форма для:
_exc = NewException() _exc.__cause__ = Cause() raise _excEllipsis, как и None, теперь может быть "причиной":
raise Exception from Ellipsis
Комментариев нет:
Отправить комментарий