Я знаю, что если я хочу повторно вызвать исключение, я просто использую 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
. Или как мне провести рефакторинг кода, чтобы избежать этой проблемы?
plan_B
другую функцию, которая возвращаетсяTrue
в случае успеха илиFalse
исключения? Тогда внешнийexcept
блок мог быть простоif not try_plan_B(): raise
arg
и я бы попробовал вызвать,arg.plan_B()
который может вызватьAttributeError
из-заarg
непредоставления плана Bplan_B
вызывать исключенияОтветы:
Что касается Python 3, трассировка сохраняется в исключении, поэтому простой
raise e
(в большинстве случаев) будет делать правильные вещи:Созданная трассировка будет включать дополнительное уведомление, которое
SomeError
произошло во время обработкиAlsoFailsError
(из-заraise e
нахождения внутриexcept AlsoFailsError
). Это вводит в заблуждение, потому что на самом деле произошло обратное: мы столкнулисьAlsoFailsError
и справились с этим, пытаясь оправитьсяSomeError
. Чтобы получить трассировку, которая не включаетAlsoFailsError
, заменитеraise e
наraise e from None
.В Python 2 вы бы сохранили тип исключения, значение и трассировку в локальных переменных и использовали бы форму
raise
с тремя аргументами :источник
raise self.exc_info[1], None, self.exc_info[2]
послеself.exc_info = sys.exc_info()
-[1]
по какой-то причине поставить на первое местоraise t, None, tb
потеряет значение исключения и заставитraise
повторно создать его экземпляр из типа, давая вам менее конкретное (или просто неправильное) значение исключения. Например, еслиKeyError("some-key")
возникло исключение , оно просто повторно вызоветKeyError()
и пропустит точный отсутствующий ключ из трассировки.raise v.with_traceback(tb)
. (Ваш комментарий даже говорит об этом, за исключением того, что он предлагает повторно создать экземпляр значения.)sys.exc_info()
в локальной переменной имело смысл до Python 2.0 (выпущенного 13 лет назад), но сегодня граничит с нелепостью. Современный Python был бы практически бесполезен без сборщика циклов, поскольку каждая нетривиальная библиотека Python создает циклы без пауз и зависит от их правильной очистки.Даже если принятое решение правильное, хорошо указать на библиотеку Six, в которой есть решение Python 2 + 3, используя
six.reraise
.Итак, вы можете написать:
источник
six.raise_from
если хотите включить информацию, котораяplan_B()
также потерпела неудачу.six.raise_from
вы создаете новое исключение, которое связано с предыдущим, вы не поднимаете его повторно , поэтому обратная трассировка будет другой.reraise
вас создается впечатление, что это толькоsomething()
бросилSomeError
, еслиraise_from
вы также знаете, что это вызвалоplan_B()
выполнение, но бросаниеAlsoFailsError
. Так что это зависит от варианта использования. Думаюraise_from
, упростит отладкуСогласно предложению Дрю МакГоуэна , но заботясь об общем случае (где присутствует возвращаемое значение
s
), вот альтернатива ответу user4815162342 :источник
raise from
, поэтому трассировка стека также позволила бы мне потерпеть неудачу в плане B. Что, кстати, можно эмулировать в Python 2 .Python 3.5+ в любом случае прикрепляет информацию трассировки к ошибке, поэтому больше нет необходимости сохранять ее отдельно.
источник
except
. Но вы правы, когда я заменяюerr = e
, скажем,,raise AttributeError
вы сначала получаетеSyntaxError
трассировку стека, а затем -During handling of the above exception, another exception occurred:
иAttributeError
трассировку стека. Полезно знать, хотя, к сожалению, нельзя полагаться на установку версии 3.5+. PS: ff verstehen nicht-Deutsche vermutlich nicht;)