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

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

Привет, @jeremy, не могли бы вы изменить принятый ответ на Кэтрин Девлин ответ? Это фантастический вопрос, и принятый в настоящее время ответ Флориана Бёша великолепен, но ответ Екатерины проще, и я думаю, что посетителям было бы лучше, если бы он был показан первым. (Я удалю этот комментарий через пару дней.)

tom 30.03.2021 16:16
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
237
1
85 808
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

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

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

Я только что проверил 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
>>>

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

Легкий, но часто именно то, что нужно

Casebash 15.02.2010 11:46

Не так полезно, запускается в глобальном масштабе. Не могу ковыряться в какой-то сбойной функции.

pixelpax 26.03.2016 23:37

как это можно сделать, чтобы он перешел в «отладку» только в случае ошибки.

MarioTheHedgehog 27.08.2020 17:53
Ответ принят как подходящий

Вы можете использовать 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)

первое решение дополнительно обсуждается на кулинарная книга python

dirkjot 28.06.2012 00:12

почему кто-то может предпочесть codepdb, если последний, кажется, расширяет первый?

K3---rnc 26.08.2014 01:49

У меня такой же вопрос? Почему вы предпочитаете code?

ARH 20.02.2015 02:06
Затем используйте sys.exc_info для извлечения трассировки и, наконец, вызовите pdb.post_mortem с этой трассировкой.. Вам не нужно передавать объект трассировки в pdb.post_mortem. От документы: Если обратная трассировка не указана, используется то исключение, которое в настоящее время обрабатывается (исключение должно обрабатываться, если должно использоваться значение по умолчанию).
Piotr Dobrogost 21.04.2015 11:13

@PiotrDobrogost Хорошее замечание. Я думаю, что более полезно знать, что вы можете передать объект tb, поскольку он лучше демонстрирует API. Приятно знать, что существуют оба варианта.

davidA 19.12.2016 02:35

Я помещаю эти фрагменты в контекстные менеджеры в свой файл util.py - pastebin.com/7rC8EsuC

loxaxs 16.05.2018 12:17

Это так же хорошо, как система перезапуска Common Lisp? Допустим, вы написали "5" + num, когда хотели написать "5" + str (num), возможно ли после выполнения post_mortem исправить вашу ошибку, а затем продолжить работу, как будто ничего не произошло?

aoeu256 16.09.2019 08:41

Относительно "почему вы предпочитаете код, а не pdb в командной строке?" - есть контексты, в которых интерпретатор запускается изнутри процесса, который не позволяет вам запускать отладчик как оболочку ... например. когда интерпретатор является просто частью более крупного приложения или некоторая структура управляет сложной средой, окружающей интерпретатор. В таких случаях имеет смысл сделать такой лаунчер-заглушку.

Stabledog 30.10.2019 21:45

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

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.

Это должен быть принятый ответ - он не требует какой-либо модификации существующего кода или обертывания всего в try-catch, что является просто уродливым ИМО.

cyphar 07.09.2015 18:01

Это выглядит великолепно, но обратите внимание, что некоторые фреймворки (например, flask) уже устанавливают sys.excepthook. У них часто есть свои собственные, даже лучшие подходы, такие как werkzeug, но см. Также python - Flask и sys \ .excepthook - qaru

nealmcb 06.11.2020 05:11
python -m pdb -c continue myscript.py

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

Вау, спасибо! Если бы теперь этого первоначального вызова PDB можно было бы избежать, все было бы идеально. Есть идеи?

flonk 04.04.2013 13:24

Спасибо за упоминание «введите 'c'» - я обычно вводил «r» (для «бега»), привык к этому с gdb; и когда вы вводите 'r' в pdb, программа действительно запускается, но НЕ останавливается (и не генерирует обратную трассировку) при ошибке; меня озадачили, пока я не прочитал это. Ваше здоровье!

sdaau 03.08.2013 00:58

Я попробовал ваш метод. Но, похоже, он не предлагает никакого реального контроля, такого как проверка переменных во время ошибки. Единственный вариант, который он предлагает, - это перезапустить выполнение.

Flame of udun 07.12.2014 05:44

Vineet, он запустит вас с включенным отладчиком, поэтому введите «cont», и он будет работать до тех пор, пока не будет обнаружена ошибка. Оттуда вы можете проверять переменные и т.д., как и в любом другом сеансе pdb.

Catherine Devlin 17.12.2014 19:41

Пожалуйста, OP, примите это как ответ. Это самый полезный, и я потратил 5 минут на чтение других, пока не выбрал этот ... это должно быть первым!

jhegedus 17.09.2015 22:46

То же самое работает и с ipdb; и, конечно же, после скрипта можно добавить аргументы!

tutuDajuju 14.12.2015 13:03

Это ответ, который я искал. Принятый ответ требует добавления кода только для запуска сеанса отладки, что очень неудобно.

Moises Silva 27.01.2016 20:09

ОП, пожалуйста, не принимайте это как ответ. Например, это не работает для KeyError. Он снова запускает сеанс отладчика в начале сценария, а не в строке с ошибкой. Выбранный ответ (тип, значение, tb = sys.exc_info () + traceback.print_exc () + pdb.post_mortem (tb)) является правильным.

David Nogueira 04.08.2016 13:44

@tutuDajuju, как заставить это работать с ipdb? Я попытался просто заменить pdb в приведенной выше команде на ipdb, но это дало мне ошибку, в которой говорилось, что «-c не существует».

Vivek Subramanian 07.03.2017 21:23

@vivek кажется, что это был исправлено недавно, но еще не выпущен для pypi.

tutuDajuju 07.03.2017 22:25

Это не работает с Python 2.7. docs.python.org/3/library/pdb.html: «Новое в версии 3.2: pdb.py теперь принимает параметр -c, который выполняет команды»

eqzx 10.04.2017 21:29

@eqzx Остальная часть ответа остается в силе. Нам нужно только нажать «c» после выполнения ... в остальном все в порядке!

AruniRC 19.12.2017 18:27

и сделайте pip install pdbpp перед использованием выше, для лучшего пользовательского интерфейса.

Sławomir Lenart 12.02.2019 19:04

@eqzx одно из решений, позволяющих избежать наличия флага -c в Python 2.7, - это (echo c && cat) | python -pdb script.py.

Ben Usman 21.03.2019 22:26
stackoverflow.com/a/242531/237059 более точно отвечает на конкретный запрос OP - они не спрашивают «как я могу постоянно оборачивать все приложение в отладчике?»
Stabledog 21.11.2019 21:30

очень удобно: псевдоним pd = 'python3 -m pdb -c continue'

Yordan Grigorov 16.12.2020 00:22

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

import pdb ; pdb.set_trace()

Подробнее: Запустить отладчик python в любой строке

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

blueFast 18.02.2016 20:53

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

И это единственный разумный ответ на этот вопрос.

Michael 18.03.2016 11:47

Документировано на ipython.readthedocs.io/en/stable/interactive/…

matthiash 03.03.2017 17:38

Обратите внимание, что - как также отмечено в связанных документах @matthiash - %debug позволяет открыть отладчик после, обнаружив ошибку. Я часто предпочитаю это %pdb. (Компромисс заключается в простом вводе q каждый раз, когда вы не хотите отлаживать ошибку, по сравнению с вводом %debug каждый раз, когда вы делать хотите отладить ошибку.)

Braham Snyder 30.10.2017 00:12

Также обратите внимание, что установка c.InteractiveShell.pdb = True в ipython_config.py автоматически включает %pdb для каждой сессии.

Braham Snyder 30.10.2017 00:17

Это правильный ответ.

Omid Ataollahi 29.01.2021 09:07

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

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

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

python -m pdb -c c <script name>

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

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.

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

python -m mymodule

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

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

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

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

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

Jean Paul 23.01.2019 13:23

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

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

In [1]: %pdb
Automatic pdb calling has been turned ON

ipdb имеет хороший диспетчер контекста для достижения этого поведения, которое делает намерение семантически более ясным:

from ipdb import launch_ipdb_on_exception

with launch_ipdb_on_exception():
    ...

Другие вопросы по теме