Как повторно создать исключение в вложенных блоках try/except?
Я знаю, что если я хочу повторно создать исключение, я просто использую raise
без аргументов в соответствующем блоке except
. Но учитывая вложенное выражение типа
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e # I'd like to raise the SomeError as if plan_B()
# didn't raise the AlsoFailsError
как я могу повторно поднять SomeError
, не нарушая трассировку стека? Только raise
в этом случае повторно поднимет более поздний AlsoFailsError
. Или как я могу реорганизовать свой код, чтобы избежать этой проблемы?
Ответы
Ответ 1
Вы можете сохранить тип исключения, значение и обратную трассировку в локальных переменных и использовать форму с тремя аргументами raise
:
try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
raise t, v, tb
В Python 3 трассировка сохраняется в исключении, поэтому raise e
сделает (в основном) правильную вещь:
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e
Единственная проблема, связанная с вышеизложенным, заключается в том, что он произведет слегка вводящую в заблуждение трассировку, которая сообщает вам, что SomeError
произошел во время обработки AlsoFailsError
(из-за raise e
внутри except AlsoFailsError
), где на самом деле почти полная противоположность произошло - мы обработали AlsoFailsError
, пытаясь восстановиться после SomeError
. Чтобы отключить это поведение и получить трассировку, которая никогда не упоминает AlsoFailsError
, замените raise e
на raise e from None
.
Ответ 2
Даже если принятое решение является правильным, было бы хорошо указать на библиотеку Six, которая имеет решение Python 2 + 3, используя six.reraise
.
шесть. ререйз (exc_type, exc_value, exc_traceback = нет)
Возбудить исключение, возможно, с другой трассировкой. [...]
Итак, вы можете написать:
import six
try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
six.reraise(t, v, tb)
Ответ 3
В соответствии с предложением Дрю МакГоуэн, но заботясь об общем случае (где присутствует возвращаемое значение s
), здесь альтернатива user4815162342 answer:
try:
s = something()
except SomeError as e:
def wrapped_plan_B():
try:
return False, plan_B()
except:
return True, None
failed, s = wrapped_plan_B()
if failed:
raise
Ответ 4
Python 3. 5+ в любом случае присоединяет информацию трассировки к ошибке, поэтому больше нет необходимости сохранять ее отдельно.
>>> def f():
... try:
... raise SyntaxError
... except Exception as e:
... err = e
... try:
... raise AttributeError
... except Exception as e1:
... raise err from None
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in f
File "<stdin>", line 3, in f
SyntaxError: None
>>>