Подтвердить что ты не робот

Как повторно создать исключение в вложенных блоках 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. Или как я могу реорганизовать свой код, чтобы избежать этой проблемы?

4b9b3361

Ответ 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
>>>