РЕДАКТИРОВАТЬ: предыдущая версия не содержала цитат или объяснений.
Я предлагаю обратиться к PEP 3134, в котором говорится в разделе Мотивация сильный>:
Иногда обработчику исключений может быть полезно намеренно повторно вызвать исключение, чтобы предоставить дополнительную информацию или преобразовать исключение в другой тип. Атрибут __cause__
предоставляет явный способ записать прямую причину исключения.
Когда Exception
возникает с атрибутом __cause__
, сообщение трассировки принимает форму:
Traceback (most recent call last):
<CAUSE TRACEBACK>
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
<MAIN TRACEBACK>
Насколько я понимаю, это именно то, чего вы пытаетесь достичь; четко указать, что причина ошибки не в вашем модуле, а в другом месте. Если вместо этого вы пытаетесь опустить информацию в трассировку, как предлагает ваше edit, то остальная часть этого ответа не принесет вам никакой пользы.
Замечание о синтаксисе:
Атрибут __cause__
в объектах исключения всегда инициализируется значением None. Он задается новой формой оператора «рейз»:
raise EXCEPTION from CAUSE
что эквивалентно:
exc = EXCEPTION
exc.__cause__ = CAUSE
raise exc
так что минимальный пример будет примерно таким:
def function():
int("fail")
def check_raise(function):
try:
function()
except Exception as original_error:
err = RuntimeError("An exception was raised.")
raise err from original_error
check_raise(function)
что дает такое сообщение об ошибке:
Traceback (most recent call last):
File "/PATH/test.py", line 7, in check_raise
function()
File "/PATH/test.py", line 3, in function
int("fail")
ValueError: invalid literal for int() with base 10: 'fail'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/PATH/test.py", line 12, in <module>
check_raise(function)
File "/PATH/test.py", line 10, in check_raise
raise err from original_error
RuntimeError: An exception was raised.
Однако первая строка причины - это инструкция в блоке try
строки check_raise
:
File "/PATH/test.py", line 7, in check_raise
function()
поэтому перед повышением err
может быть (а может и не быть) желательно удалить самый внешний фрейм трассировки из original_error
:
except Exception as original_error:
err = RuntimeError("An exception was raised.")
original_error.__traceback__ = original_error.__traceback__.tb_next
raise err from original_error
Таким образом, единственная строка в трассировке, которая, по-видимому, исходит от check_raise
, - это самый последний оператор raise
, который нельзя пропустить с чистым кодом Python, хотя в зависимости от того, насколько информативным является сообщение, вы можете очень четко указать, что ваш модуль не был причиной проблема:
err = RuntimeError("""{0.__qualname__} encountered an error during call to {1.__module__}.{1.__name__}
the traceback for the error is shown above.""".format(function,check_raise))
Преимущество создания такого исключения заключается в том, что исходное сообщение Traceback не теряется при возникновении новой ошибки, а это означает, что может быть вызвана очень сложная серия исключений, и python по-прежнему будет правильно отображать всю соответствующую информацию:
def check_raise(function):
try:
function()
except Exception as original_error:
err = RuntimeError("""{0.__qualname__} encountered an error during call to {1.__module__}.{1.__name__}
the traceback for the error is shown above.""".format(function,check_raise))
original_error.__traceback__ = original_error.__traceback__.tb_next
raise err from original_error
def test_chain():
check_raise(test)
def test():
raise ValueError
check_raise(test_chain)
дает мне следующее сообщение об ошибке:
Traceback (most recent call last):
File "/Users/Tadhg/Documents/test.py", line 16, in test
raise ValueError
ValueError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/Tadhg/Documents/test.py", line 13, in test_chain
check_raise(test)
File "/Users/Tadhg/Documents/test.py", line 10, in check_raise
raise err from original_error
RuntimeError: test encountered an error during call to __main__.check_raise
the traceback for the error is shown above.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/Tadhg/Documents/test.py", line 18, in <module>
check_raise(test_chain)
File "/Users/Tadhg/Documents/test.py", line 10, in check_raise
raise err from original_error
RuntimeError: test_chain encountered an error during call to __main__.check_raise
the traceback for the error is shown above.
Да, он длинный, но он значительно информативнее, чем:
Traceback (most recent call last):
File "/Users/Tadhg/Documents/test.py", line 18, in <module>
check_raise(test_chain)
RuntimeError: An exception was raised.
не говоря уже о том, что исходная ошибка все еще может использоваться, даже если программа не заканчивается:
import traceback
def check_raise(function):
...
def fail():
raise ValueError
try:
check_raise(fail)
except RuntimeError as e:
cause = e.__cause__
print("check_raise failed because of this error:")
traceback.print_exception(type(cause), cause, cause.__traceback__)
print("and the program continues...")
person
Tadhg McDonald-Jensen
schedule
24.01.2016
filter
, чтобы превратить предупреждение в ошибку, а затем просто позвонитеwarnings.warn
, поможет ли это? - person Dimitris Fasarakis Hilliard   schedule 09.12.2015source
этоraise
это, я просто не уверен, будет ли он делать это так, как вы хотите (и, глядя на это, я думаю, что это не так). - person Dimitris Fasarakis Hilliard   schedule 09.12.2015logging.Logger
, который изменяет трассировку (или создает измененную копию), прежде чем передавать его своему классуsuper
. Кроме того, вы можете заменить функциюsys.excepthook
, которая распечатывает неперехваченные исключения и проделывают те же трюки. Если вы на самом деле не используете трассировку для чего-либо, кроме печати, это может достаточно хорошо соответствовать вашим потребностям. - person eestrada   schedule 16.01.2016raise
, но имеет другую логику. Я думаю, что это, вероятно, бесполезное упражнение из-за того, насколько оно похоже на простое создание оператора, который потребует перекомпиляции python, что определенно НЕ является маршрутом, который я иду с пакетом, который хочу распространить. stackoverflow.com/ questions / 214881 / - person Joel   schedule 16.01.2016catch
исходное исключение и выбросить новое в соответствующий момент? В качестве альтернативы, можете ли вы создать tb в соответствующей точке независимо от того, возникает ли ошибка, и передать его в стек вызовов, чтобы при необходимости выбросить позже? - person Kyle Strand   schedule 21.01.2016