Python. Отладка при помощи pdb

pdb - это интерактивный отладчик для Python, с помощью которого можно перемещаться по коду во время запуска вашей программы, смотреть и изменять значения переменных, построчно навигироваться по коду (в том числе углубляться во вложенности кода), назначать брейкпоинты и все прочие операции присущие отладчику.
Когда нет под рукой PuDB и не имеется возможности его установить, либо плывет графика в urwid я использую pdb.
Отладчик pdb внешне похож на gdb.
Использование отладчика
Запускаем отладчик так:
python -m pdb script.py python -m pdb manage.py runserver 8000
Либо в коде можно указать место откуда надо запускать отладчик:
import pdb; pdb.set_trace()
А post-mortem запускается так:
import pdb; pdb.pm()
Для тех кто не знает что такое post-mortem, то это режим в котором отладчик стартует сразу после необработанного исключения.
Также в коде можно вызывать скрипты через pdb.run/pdb.runcall, но приведенные выше типы запусков более популярные.
Список команд отладчика
- h(elp) - отладчик выведет список допустимых команд. Выполните help <command> для того чтобы получить справку по конкретной команде;
- q(uit), exit - выход из отладчика.
Общие команды
- l(ist) [<first> [,<last>]] - печать исходного кода. Без передачи аргументов выводится +5 сверху и +5 снизу строк кода. Можно передать два аргумента "first" и "last" - номера строк диапазон которых надо выводить, если передать только один аргумент, то произойдет вывод +5 сверху и +5 снизу строк относительно указанного номера строки;
- p <expression>, pp <expression> - "print" и "pprint" соответственно;
- a(rgs) - выводит аргументы функции;
- whatis <arg> - выведет тип объекта;
- alias [<name> [<command> [<parameter>, <parameter> ...]]] - установить алиас на какую либо команду, например:
(Pdb) alias lv locals().keys()
(Pdb) alias gv pp globals().keys()
Алисы, помимо команд отладчика и инструкций интерпретатора могут вызывать другие алиасы. Выполнение без аргументов отображает список установленных алиасов, а если передать имя алиаса в качестве аргумента, то выведится его содержимое.
Для работы с аргументами алиаса нужно использовать следующий формат %<номер_аргумента>, а для обращения ко всем аргументам используйте %*, пример:
(Pdb) alias dirs !dir(%1)
dirs __name__
- unalias <name> - удалить определенный алиас;
run [<args>...] - перезагружает скрипт с задаными параметрами, пример:
(Pdb) run runserver 8001
Restarting ./manage.py with arguments:
runserver 8001
> /home/adw0rd/work/project/manage.py(2)()
-> import os (Pdb) c
Validating models...
0 errors found
Django version 1.4.1, using settings 'project.settings'
Development server is running at
Quit the server with CONTROL-C.
Тем самым вы не выходите из сессии, и если у вас есть к примеру брейкпоинты, то вы их не потеряте. Также есть встроенный алиас restart, который запускает run без аргументов;! - префикс для работы напрямую с интерпретатором, например вызов функций или обращение к переменным, всё что следует после префикса будет отправлено интерпретатору на выполнение, пример:
# Когда имя уникально можно делать и так:
test_var = 42
# Но когда оно совпадает с именем команды, надо так:
!step = 42 (Pdb) step = 42
> /home/adw0rd/work/project/manage.py(3)()
-> import sys
(Pdb) step
> /home/adw0rd/work/project/manage.py(5)()
-> if __name__ == "__main__": (Pdb) !step = 42
(Pdb) !step
42
Навигация по коду
- w(here) - выводит информаию о позиции в которой сейчас находитесь;
- s(tep) - "step into", перейти во внутрь вызова объекта, если это возможно, иначе перейти к следующей строке кода;
- n(ext) - "step over" (перешагнуть), перейти к следующей строке кода;
- unt(il) - перейти к следующей строке кода, но гарантировано чтобы номер строки был больше чем текущий. Пример: если вы находитесь в конце тела цикла, но это не последняя итерация, то вас не отправит в начало тела цикла, а выполнится весь цикл и отладчик встанет на следующей строке после цикла, в отличии от next;
- r(eturn) - завершить ("выйти из") текущую функцию;
- u(p) - подняться на один стек-фрейм вверх;
- d(own) - опуститься на один стек-фрейм вниз;
- j(ump) <lineno> - перепрыгнуть на указанную строку кода не выполняя код находящийся между текущей позицией и указанной. Исключение составляют циклы for и код в блоке finally (т.к. должен быть обязательно выполнен). Также, вы можете перепрыгивать только внутри текущего фрейма (т.е. нижнего фрейма).
Точки останова
Далее просто "брейкопоинты", потому что мне привычнее их так называть.
- При использовании листинга (команда "list") строки с брейкпоинтами помечаются префиксом "B":
(Pdb) b
Num Type Disp Enb Where
1 breakpoint keep yes at /home/adw0rd/work/project/manage.py:5
(Pdb) list
1 #!/usr/bin/env python
2 -> import os
3 import sys
4
5 B if __name__ == "__main__":
6 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
7 from django.core.management import execute_from_command_line
8 execute_from_command_line(sys.argv)
[EOF]
- b(reak) ([<file>:]<lineno> | <function>) [<condition>] - установка и листинг брейкпоинтов. Аргументы позволяют указать файл и номер строки или функцию, где исполнение кода должно остановиться, пример:
(Pdb) b 5
Breakpoint 1 at /home/adw0rd/work/project/manage.py:5
(Pdb) b project/settings.py:10
Breakpoint 2 at /home/adw0rd/work/project/project/settings.py:10
(Pdb) b my_function
Breakpoint 3 at /home/adw0rd/work/project/manage.py:15
Для просмотра текущих установленных брейпоинтов, используйте команду break без аргументов:
(Pdb) b
Num Type Disp Enb Where
1 breakpoint keep yes at /home/adw0rd/work/project/manage.py:5
2 breakpoint keep yes at /home/adw0rd/work/project/project/settings.py:10
3 breakpoint keep yes at /home/adw0rd/work/project/manage.py:15
Отдельно стоит рассмотреть брейкпоинты с условием, брейкпоинт сработает если только условие будет верно, пример:
# Брейкпоинт выполнится только если переменная "some_var" будет больше 42
(Pdb) b my_function, some_var > 42
Breakpoint 4 at /home/adw0rd/work/project/manage.py:15
- condition <bpnumber> <str_condition> - добавляет к существующему брейкпоинту условие, пример:
(Pdb) condition 3 True != False
- tbreak - временный брекпоинт, после использования удаляется, имеет одинаковый синтаксис с break;
- ignore <bpnumber> <count> - игнорировать count-раз определенный брейкпоинт. Если передать count равный нулю, то игнорирование сбросится;
- c(ont(inue)) - продолжить (до первого брейкпоинта или до завершения работы программы);
disable <bpnumber> [<bpnumber> ...] и enable <bpnumber> [<bpnumber> ...] - первый деактивирует брейкпоинт, но НЕ удаляет его из списка брейкпоинтов. А второй снова активирует брейкпоинт, пример:
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../script.py:2
2 breakpoint keep yes at .../script.py:12 (Pdb) disable 1
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep no at .../script.py:2
2 breakpoint keep yes at .../script.py:12 (Pdb) enable 1
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../script.py:2
2 breakpoint keep yes at .../script.py:12
- cl(ear) (<filename>:<lineno> | [<bpnumber>...]) - удаляет брейкпоинт(ы). Если передать номер брейкопинта то удалится только он, если ничего не пердавать - удалятся все брейкпоинты. Если передать путь и номер строки, то удалятся все брейкпоинты установленные в указанном месте;
commands [<bpnumber>] - написание дополнительных действий для брейкпоинта, например вывод локальных переменных и т.п. Пример:
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at .../script.py:2 (Pdb) commands 1
(com) p "DEBUG"
(com) pp locals()
(com) end (Pdb) continue
# Тут идёт печать locals()
Для удаления необходимо снова запустить commands, но с пустым телом:
(Pdb) commands 1
(com) end
Сохранение настроек конфигурации
Для настроек pdb необходимо создать файл .pdbrc в корне домашней директории, либо в корне проекта. При этом совпадающие инструкции находящиеся в корне проекта переопределят тех кто находятся в корне домашней директории.
Пример файла ~/.pdbrc:
# Алиас, который позволяет указывать несколько объектов для dir() # конечно он нужен только для примера... alias dirs pp [dir(arg) for arg in "%*".split()]
Пример файла ~/work/project/.pdbrc:
# Пример переопределения алиаса: alias dirs pp "OVERRIDDEN!"
Интеграция с Emacs
Во многих IDE есть поддержка pdb, не исключение и Emacs, достаточно запустить так:
M+x pdb
И вам предстанет консоль pdb в одной панели, а в другой реалтайм-листинг кода, почти PuDB, но всеравно менее удобнее.
Что ещё почитать?
Страница официальной документации The Python Debugger.
Старенькую статью Using pdb, the Python Debugger (Django Debugging Series, Part 3) и видео:
И статью на хабре pdb – Интерактивный отладчик.

Комментарии
лес зеленый и густой...непролазный и глухой. Ничего не понял :)
Мне всеравно кажется что python проще для понимания чем php :-)
Оставьте свой комментарий