Python "поднять от" использования
В чем разница между raise
а также raise from
в питоне?
try:
raise ValueError
except Exception as e:
raise IndexError
который дает
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError
IndexError
а также
try:
raise ValueError
except Exception as e:
raise IndexError from e
который дает
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError from e
IndexError
4 ответа
Разница в том, что когда вы используете from
, __cause__
атрибут установлен, и в сообщении указывается, что исключение было вызвано напрямую. Если вы опустите from
Тогда нет __cause__
установлен, но __context__
Атрибут также может быть установлен, и трассировка затем показывает контекст, как во время обработки чего-то еще произошло.
Настройка __context__
произойдет, если вы использовали raise
в обработчике исключений; если вы использовали raise
нигде нет __context__
установлен либо.
Если __cause__
установлен, __suppress_context__ = True
флаг также установлен на исключение; когда __suppress_context__
установлен в True
, __context__
игнорируется при печати трассировки.
При вызове из обработчика исключений, когда вы не хотите показывать контекст (не хотите, чтобы во время обработки другого сообщения об исключении произошло), используйте raise ... from None
установить __suppress_context__
в True
,
Другими словами, Python устанавливает контекст для исключений, чтобы вы могли проанализировать, где возникло исключение, позволяя вам увидеть, было ли заменено другое исключение. Вы также можете добавить причину в исключение, сделав трассировку явной для другого исключения (используйте другую формулировку), и контекст игнорируется (но все же может подвергаться самоанализу при отладке). С помощью raise ... from None
позволяет подавить контекст печати.
Увидеть raise
документация заявления:
from
Предложение используется для цепочки исключений: если дано, второе выражение должно быть другим классом или экземпляром исключения, который затем будет присоединен к возбужденному исключению как__cause__
атрибут (который доступен для записи). Если возникшее исключение не обработано, оба исключения будут напечатаны:>>> try: ... print(1 / 0) ... except Exception as exc: ... raise RuntimeError("Something bad happened") from exc ... Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: int division or modulo by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError: Something bad happened
Подобный механизм работает неявно, если исключение вызывается внутри обработчика исключения: > предыдущее исключение затем присоединяется как новое исключение
__context__
атрибут:>>> try: ... print(1 / 0) ... except: ... raise RuntimeError("Something bad happened") ... Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: int division or modulo by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError: Something bad happened
Также см. Документацию " Встроенные исключения" для получения подробной информации о контексте и сведениях о причинах, прикрепленных к исключениям.
PEP 3134, цепочка исключений и встроенная обратная трассировка представили цепочку исключений ( неявная цепочка с явным
raise EXCEPTION
или неявный подъем, и явно связанный с явным
raise EXCEPTION from CAUSE
). Вот соответствующие параграфы, чтобы понять их использование:
Мотивация
Во время обработки одного исключения (исключения A) может возникнуть другое исключение (исключение B). В современном Python (версия 2.4), если это происходит, исключение B распространяется наружу, а исключение A теряется. Для отладки проблемы полезно знать об обоих исключениях. Атрибут сохраняет эту информацию автоматически.
Иногда обработчику исключений может быть полезно намеренно повторно вызвать исключение либо для предоставления дополнительной информации, либо для преобразования исключения в другой тип. Атрибут предоставляет явный способ записи непосредственной причины исключения.
[…]
Неявная цепочка исключений
Вот пример, иллюстрирующий атрибут:
def compute(a, b): try: a/b except Exception, exc: log(exc) def log(exc): file = open('logfile.txt') # oops, forgot the 'w' print >>file, exc file.close()
Вызов
compute(0, 0)
вызывает а. Функция перехватывает это исключение и вызываетlog(exc)
, ноlog()
Функция также вызывает исключение при попытке записи в файл, который не был открыт для записи.В современном Python вызывающая сторона
compute()
получает брошенный . Потерян. С предлагаемым изменением экземпляр имеет дополнительный атрибут, который сохраняетZeroDivisionError
.[…]
Явная цепочка исключений
Атрибут объектов исключений всегда инициализируется
None
. Он устанавливается новой формойraise
утверждение:raise EXCEPTION from CAUSE
что эквивалентно:
exc = EXCEPTION exc.__cause__ = CAUSE raise exc
В следующем примере база данных предоставляет реализации для нескольких различных типов хранилищ, одним из которых является файловое хранилище. Разработчик базы данных хочет, чтобы ошибки распространялись как объекты, чтобы клиенту не приходилось знать подробности, относящиеся к хранилищу, но при этом он не хотел терять основную информацию об ошибке.
class DatabaseError(Exception): pass class FileDatabase(Database): def __init__(self, filename): try: self.file = open(filename) except IOError, exc: raise DatabaseError('failed to open') from exc
Если вызов к
open()
вызывает исключение, о проблеме будет сообщено какDatabaseError
, с атрибутом, раскрывающимIOError
как первопричина.Расширенная отчетность
Обработчик исключений по умолчанию будет изменен, чтобы сообщать о связанных исключениях. Цепочка исключений проходится по атрибутам и с приоритетом. В соответствии с хронологическим порядком трассировки самое последнее возникшее исключение отображается последним; то есть отображение начинается с описания самого внутреннего исключения и повторяет цепочку до самого внешнего исключения. Трассировки отформатированы как обычно, с одной из строк:
Вышеупомянутое исключение было прямой причиной следующего исключения:
или же
Во время обработки вышеупомянутого исключения произошло другое исключение:
между обратными трассировками, в зависимости от того, связаны ли они
__context__
соответственно. Вот набросок процедуры:def print_chain(exc): if exc.__cause__: print_chain(exc.__cause__) print '\nThe above exception was the direct cause...' elif exc.__context__: print_chain(exc.__context__) print '\nDuring handling of the above exception, ...' print_exc(exc)
[…]
PEP 415, Реализовать подавление контекста с помощью атрибутов исключений, затем было введено подавление контекстов исключений (с явным
raise EXCEPTION from None
). Вот соответствующий абзац, чтобы понять его использование:
Предложение
Новый атрибут на
BaseException
, , будут введены. Всякий раз, когда установлено, будет установлено значение . В частности, синтаксис будет установлен наTrue
. Код печати исключения проверит этот атрибут, чтобы определить, будут ли напечатаны контекст и причина.__cause__
вернется к своей первоначальной цели и ценностям.Существует приоритет для
__suppress_context__
сprint_line_and_file
атрибут исключения.Обобщить,
raise exc from cause
будет эквивалентно:exc.__cause__ = cause raise exc
куда
exc.__cause__ = cause
неявно устанавливаетexc.__suppress_context__
.
Таким образом, в PEP 415 набросок процедуры, приведенный в PEP 3134, становится следующим:
def print_chain(exc):
if exc.__cause__:
print_chain(exc.__cause__)
print '\nThe above exception was the direct cause...'
elif exc.__context__ and not exc.__suppress_context__:
print_chain(exc.__context__)
print '\nDuring handling of the above exception, ...'
print_exc(exc)
Самый короткий ответ. PEP-3134 говорит сам за себя.raise Exception from e
устанавливает поле нового исключения.
Более длинный ответ от того же PEP:
-
__context__
поле будет неявно установлено исходной ошибкой внутриexcept:
блокировать, если не сказано не с__suppress_context__ = True
. -
__cause__
точно так же, как контекст, но должен быть установлен явно с помощьюfrom
синтаксис -
traceback
всегда будет цепочка, когда вы звонитеraise
внутриexcept
блокировать. Вы можете избавиться от трассировки, а) проглотив исключениеexcept: pass
или возиться сsys.exc_info()
напрямую.
Длинный ответ
import traceback
import sys
class CustomError(Exception):
def __init__(self):
super().__init__("custom")
def print_exception(func):
print(f"\n\n\nEXECURTING FUNCTION '{func.__name__}' \n")
try:
func()
except Exception as e:
"Here is result of our actions:"
print(f"\tException type: '{type(e)}'")
print(f"\tException message: '{e}'")
print(f"\tException context: '{e.__context__}'")
print(f"\tContext type: '{type(e.__context__)}'")
print(f"\tException cause: '{e.__cause__}'")
print(f"\tCause type: '{type(e.__cause__)}'")
print("\nTRACEBACKSTART>>>")
traceback.print_exc()
print("<<<TRACEBACKEND")
def original_error_emitter():
x = {}
print(x.does_not_exist)
def vanilla_catch_swallow():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
pass
def vanilla_catch_reraise():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise e
def catch_replace():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise CustomError()
def catch_replace_with_from():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise CustomError() from e
def catch_reset_trace():
saw_an_error = False
try:
original_error_emitter()
except Exception as e:
saw_an_error = True
if saw_an_error:
raise CustomError()
print("Note: This will print nothing")
print_exception(vanilla_catch_swallow)
print("Note: This will print AttributeError and 1 stack trace")
print_exception(vanilla_catch_reraise)
print("Note: This will print CustomError with no context but 2 stack traces")
print_exception(catch_replace)
print("Note: This will print CustomError with AttributeError context and 2 stack traces")
print_exception(catch_replace_with_from)
print("Note: This will brake traceback chain")
print_exception(catch_reset_trace)
Приведет к следующему результату:
Note: This will print nothing
EXECURTING FUNCTION 'vanilla_catch_swallow'
Note: This will print AttributeError and 1 stack trace
EXECURTING FUNCTION 'vanilla_catch_reraise'
Exception type: '<class 'AttributeError'>'
Exception message: ''dict' object has no attribute 'does_not_exist''
Exception context: 'None'
Context type: '<class 'NoneType'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 41, in vanilla_catch_reraise
raise e
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 39, in vanilla_catch_reraise
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
<<<TRACEBACKEND
Note: This will print CustomError with no context but 2 stack traces
EXECURTING FUNCTION 'catch_replace'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: ''dict' object has no attribute 'does_not_exist''
Context type: '<class 'AttributeError'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 46, in catch_replace
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 48, in catch_replace
raise CustomError()
CustomError: custom
<<<TRACEBACKEND
Note: This will print CustomError with AttributeError context and 2 stack traces
EXECURTING FUNCTION 'catch_replace_with_from'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: ''dict' object has no attribute 'does_not_exist''
Context type: '<class 'AttributeError'>'
Exception cause: ''dict' object has no attribute 'does_not_exist''
Cause type: '<class 'AttributeError'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 53, in catch_replace_with_from
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 55, in catch_replace_with_from
raise CustomError() from e
CustomError: custom
<<<TRACEBACKEND
Note: This will brake traceback chain
EXECURTING FUNCTION 'catch_reset_trace'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: 'None'
Context type: '<class 'NoneType'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 64, in catch_reset_trace
raise CustomError()
CustomError: custom
<<<TRACEBACKEND
У меня была аналогичная ошибка с использованием pylint. Если причины нет, чтобы подавить эту ошибку, просто выполните следующие действия.
raise Exception from None
или просто
raise KeyError() from NameError()