7 октября 2012 г. Debug Django PuDB Python pdb

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 http://127.0.0.1:8001/
    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, но всеравно менее удобнее.

img

Что ещё почитать?

Страница официальной документации The Python Debugger.

Старенькую статью Using pdb, the Python Debugger (Django Debugging Series, Part 3) и видео:

И статью на хабре pdb – Интерактивный отладчик.

UPD: Python DEBUGGING Fundamentals.

Комментарии

Алик Камилевич 9 октября 2012 г. 18:36

лес зеленый и густой...непролазный и глухой. Ничего не понял :)

Мне всеравно кажется что python проще для понимания чем php :-)

Оставьте свой комментарий

Markdown