В Python, как попасть в отладчик в блоке исключения и получить доступ к экземпляру исключения?

Я пытаюсь сделать что-то похожее на следующее:

try:
    1/0
except ZeroDivisionError as e:
    import ipdb; ipdb.set_trace()

Когда я попадаю в отладчик, я бы хотел, чтобы экземпляр исключения e находился в моей локальной области. Однако, если я запустил этот скрипт, я обнаружил, что это не так:

Kurts-MacBook-Pro-2:Scratch kurtpeek$ python debug_exception.py
--Return--
None
> /Users/kurtpeek/Documents/Scratch/debug_exception.py(4)<module>()
      2         1/0
      3 except ZeroDivisionError as e:
----> 4         import ipdb; ipdb.set_trace()

ipdb> dir()
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__return__', '__spec__', 'ipdb']
ipdb> e
*** NameError: name 'e' is not defined

Почему e не определено? В настоящее время я использую инструкции print чтобы узнать атрибуты e, но я считаю, что это должно быть возможно сделать в интерактивном режиме.

Ответ 1

ipdb.set_trace() не запускает IPDB сразу. Он запускает следующее событие трассировки, которое в вашем случае - это когда функция вот-вот вернется.

Python 3 удаляет переменную e в конце блока except, чтобы разбить контрольные циклы отслеживания. К сожалению, для вас это происходит до того, как IPDB может запускаться.

Одним из хакерских решений, которые вы могли бы использовать, было бы добавить еще одну строку после set_trace, поэтому IPDB запускает событие 'line':

try:
    1/0
except ZeroDivisionError as e:
    import ipdb
    ipdb.set_trace()
    workaround = True

Другим вариантом было бы использовать посмертную отладку, которая не должна ждать события трассировки:

try:
    1/0
except ZeroDivisionError as e:
    import ipdb
    ipdb.post_mortem()

Однако посмертная отладка имеет ряд важных отличий от обычной отладки. Он поместит вас в (как правило, мертвый) стек стека, где возникло исключение, а не в стек стека, где post_mortem вызов post_mortem. В вашем примере это один и тот же кадр, но обычно этого не будет. Наличие доступа к фрейму стека, в котором было создано исключение, довольно хорошо, и вы все же можете перейти к кадру, в котором было обнаружено исключение (но не более того, из-за необычной системы трассировки Python), но это все еще большое различие.

Кроме того, вы не можете перейти в посмертный режим. При попытке запустить next или step выйдет из отладки.

Ответ 2

Фактически вы можете использовать post_mortem для доступа к контексту post_mortem

import ipdb; ipdb.post_mortem()

ipdb> e
ZeroDivisionError('division by zero',)