Автоматический запуск Python-отладчика при ошибке

217

Это вопрос, который меня интересовал уже довольно давно, но я так и не нашел подходящего решения. Если я запускаю скрипт и сталкиваюсь, скажем, с IndexError, python печатает строку, местоположение и краткое описание ошибки и завершает работу. Можно ли автоматически запускать pdb при возникновении ошибки? Я не против наличия дополнительного оператора import в верхней части файла или нескольких дополнительных строк кода.

Jeremy
источник
12
Рассматривали ли вы изменение принятого ответа?
Joost

Ответы:

127

Вы можете использовать traceback.print_exc для печати трассировки исключений. Затем используйте sys.exc_info для извлечения трассировки и, наконец, вызовите pdb.post_mortem с этой трассировкой

import pdb, traceback, sys

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        extype, value, tb = sys.exc_info()
        traceback.print_exc()
        pdb.post_mortem(tb)

Если вы хотите запустить интерактивную командную строку с code.interact, используя локали фрейма, где возникло исключение, вы можете сделать

import traceback, sys, code

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        type, value, tb = sys.exc_info()
        traceback.print_exc()
        last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
        frame = last_frame().tb_frame
        ns = dict(frame.f_globals)
        ns.update(frame.f_locals)
        code.interact(local=ns)
Флориан Бош
источник
первое решение обсуждается далее в кулинарной книге питона
dirkjot
3
почему кто -то предпочитает codeболее , pdbпоскольку последний , кажется, расширить на прежний?
K3 --- RNC
У меня есть тот же вопрос? Почему вы предпочитаете code?
ARH
2
Затем используйте, sys.exc_infoчтобы извлечь трассировку и, наконец, вызвать pdb.post_mortemэту трассировку . Вам не нужно передавать объект трассировки pdb.post_mortem. Из документов : Если трассировка не задана, используется исключение, которое обрабатывается в настоящее время (исключение должно быть обработано, если используется значение по умолчанию).
Петр Доброгост
2
@PiotrDobrogost Хороший вопрос. Я думаю, что более полезно знать, что вы можете передать объект tb, так как он лучше демонстрирует API. Приятно знать, что существуют оба варианта.
ДавидА
456
python -m pdb -c continue myscript.py

Если вы не предоставите -c continueфлаг, то вам нужно будет ввести 'c' (для продолжения), когда начнется выполнение. Затем он дойдет до точки ошибки и даст вам контроль там. Как уже упоминалось в eqzx , этот флаг является новым дополнением в Python 3.2, поэтому ввод 'c' необходим для более ранних версий Python (см. Https://docs.python.org/3/library/pdb.html ).

Екатерина Девлин
источник
5
Спасибо, что упомянули « enter» c »- я обычно использовал для ввода« r »(для« run »), привык к этому от gdb; и когда вы вводите 'r' pdb, программа действительно запускается, но НЕ останавливается (и не генерирует обратную трассировку) при ошибке; меня озадачило, пока я не прочитал это. Ура!
sdaau
3
Vineet, он запустит вас с включенным отладчиком, поэтому введите «продолжение», и он будет работать, пока не возникнет ошибка. Оттуда вы можете просматривать переменные и т. Д., Как и в любой другой сессии pdb.
Кэтрин Девлин
40
Пожалуйста, ОП, примите это как ответ. Это самое полезное, и я потратил 5 минут на чтение остальных, пока не попал в этот ... это должно быть первым!
Джегедус
4
Это также работает с ipdb; и, конечно, аргументы могут быть добавлены после сценария!
tutuDajuju
20
Это не работает с Python 2.7. docs.python.org/3/library/pdb.html : «Новое в версии 3.2: теперь pdb.py принимает опцию -c, которая выполняет команды»
eqzx
68

Используйте следующий модуль:

import sys

def info(type, value, tb):
    if hasattr(sys, 'ps1') or not sys.stderr.isatty():
    # we are in interactive mode or we don't have a tty-like
    # device, so we call the default hook
        sys.__excepthook__(type, value, tb)
    else:
        import traceback, pdb
        # we are NOT in interactive mode, print the exception...
        traceback.print_exception(type, value, tb)
        print
        # ...then start the debugger in post-mortem mode.
        # pdb.pm() # deprecated
        pdb.post_mortem(tb) # more "modern"

sys.excepthook = info

Назовите это debug(или как вам нравится) и поместите его где-нибудь в вашем пути к Python.

Теперь, в начале вашего сценария, просто добавьте import debug.

tzot
источник
2
Это должен быть принятый ответ - он не требует какой-либо модификации существующего кода или оборачивания всего, try-catchчто является просто уродливым ИМО.
cyphar
Я люблю этот ответ довольно часто, но предпочитаю pudbбольше pdb. Продолжайте возвращаться к копированию и вставке, что наверняка говорит о неупорядоченности в моей жизни.
Конюшня
47

Ipython имеет команду для переключения этого поведения: % pdb . Он делает именно то, что вы описали, может быть, даже немного больше (дает вам более информативные обратные следы с подсветкой синтаксиса и дополнением кода). Это определенно стоит попробовать!

Latanius
источник
3
И это единственный разумный ответ на это.
Майкл
4
Задокументировано на ipython.readthedocs.io/en/stable/interactive/…
matthiash
4
Обратите внимание, что - как также отмечено в связанных документах @matthiash - %debugпозволяет открыть отладчик после возникновения ошибки. Я часто предпочитаю это %pdb. (Компромисс будет только набирать qкаждый раз , когда вы не хотите , чтобы отладить vs. опечатка %debugкаждый раз , когда вы делаете хотите отладить ошибку.)
Брэм Снайдера
1
Также обратите внимание, что установка c.InteractiveShell.pdb = Trueв свою очередь ipython_config.pyвключается %pdbавтоматически для каждого сеанса.
Брахам Снайдер
33

Это не отладчик, но, вероятно, так же полезен (?)

Я знаю, что слышал, как Гвидо упоминал об этом в своей речи.

Я только что проверил python -?, И если вы используете команду -i, вы можете взаимодействовать там, где остановился ваш скрипт.

Итак, учитывая этот сценарий:

testlist = [1,2,3,4,5, 0]

prev_i = None
for i in testlist:
    if not prev_i:
        prev_i = i
    else:
        result = prev_i/i

Вы можете получить этот вывод!

PS D:\> python -i debugtest.py
Traceback (most recent call last):
  File "debugtest.py", line 10, in <module>
    result = prev_i/i
ZeroDivisionError: integer division or modulo by zero
>>>
>>>
>>> prev_i
1
>>> i
0
>>>

Честно говоря, я не использовал это, но я должен быть, кажется очень полезным.

monkut
источник
Легкий, но часто именно то, что нужно
Casebash
8
Не так полезно, запускается в глобальном масштабе. Не могу покопаться в какой-либо функции, потерпевшей крах.
pixelpax
21

IPython делает это простым в командной строке:

python myscript.py arg1 arg2

можно переписать на

ipython --pdb myscript.py -- arg1 arg2

Или, аналогично, при вызове модуля:

python -m mymodule arg1 arg2

можно переписать на

ipython --pdb -m mymodule -- arg1 arg2

Обратите внимание -- чтобы IPython не считывал аргументы скрипта как свои собственные.

Это также имеет преимущество вызова расширенного отладчика IPython (ipdb) вместо pdb.

wodow
источник
10

Если вы используете ipython, после запуска типа%pdb

In [1]: %pdb
Automatic pdb calling has been turned ON
Willemoes
источник
Юпитер например
Blaztix
6

Если вы используете среду IPython, вы можете просто использовать% debug, и оболочка вернет вас к вызывающей проблемы среде со средой ipdb для проверок и т. Д. Другой вариант, как указано выше, - это использовать iPython magic% pdb, который эффективно тот же самый.

Jehandad
источник
Обратите внимание , что если произошла ошибка в функции модуля, вы можете перемещаться по рамах с upи downкомандами , чтобы вернуться к строке кода , который сгенерировал ошибку.
Жан Поль
4

Вы можете поместить эту строку в свой код:

import pdb ; pdb.set_trace()

Больше информации: Запустите отладчик Python в любой строке

Рори
источник
8
Это останавливает код и запускает отладчик в строке, где вы поместили эту команду, а не в строку, где произошло исключение
blueFast
3

Чтобы запустить его без необходимости вводить c в начале, используйте:

python -m pdb -c c <script name>

Pdb имеет свои собственные аргументы командной строки: -cc выполнит команду c (ontinue) в начале выполнения, и программа будет работать непрерывно до появления ошибки.

Златко Каракаш
источник
3

python -m pdb script.py в python2.7 нажмите продолжить, чтобы запустить, и это приведет к ошибке и прекратит работу для отладки.

Анна
источник
1

Если вы используете модуль:

python -m mymodule

И теперь вы хотите ввести, pdbкогда возникает исключение, сделайте это:

PYTHONPATH="." python -m pdb -c c mymodule/__main__.py

(или продли свой PYTHONPATH). Это PYTHONPATHнеобходимо для того, чтобы модуль был найден в пути, поскольку вы сейчас запускаете pdbмодуль.

blueFast
источник
0

Поместите точку останова в конструктор верхнего класса исключений в иерархии, и в большинстве случаев вы увидите, где возникла ошибка.

Установка точки останова означает, что вы хотите, чтобы она означала: вы можете использовать IDE, или pdb.set_trace, или как угодно

влад-Ardelean
источник