Как использовать отладчик pdb для Python приложений
Разбираемся с утилитой для отладки кода на Python — pdb. В материале рассмотрены приёмы работы с отладчиком pdb, а также его основные возможности.
pdb — это встроенный отладчик для Python, который, в отличие от print() , позволяет отлаживать программу в процессе её работы.
Отладка Python-кода с помощью print
Как уже говорилось ранее, кто-то используют print() для отображения информации, которая помогает понять, что происходит в коде. Кто-то использует логи для тех же целей, но давайте не путать использование логов на продакшене и случаи, когда их используют во время поиска багов в коде и после удаляют.
Но самая большая проблема в использовании print() – это необходимость вносить изменения в код и перезапускать приложение, чтобы увидеть изменения. Давайте разберёмся, почему отладчики эффективнее.
Команды Python-отладчика
Главная задача отладчика – предоставить возможность заглянуть в процесс выполнения кода. Так, например, можно просмотреть стек вызовов, узнать значения переменных, установить брейкпоинты или запустить выполнение кода построчно. Можно провести аналогию с тем, как автомеханик заглядывает под капот автомобиля и начинает перебирать деталь за деталью, пока не найдет причину поломки.
Если вы работаете с Python, то можете не только просматривать код во время отладки, но даже запускать код из командной строки или изменять значения переменных на лету.
Python есть встроенный отладчик под названием pdb. Это простая консольная утилита, которая обладает основной функциональностью для отладки кода. Но если вы ищете что-то более продвинутое, то стоит обратить внимание на ipdb – отладчик с функциональностью из IPython.
Проще всего вызвать отладчик pdb из кода, где вы работаете:
import pdb; pdb.set_trace()
Как только интерпретатор доберётся до этой строчки, запустится отладчик и в консоли будут доступны новые команды.
list()
Эта команда покажет часть кода, на выполнении которой сейчас находится интерпретатор. Можно передать два аргумента first и last для просмотра определённого участка кода. Если указать только first, то будет выведен код вокруг искомой строки.
up(p) и down(d)
Эти команды используются для передвижения по стеку вызовов. С их помощью можно отследить, откуда была вызвана текущая функция.
step() и next()
Другая пара не менее важных команд. С их помощью можно выполнять код построчно. Единственное различие между ними в том, что next() перейдёт к следующей строке вне зависимости от вызываемых функций, а step() перейдёт в вызванную функцию, если это возможно.
break()
Эта команда позволяет создавать брейкпоинты без внесений изменений в код. Ниже разберём этот этап более детально.
Краткий список команд отладчика pdb:
- args() — выводит аргументы функции;
- continue() или (cont) — продолжит выполнение до первого брейкпоинта или до завершения программы;
- help() — выводит список доступных команд или подсказки по определённой команде;
- jump() — перепрыгивает к выполнению указанной строчки кода;
- list() — выводит исходный код программы вокруг выбранной строки;
- expression() — выводит значение выражения;
- pp — выводит значение в «красивом» виде;
- quit или exit() — отменяет выполнение программы;
- return() — завершает выполнение текущей функции.
Продолжаем изучать Python-отладчик
Рассмотренный ранее способ работы с отладчиком требовал внесения изменения в код для вывода чего-нибудь или установки брейкпоинта. Но часто при работе с внешними библиотеками появляется необходимость в их отладке. Конечно, можно открыть исходный код библиотеки и вызвать pdb .
Но теперь есть возможность запускать приложение напрямую из отладчика без внесения изменения в код. Для этого воспользуемся следующей командой:
$ python3 -m pdb
Давайте разберём на примере. Есть простое приложение, которое отслеживает рабочее время. Для её работы используется библиотека requests , отвечающая за выполнение HTTP-запросов. Попробуем прервать выполнение во время запроса. Как это сделать? Запустим приложение через отладчик и установим брейкпоинт внутри библиотеки requests .
$ python3 -m pdb run.py > /Users/. /run.py(1)() -> from TimeLog.app import run_app (Pdb) b requests/sessions.py:555 Breakpoint 1 at /. /lib/python3.6/site-packages/requests/sessions.py:555 (Pdb) c
Как можно заметить, не нужно указывать полный путь до библиотеки. Можно указать относительную ссылку от sys.path . Таким же образом можно отлаживать и ваше приложение.
Теперь куда проще отлаживать код. Не надо вносить изменения в приложение или во внешние библиотеки.
Но что делать, если в приложении происходит много вызовов, а вам надо обработать только какой-то определённый? Можно точно указать условие, при выполнении которого отладчик будет прерывать выполнение приложения.
В данном примере прерывание произойдёт только в случае, если json будет иметь в себе ключ time_entry .
$ python3 -m pdb run.py > /Users/. /run.py(1)() -> from TimeLog.app import run_app (Pdb) b requests/sessions.py:555, json is not None and 'time_entry' in json Breakpoint 1 at /Users/. /lib/python3.6/site-packages/requests/sessions.py:555 (Pdb) c
Отладка кода Django
Если вы используете Django, то скорее всего знаете, что, если в настройках значение параметра DEBUG установлено как True , то для каждого исключения будет выводиться отдельная страница с указанием типа исключения, стек вызовов, локальные переменные и т.д.
Если вы хотите прокачать отладчик, то установите django-extensions и используйте команду runserver_plus для запуска сервера. Также можно указать пароль для доступа к отладке следующей командой:
WERKZEUG_DEBUG_PIN=1234 ./manage.py runserver_plus
Прим. перев. В Werkzeug, начиная с версии 0.11, появилась возможность доступа по паролю к отладчику. Это сделано для повышения безопасности при попытках несанкционированного доступа.
Если вы используете django-extensions, то получите страницу со всеми вызовами, кодом и окном отладчика.
Процесс отладки осуществляется с помощью WSGI библиотеки Werkzeug.
Существует множество способов отладки приложений, но специализированные инструменты могут дать вам огромное преимущество по сравнению с другими разработчиками и помогут сэкономить время и силы при поиске багов. Cреды разработки предлагают широкий выбор средств отладки, подробнее о них в нашей подборке лучших IDE и редакторов кода для Python.
Следите за новыми постами по любимым темам
Подпишитесь на интересующие вас теги, чтобы следить за новыми постами и быть в курсе событий.
Профилирование и отладка Python, отладка
В предыдущей статье мы закончили разговор о профилировании обзором событийных профайлеров.
Сегодня я предлагаю рассмотреть методы отладки программ.
- Введение и теория — зачем вообще нужно профилирование, различные подходы, инструменты и отличия между ними
- Ручное и статистическое профилирование — переходим к практике
- Событийное профилирование — инструменты и их применение
- Отладка — что делать, когда ничего не работает
Прежде, чем начинать разговор об отладке, нам нужно определиться, что же это такое. По традиции, обратимся к Википедии: «Отладка — обнаружение, локализация и устранение ошибок». Вообще, для успешной отладки программы, нам необходимо (но не всегда достаточно) две вещи: значение переменных в том месте программы, в котором произошла ошибка, а так же стектрейс: порядок вызова функций, вплоть до ошибочной. Полезно так же знать об окружении, в котором выполняется программа: наличие (или отсутствие) свободной памяти, системные ограничения (например, на количество файловых дескрипторов) и так далее, но это слегка выходит за пределы нашей статьи.
Классика жанра
Что делает начинающий программист, когда хочет «проникнуть» внутрь программы и изучить содержимое переменных (в нужных местах) и логику работы программы (вызовы функций, выполнение условий)? Он прибегает к самому распространённому, самому простому и самому «действенному» способу отладки: расставляет по всему коду «принты» (оператор print в Python 2.x и функция print() в Python 3.x). Но не только начинающие грешат этим: продвинутые разработчики частенько ленятся использовать специальные инструменты для отладки, надеясь быстренько, за пару минут, найти причину ошибки и всё исправить, и не замечая, что поиски ошибок растягиваются на часы и даже дни. Кстати, такой подход называют «журналированием».
Сюда же относится запись всей нужной для отладки программы информации в лог-файл. Иногда других вариантов нет, например, когда скрипт работает в продакшене и ошибки возникают эпизодически, или когда ошибка проявляется только после долгой работы программы (скажем, через две-три недели после запуска).
Но мы же пишем на Python, так почему бы не воспользоваться встроенными средствами отладки или инструментами, предлагаемыми сообществом? Например, вместо обычного логгирования в файл имеет смысл использовать Sentry, чтобы кроме сообщения об ошибке можно было отправить дополнительную информацию: стектрейс со всеми локальными переменными, любые другие переменные и вообще всё, что посчитаете нужным.
The Python Debugger
Python имеет встроенный отладчик: модуль pdb. В принципе, на этом можно было бы закончить статью, т.к. pdb — чрезвычайно мощный отладчик и всё остальное, по сути, всего лишь «украшательства» к нему. Но мы продолжим =)
Как можно заметить из названия, pdb многое взял от gdb (GNU Project debugger) — отладчика Си (и не только) программ. К слову, программы на Python можно отлаживать и с помощью gdb, просто это немножко сложнее и дольше, хотя тем, кто хочет углубиться в устройство языка, а так же тем, кто пишет сишные модули для питона без gdb никак не обойтись. Кроме того, gdb позволяет подключиться к уже работающей программе (через её pid) и заниматься её отладкой «прямо на месте».
Я сейчас не буду подробно описывать pdb, вот замечательная статья Дага Хеллманна (Doug Hellmann): pdb – Interactive Debugger на очень полезном ресурсе Python Module of the Week, а вот её хороший перевод на хабре: pdb – Интерактивный отладчик, выполненный пользователем xa4a.
IPython pdb
Предлагаю сразу поставить IPython и модуль ipdb для него:
➜ pip install ipython ipdb
IPython (и ipdb, как его часть) предлагает «более лучшую» консоль и, как следствие, более удобную отладку программ: подсветка кода, автодополнение, историю команд, динамическую интроспекцию любых объектов, магические функции, алиасы и многое другое. Полный список улучшений можно посмотреть в документации или прямо из консоли IPython, введя «?» в качестве команды. Всё это помогает при отладке и делает её простой и увлекательной.
Запустить отладку скрипта в ipdb можно несколькими способами:
➜ python -m ipdb script.py
➜ ipdb script.py
Эти команды откроют программу в отладчике, дальше можно делать всё, что угодно: ставить брейкпоинты, изучать её работу по шагам или просто запустить программу — отладчик автоматически остановится при возникновении неотловленного исключения.
Но обычно такой вариант чересчур изнурителен: пока доберёшься до нужного места всеми этими «next», «step», да и ставить точку останова («break») руками каждый раз утомительно. Гораздо удобнее в нужном месте программы вставить следующую строку:
import ipdb; ipdb.set_trace()
И тогда при исполнении этой строки выполнение программы приостановится и будет запущен отладчик, — дальше можно начинать углубляться в изучение программы. По сути, функция «set_trace» — это установка точки останова (breakpoint).
Примечание для эстетов
Любой, кому знакома аббревиатура PEP8, в этом месте может обвинить меня в использовании двух команд в одной строке через точку с запятой, однако такой подход вполне имеет право на жизнь. Во-первых, это временный код, который никогда не будет закоммичен в репозиторий (об этом позаботится специальный хук, который проверяет код на соответствие PEP8 перед коммитом и пушем, а так же автопроверка кода в текстовом редакторе). Во-вторых, так проще вставлять и удалять эту строку в код. Ну и в-третьих, как написано в PEP8: «A Foolish Consistency is the Hobgoblin of Little Minds».
Python Debugger имеет ещё один режим работы, который в некоторых случаях оказывается удобнее фукнции set_trace. Он называется «post mortem»: запуск отладчика с заданным трейсбеком:
try: some_code() except: import sys import ipdb tb = sys.exc_info()[2] ipdb.post_mortem(tb)
import sys import ipdb def run_debugger(type, value, tb): ipdb.pm() sys.excepthook = run_debugger some_code()
В случае возникновения любых неотлавливаемых исключений в функции «some_code» будет вызван отладчик в том месте программы, где произошло исключение.
debug
Интересной заменой функции «set_trace» является модуль debug, который просто импортирует библиотеки ipdb и see (удобная альтернатива функции dir) и запускает отладку. Единственным плюсом модуля является удобство использования, достаточно в любом месте программы вставить строку:
import debug
И при выполнении этой строки будет вызван отладчик «ipdb» и импортирован модуль «see».
ipdbplugin
Ещё одна интересная и, на этот раз, полезная библиотека: nose-ipdb. С её помощью можно автоматически запускать отладчик при ошибках (исключениях) или же просто при неверно отработанных тестах (я надеюсь, вы используете nose? =). Для запуска отладчика ipdb при ошибках, достаточно при запуске тестов добавить ключ «—ipdb»:
➜ nosetests --ipdb
А для запуска отладчика при некорректно завершившихся тестов нужно добавить ключ «—ipdb-failures»:
➜ nosetests --ipdb-failures
Конечно, можно ловить всё и сразу:
➜ nosetests --ipdb --ipdb-failures
Я каждый день использую этот модуль и просто не представляю себе жизни без него.
werkzeug
Потрясающий проект Армина Ронахера (Armin Ronacher), автора фреймворка Flask и вообще одного из крутейших программистов Python называется werkzeug и представляет собой сборник различных утилит для WSGI приложений. Одна из них — клёвый отладчик, который позволяет выводить удобный трейсбек ошибки, а так же запускать консоль Python в соответствующем месте трейсбека прямо на странице браузера:
Использовать его очень просто, достаточно обернуть приложение с помощью соответствующего middleware:
from werkzeug.debug import DebuggedApplication from myapp import app app = DebuggedApplication(app, evalex=True)
Говорят, что werkzeug умеет отлаживать даже Ajax-запросы, но, к сожалению, я сам лично никогда этого не делал. Буду рад любым комментариям на эту тему.
django-pdb
Ещё один хороший модуль, на этот раз для Django: django-pdb. Он позволяет запускать отладчик при наличии соответствующего GET-параметра в запросе (например: http://127.0.0.1:8000/app/view?ipdb) либо для всех запросов:
➜ python manage.py runserver --ipdb
Либо вызывать отладчик при возникновении исключений (режим «post-mortem»):
➜ python manage.py runserver --pm
POST_MORTEM = True
django-extensions
Но гораздо лучше в Django использовать модуль django-extensions, который добавляет очень полезную команду runserver_plus. С помощью этой батарейки можно подружить Django и Werkzeug (см. выше) и начать получать удовольствие от страниц с пятисотой ошибкой =)
Для использования всего этого чуда достаточно запустить девелоперский сервер с помощью команды runserver_plus:
➜ python manage.py runserver_plus
sentry
Отладка программы в девелоперском окружении это, конечно, удобно и хорошо, но самое сложное — локализовать проблему по багрепорту от живого пользователя. Иногда это бывает сложно. Несмотря на все преимущества Python, модуля «телепатии» не существует, и разработчик остаётся один на один со словами пользователя «ничего не работает. 11».
Проект Sentry позволяет сохранять каждую ошибку пользователя с текстом исключения, полным стектрейсом исключения и значениями всех локальных переменных в каждой из функций стектрейса, а так же много другой информации: окружение пользователя (браузер, ОС), все заголовки запроса и вообще всё, что пожелает разработчик.
Одинаковые ошибки группируются, таким образом можно наблюдать за «пульсом» проекта и чинить в первую очередь самые критичные места. Ещё один пример использования sentry — логгирование. Можно просто добавить в спорное место запись в лог сообщения с любыми интересующими разработчика переменными, и всё это окажется в sentry.
Но самый большой плюс в том, что всё это можно (и нужно!) использовать в продакшене.
PuDB
Ещё один интересный отладчик: PuDB представляет собой консольный дебагер с графическим интерфейсом:
Не буду много о нём писать (честно говоря, я сам активно им не пользовался), предлагаю прочитать короткую заметка на хабре: Удобный отладчик для Python/Django проектов от пользователя int22h или более полный обзор: Отладка Python/Django при помощи PuDB.
Winpdb
Standalone отладчик Python, на этот раз с полноценным графическим интерфейсом: Winpdb:
Его разработчики утверждают, что winpdb в 20 раз быстрее pdb, а так же поддерживает работу с тредами. Очень большой и подробный туториал можно найти на этой странице: code.google.com/p/winpdb/wiki/DebuggingTutorial.
IDE
Отдельного слова заслуживают универсальные «комбайны» программирования: IDE, которые позволяют не выходя из редактора запустить код, отпрофилировать его или запустить встроенный отладчик. Я, пожалуй, выделю несколько продуктов, предназначенных для разработки на Python: PyCharm, PyDev, Wing IDE и PTVS.
Лирическое отступление
К сожалению, лично я сколько не пытался, так и не смог пересилить себя и променять скорость, удивительную гибкость и удобство работы с консолью и любимым текстовым редактором (будь то vim, emacs или sublime text) на любую из вышеперечисленных IDE, однако мои коллеги успешно ими пользуются и поэтому я советую хотя бы дать этим замечательным продуктам шанс и попробовать их.
Если честно, я не вижу особого смысла рассматривать каждую из этих IDE, достаточно знать что они есть, что они успешно справляются со своими задачами и вы можете использовать встроенный отладчик прямо из редактора, — это действительно удобно, круто и здорово.
Спасибо всем, кто дочитал и прокомментировал.
Владимир Рудных,
Технический руководитель Календаря Mail.Ru.
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 для того чтобы получить справку по конкретной команде;
- q(uit), exit - выход из отладчика.
Общие команды
- l(ist) [ [,]] - печать исходного кода. Без передачи аргументов выводится +5 сверху и +5 снизу строк кода. Можно передать два аргумента "first" и "last" - номера строк диапазон которых надо выводить, если передать только один аргумент, то произойдет вывод +5 сверху и +5 снизу строк относительно указанного номера строки;
- p , pp - "print" и "pprint" соответственно;
- a(rgs) - выводит аргументы функции;
- whatis - выведет тип объекта;
- alias [ [ [, . ]]] - установить алиас на какую либо команду, например:
(Pdb) alias lv locals().keys()
(Pdb) alias gv pp globals().keys()Алисы, помимо команд отладчика и инструкций интерпретатора могут вызывать другие алиасы. Выполнение без аргументов отображает список установленных алиасов, а если передать имя алиаса в качестве аргумента, то выведится его содержимое.
Для работы с аргументами алиаса нужно использовать следующий формат % , а для обращения ко всем аргументам используйте %*, пример:
(Pdb) alias dirs !dir(%1)
dirs __name__
(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.
# Когда имя уникально можно делать и так:
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) - перепрыгнуть на указанную строку кода не выполняя код находящийся между текущей позицией и указанной. Исключение составляют циклы 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]
(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
(Pdb) condition 3 True != False
(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
(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.
pdb – Интерактивный отладчик
pdb является интерактивной средой отладки для программ на Python. Он включает возможности приостановки выполнения программы, просмотра значений переменных, построчного выполнения кода так, что вы можете понять, чем ваша программа на самом деле занимается, и найти логические ошибки.
Запуск отладчика
Для начала использования pdb необходимо указать интерпретатору, как и когда мы хотим увидеть отладчик. Существует несколько способов это сделать, в зависимости от условий запуска, а также требований к отладке.
Из командной строки
Самый очевидный метод использования отладчика — запускать его из командной строки, передавая вашу программу параметром так, чтобы он знал, что запускать.
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- class MyObj( object ):
- def __init__(self, num_loops):
- self.count = num_loops
- def go(self):
- for i in range(self.count):
- print i
- return
- if __name__ == '__main__' :
- MyObj(5).go()
При запуске отладчика из командной строки он загружает ваш исходник и останавливает выполнение на первом найденном выражении. В нашем случае он остановится перед выполнением определения класса MyObj в седьмой строке.
$ python -m pdb pdb_script.py
> . /pdb_script.py(7)()
-> class MyObj(object):
(Pdb)Note:
Обычно pdb включает полный путь к каждому модулю при выводе пути файла. Для соблюдения лаконичности примеров в выводе отладчика эти пути заменены на …Из интерпретатора
Многие разработчики работают с интерактивным интерпретатором на ранних стадиях разработки модулей, так как это позволяет им более итеративно экспериментировать без необходимости повторения цикла save/run/repeat, необходимого при создании самостоятельных скриптов. Для запуска отладчика из интерактивной сессии используйте run() или runeval().
$ python
Python 2.7 (r27:82508, Jul 3 2010, 21:12:11)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb_script
>>> import pdb
>>> pdb.run('pdb_script.MyObj(5).go()')
> (1)()
(Pdb)Аргумент у run() — это строковое выражение, которое может быть выполнено интерпретатором Python. Отладчик распарсит его и остановит выполнение как раз перед запуском первого выражения. Для навигации и управления выполнением вы можете использовать команды, описанные ниже.
Из вашей программы
Оба предыдущих примера основывались на том, что вы хотите запустить отладчик в самом начале вашей программы. Для более сложных процессов, в которых проблема возникает намного позже, во время выполнения, удобнее запускать отладчик изнутри программы используя set_trace().
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- import pdb
- class MyObj( object ):
- def __init__(self, num_loops):
- self.count = num_loops
- def go(self):
- for i in range(self.count):
- pdb.set_trace()
- print i
- return
- if __name__ == '__main__' :
- MyObj(5).go()
В 16-й строке тестовый скрипт вызывает отладчик.
$ python ./pdb_set_trace.py
> . /pdb_set_trace.py(17)go()
-> print i
(Pdb)set_trace() – это просто функция в Python, так что её можно вызывать в любой момент исполнения программы. Это позволяет попадать в режим отладки, в зависимости от условий, сложившихся внутри программы, например при обработке исключений, или в особой ветке какого-либо управляющего выражения
После сбоя
Отладка программы после её завершения называется отладкой post-mortem. pdb поддерживает такую отладку с помощью функций pm() и post_mortem().
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- class MyObj( object ):
- def __init__(self, num_loops):
- self.count = num_loops
- def go(self):
- for i in range(self.num_loops):
- print i
- return
Тут, на 13-й строке неправильное имя поля вызовет исключение AttributeError, это прекратит выполнение. pm() смотрит на интерактивный трейсбек и запускает отладчик в том фрейме стека, в котором произошло исключение.
$ python
Python 2.7 (r27:82508, Jul 3 2010, 21:12:11)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from pdb_post_mortem import MyObj
>>> MyObj(5).go()
Traceback (most recent call last):
File "", line 1, in
File "pdb_post_mortem.py", line 13, in go
for i in range(self.num_loops):
AttributeError: 'MyObj' object has no attribute 'num_loops'
>>> import pdb
>>> pdb.pm()
> . /pdb_post_mortem.py(13)go()
-> for i in range(self.num_loops):
(Pdb)Управление отладчиком
Взаимодействие с отладчиком сводится к небольшому языку команд, который позволяет вам перемещаться по стеку, просматривать и изменять значения переменных, управлять тем, как отладчик будет выполнять вашу программу. Отладчик использует readline для считывания команд. Ввод пустой строки повторяет предыдущую команду, кроме случаев просмотра исходника.
Навигация по стеку вызовов
В любой момент работы отладчика вы можете использовать where (или просто w) для уточнения, какая строка исполняется в данный момент, и где вы находитесь в стеке вызовов. В этом случае — модуль pdb_set_trace.py строка 17 в методе go().
$ python pdb_set_trace.py
> . /pdb_set_trace.py(17)go()
-> print i
(Pdb) where
. /pdb_set_trace.py(21)()
-> MyObj(5).go()
> . /pdb_set_trace.py(17)go()
-> print iЧтобы ознакомиться с контекстом текущего места, используйте list (l).
(Pdb) list
12 self.count = num_loops
13
14 def go(self):
15 for i in range(self.count):
16 pdb.set_trace()
17 -> print i
18 return
19
20 if __name__ == '__main__':
21 MyObj(5).go()
[EOF]
(Pdb)По умолчанию, выводится 11 строк вокруг текущей (5 выше и 5 ниже). При вызове list с единым аргументом, она выведет 11 строк, соседних с заданной, вместо текущей.
(Pdb) list 14
9 class MyObj(object):
10
11 def __init__(self, num_loops):
12 self.count = num_loops
13
14 def go(self):
15 for i in range(self.count):
16 pdb.set_trace()
17 -> print i
18 return
19Если передать list два аргумента, то они будут обработаны, как первая и последняя строки, которые будут выведены на экран.
(Pdb) list 5, 19
5 #
6
7 import pdb
8
9 class MyObj(object):
10
11 def __init__(self, num_loops):
12 self.count = num_loops
13
14 def go(self):
15 for i in range(self.count):
16 pdb.set_trace()
17 -> print i
18 return
19Перемещайтесь по стеку вызовов, используя up и down. up (коротко u) перемещает к более ранним вызовам в стеке. down (или d) переносит к более глубоким вызовам.
(Pdb) up
> . /pdb_set_trace.py(21)()
-> MyObj(5).go()(Pdb) down
> . /pdb_set_trace.py(17)go()
-> print iПри каждом перемещении по стеку, отладчик выводит текущее местонахождение в том же формате, что и where.
Просмотр переменных в стеке
С каджым фреймом стека связан набор переменных, включая значения, локальные для исполняемой функции и информацию о глобальном состоянии. pdb позволяет несколькимя способами просмотреть содержимое этих переменных.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- import pdb
- def recursive_function(n=5, output= 'to be printed' ):
- if n > 0:
- recursive_function(n-1)
- else :
- pdb.set_trace()
- print output
- return
- if __name__ == '__main__' :
- recursive_function()
Команда args (она же a) выводит все аргументы функции, активной в текущем фрейме. В этом примере также используется рекурсивная функция для демонстрации того, как выглядит глубокий стек в выводе where.
$ python pdb_function_arguments.py
> . /pdb_function_arguments.py(14)recursive_function()
-> return
(Pdb) where
. /pdb_function_arguments.py(17)()
-> recursive_function()
. /pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
. /pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
. /pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
. /pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
. /pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)
> . /pdb_function_arguments.py(14)recursive_function()
-> return(Pdb) args
n = 0
output = to be printed(Pdb) up
> . /pdb_function_arguments.py(11)recursive_function()
-> recursive_function(n-1)(Pdb) args
n = 1
output = to be printedКоманда p выполняет выражение, переданное аргументом и выводит результат. Вы также можете использовать выражение print, но оно передаётся интерпретатору Python для выполнения, вместо выполнения, как команда отладчика.
Аналогично, начиная выражение с ! вы сразу отправляете его в интерпретатор Python к исполнению. Эту возможность можно использовать для выполнения произвольных Python команд, в том числе изменение переменных. В этом примере значение output изменяется прежде, чем позволить отладчику продолжить исполнение программы. Следующий вызов, после set_trace() выводит output, отображая изменённое значение.
$ python pdb_function_arguments.py
> . /pdb_function_arguments.py(14)recursive_function()
-> print output(Pdb) !output
'to be printed'(Pdb) !output='changed value'
(Pdb) continue
changed valueДля более сложных значений, таких как вложенные или большие структуры данных, используйте pp (pretty print) для «красивого» вывода. Эта программа читает несколько строк текста из файла.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- import pdb
- with open( 'lorem.txt' , 'rt' ) as f:
- lines = f.readlines()
- pdb.set_trace()
Вывод строк при помощи p получается сложночитаемым, т.к. он переносится неестественно. pp использует pprint для форматирования значений перед выводом.
$ python pdb_pp.py
--Return--
> . /pdb_pp.py(12)()->None
-> pdb.set_trace()
(Pdb) p lines
['Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\n', 'egestas, enim
et consectetuer ullamcorper, lectus ligula rutrum leo, a\n', 'elementum elit tortor
eu quam.\n'](Pdb) pp lines
['Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\n',
'egestas, enim et consectetuer ullamcorper, lectus ligula rutrum leo, a\n',
'elementum elit tortor eu quam.\n']Шагание по программе
Кроме навигации вверх и вниз по стеку, пока программа приостановлена, вы также можете продолжить выполнение по шагам с места, где вызывается отладчик.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- import pdb
- def f(n):
- for i in range(n):
- j = i * n
- print i, j
- return
- if __name__ == '__main__' :
- pdb.set_trace()
- f(5)
Используйте step, чтобы выполнить текущую строку и остановиться перед следующей исполняемой точкой — или первым выражением внутри вызываемой функции, или следующей строкой в текущей функции.
$ python pdb_step.py
> /Users/dhellmann/Documents/PyMOTW/src.pdb/PyMOTW/pdb/pdb_step.py(17)()
-> f(5)Интерпретатор останавливается на вызове set_trace() и передаёт управление отладчику. В результате первого вызова step выполняется вызов f().
(Pdb) step
--Call--
> . /pdb_step.py(9)f()
-> def f(n):Ещё раз step, и текущей строкой становится первая в функции f() и начинает выполняться цикл.
(Pdb) step
> . /pdb_step.py(10)f()
-> for i in range(n):Перешагнув ещё раз, попадаем на первую строку внутри цикла, где определяется j.
(Pdb) step
> /Users/dhellmann/Documents/PyMOTW/src.pdb/PyMOTW/pdb/pdb_step.py(11)f()
-> j = i * n
(Pdb) p i
0Значение i — 0, поэтому после этого шага значение j тоже должно быть 0.
(Pdb) step
> /Users/dhellmann/Documents/PyMOTW/src.pdb/PyMOTW/pdb/pdb_step.py(12)f()
-> print i, jПерешагивать снова и снова может быть утомительно, если предстоит преодолеть много кода до места ошибки, или если функция повторно вызывается.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- import pdb
- def calc(i, n):
- j = i * n
- return j
- def f(n):
- for i in range(n):
- j = calc(i, n)
- print i, j
- return
- if __name__ == '__main__' :
- pdb.set_trace()
- f(5)
В этом примере нет никаких проблем с calc(), так что проход по всем её строкам, при каждом вызове в цикле в f(), только засоряет полезный вывод, отображая все строки calc() по мере их выполнения.
$ python pdb_next.py
> . /pdb_next.py(21)()
-> f(5)
(Pdb) step
--Call--
> . /pdb_next.py(13)f()
-> def f(n):(Pdb) step
> . /pdb_next.py(14)f()
-> for i in range(n):(Pdb) step
> . /pdb_next.py(15)f()
-> j = calc(i, n)(Pdb) step
--Call--
> . /pdb_next.py(9)calc()
-> def calc(i, n):(Pdb) step
> . /pdb_next.py(10)calc()
-> j = i * n(Pdb) step
> . /pdb_next.py(11)calc()
-> return j(Pdb) step
--Return--
> . /pdb_next.py(11)calc()->0
-> return j(Pdb) step
> . /pdb_next.py(16)f()
-> print i, jКоманда next похожа на step, но не входит в функции, вызываемые текущим выражением. В результате, после выполнения этой команды, отладчик переходит к следующему выражению текущей функции.
> . /pdb_next.py(14)f()
-> for i in range(n):
(Pdb) step
> . /pdb_next.py(15)f()
-> j = calc(i, n)(Pdb) next
> . /pdb_next.py(16)f()
-> print i, jКоманда until — как next, только она продолжает выполнение, пока оно не достигнет строки текущей функции, расположенной ниже (с номером выше, чем у текущей). Это значит, что, например, until может использоваться для выхода из циклов.
$ python pdb_next.py
> . /pdb_next.py(21)()
-> f(5)
(Pdb) step
--Call--
> . /pdb_next.py(13)f()
-> def f(n):(Pdb) step
> . /pdb_next.py(14)f()
-> for i in range(n):(Pdb) step
> . /pdb_next.py(15)f()
-> j = calc(i, n)(Pdb) next
> . /pdb_next.py(16)f()
-> print i, j(Pdb) until
0 0
1 5
2 10
3 15
4 20
> . /pdb_next.py(17)f()
-> returnДо выполнения until, текущая строка была 16 — последняя строка цикла. После запуска until, исполнение дошло до 17-й строки и цикл исчерпался.
return — другой способ пропустить части функции. Он продолжает исполнение до момента, когда должен выполниться возврат из текщуей функции, и приостанавливает программу. Это даёт время посмотреть на возвращаемое значение перед возвратом из функции.
$ python pdb_next.py
> . /pdb_next.py(21)()
-> f(5)
(Pdb) step
--Call--
> . /pdb_next.py(13)f()
-> def f(n):(Pdb) step
> . /pdb_next.py(14)f()
-> for i in range(n):(Pdb) return
0 0
1 5
2 10
3 15
4 20
--Return--
> . /pdb_next.py(17)f()->None
-> returnТочки остановки
Когда программы становятся ещё больше, даже использование next и until станет медленным и скучным. Вместо построчного перешагивания по программе, есть решение получше — позволить ей выполняться нормально до момента, когда она достигнет точки, в который вам необходимо прервать её. Вы можете использовать set_trace() для запуска отладчика, но это работает, только если вы хотите остановить программу в одном месте. Более удобное решение — это запускать всю программу в отладчике, но заранее указать ему, где останавливаться, используя брейкпоинты. Отладчик следит за выполнением программы, и, когда она достигает описанного брейкпоинтом места, останавливает выполнение перед обозначенной строкой.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- def calc(i, n):
- j = i * n
- print 'j =' , j
- if j > 0:
- print 'Positive!'
- return j
- def f(n):
- for i in range(n):
- print 'i =' , i
- j = calc(i, n)
- return
- if __name__ == '__main__' :
- f(5)
Команда break принимает несколько опций для установки брейкпоинтов. Вы можете указать номер строки, файл или функцию, где исполнение должно остановиться. Для установки брейкпоинта на конкретной строке текущего файла, используйте break lineno:
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break 11
Breakpoint 1 at . /pdb_break.py:11(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> . /pdb_break.py(11)calc()
-> print 'Positive!'(Pdb)
Команда continue сообщает отладчику о продолжении выполнения программы до следующего брейкпоинта. В этом случае, первая итерация цикла в f() пройдёт, и остановится в calc() на второй итерации.Брейкпоинты также могут быть установлены на первую строку функции, если указать её имя вместо номера строки. Следующий пример покажет, что произойдёт, если добавить брейкпоинт в функцию calc().
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at . /pdb_break.py:7(Pdb) continue
i = 0
> . /pdb_break.py(8)calc()
-> j = i * n(Pdb) where
. /pdb_break.py(21)()
-> f(5)
. /pdb_break.py(17)f()
-> j = calc(i, n)
> . /pdb_break.py(8)calc()
-> j = i * nДля указания брейкпоинта в другом файле, предварите номер строки, или название функции именем файла.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- from pdb_break import f
- f(5)
Тут устанавливаем брейпоинт на 11-ю строку pdb_break.py после запуска основной программы pdb_break_remote.py.
$ python -m pdb pdb_break_remote.py
> . /pdb_break_remote.py(7)()
-> from pdb_break import f
(Pdb) break pdb_break.py:11
Breakpoint 1 at . /pdb_break.py:11(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> . /pdb_break.py(11)calc()
-> print 'Positive!'Имя файла может быть полным путём к исходнику, или относительным путём к файлу, доступному в sys.path.
Для просмотра текущих установленных брейпоинтов, используйте break без аргументов. Вывод включает файл и номер строки каждого брейкпоинта, а также информацию о том, сколько раз на него попали.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break 11
Breakpoint 1 at . /pdb_break.py:11(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:11(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> . /pdb/pdb_break.py(11)calc()
-> print 'Positive!'(Pdb) continue
Positive!
i = 2
j = 10
> . /pdb_break.py(11)calc()
-> print 'Positive!'(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:11
breakpoint already hit 2 timesУправление брейкпоинтами
Каждому добавленному брейкпоинту присваивается числовой идентификатор. Эти идентификаторы используются для интерактивного включения, выключения и удаления брейкпоинтов.
Выключение брейкпоинта с помощью disable сообщает отладчику, что не нужно останавливаться по достижению этой строки. Брейпоинт запоминается, но игнорируется.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at . /pdb_break.py:7(Pdb) break 11
Breakpoint 2 at . /pdb_break.py:11(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:7
2 breakpoint keep yes at . /pdb_break.py:11(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep no at . /pdb_break.py:7
2 breakpoint keep yes at . /pdb_break.py:11(Pdb) continue
i = 0
j = 0
i = 1
j = 5
> . /pdb_break.py(11)calc()
-> print 'Positive!'Сессия отладки из следующего примера устанавливает в программе два брейкпоинта, потом один из них отключает. Программа выполняется, пока не достигнут оставшийся брейкпоинт, тогда другой брейкпоинт включается и исполнение продолжается.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at . /pdb_break.py:7(Pdb) break 16
Breakpoint 2 at . /pdb_break.py:16(Pdb) continue
> . /pdb_break.py(16)f()
-> print 'i =', i(Pdb) list
11 print 'Positive!'
12 return j
13
14 def f(n):
15 for i in range(n):
16 B-> print 'i =', i
17 j = calc(i, n)
18 return
19
20 if __name__ == '__main__':
21 f(5)(Pdb) continue
i = 0
j = 0
> . /pdb_break.py(16)f()
-> print 'i =', i(Pdb) list
11 print 'Positive!'
12 return j
13
14 def f(n):
15 for i in range(n):
16 B-> print 'i =', i
17 j = calc(i, n)
18 return
19
20 if __name__ == '__main__':
21 f(5)(Pdb) continue
i = 1
> . /pdb_break.py(8)calc()
-> j = i * n(Pdb) list
3 #
4 # Copyright (c) 2010 Doug Hellmann. All rights reserved.
5 #
6
7 B def calc(i, n):
8 -> j = i * n
9 print 'j =', j
10 if j > 0:
11 print 'Positive!'
12 return j
13Строки, перед которыми указано B в выводе list показывают места программы, где установлены брейкпоинты (строки 9 и 18).
Используйте clear для полного удаления брейкпоинта.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break calc
Breakpoint 1 at . /pdb_break.py:7(Pdb) break 11
Breakpoint 2 at . /pdb_break.py:11(Pdb) break 16
Breakpoint 3 at . /pdb_break.py:16(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:7
2 breakpoint keep yes at . /pdb_break.py:11
3 breakpoint keep yes at . /pdb_break.py:16(Pdb) clear 2
Deleted breakpoint 2(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:7
3 breakpoint keep yes at . /pdb_break.py:16Остальные брейпоинты сохранят свои идентификаторы и не перенумеровываются.
Временные брейкпоинты
Временные брейкпоинты автоматичски очищаются после первого попадания. Используя временный брейкпоинт, можно быстро достичь некоторой точки исполнения программы, как и с обычным брейкпоинтом, однако, т.к. он сразу же очищается, то не мешает дальнейшей работе, если этот кусок программы будеет выполняться повторно.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) tbreak 11
Breakpoint 1 at . /pdb_break.py:11(Pdb) continue
i = 0
j = 0
i = 1
j = 5
Deleted breakpoint 1
> . /pdb_break.py(11)calc()
-> print 'Positive!'(Pdb) continue
Positive!
i = 2
j = 10
Positive!
i = 3
j = 15
Positive!
i = 4
j = 20
Positive!
The program finished and will be restarted
> . /pdb_break.py(7)()
-> def calc(i, n):После того, как выполнение дошло до 11-й строки в первый раз, брейкпоинт убирается и программа работает без остановок до конца.
Брейкпоинты с условием
К брейпоинтам также могут быть применены правила так, что выполнение программы остановится только если условия выполняются. Использование условных брейпоинтов даёт возможность более тонкого управления остановками, чем ручное включение и выключение брейкпоинтов.
Условные брейкпоинты могут быть установлены одним из двух способов. Первый — указать условие в момент создания брейкпоинта используя break.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break 9, j>0
Breakpoint 1 at . /pdb_break.py:9(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:9
stop only if j>0(Pdb) continue
i = 0
j = 0
i = 1
> . /pdb_break.py(9)calc()
-> print 'j =', jАргумент условия должен быть выражением, использующим имена переменных, видимые в том фрейме стека, в котором брейкпоинт определён. Если выражение сводится к значению True, то выполнение программы останавливается на брейкпоинте.
Условие может также быть применено к существующему брейкпоинту, используя команду condition. Аргумент — id брейкпоинта и выражение.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break 9
Breakpoint 1 at . /pdb_break.py:9(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:9(Pdb) condition 1 j>0
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:9
stop only if j>0Игнорирование брейкпоинтов
Программы с большим количеством циклов или рекурсивных вызовов часто проще отлаживать «прокручивая» некоторые этапы выполнения программы, вместо просмотра каждого вызова и брейкпоинта. Команда ignore указывает отладчику пропустить некоторый брейкпоинт. При каждом прохождении через брейкпоинт, он уменьшает счётчик игнорирования. Когда он доходит до 0, брейкпоинт снова активируется.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break 17
Breakpoint 1 at . /pdb_break.py:17(Pdb) continue
i = 0
> . /pdb_break.py(17)f()
-> j = calc(i, n)(Pdb) next
j = 0
> . /pdb_break.py(15)f()
-> for i in range(n):(Pdb) ignore 1 2
Will ignore next 2 crossings of breakpoint 1.(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:17
ignore next 2 hits
breakpoint already hit 1 time(Pdb) continue
i = 1
j = 5
Positive!
i = 2
j = 10
Positive!
i = 3
> . /pdb_break.py(17)f()
-> j = calc(i, n)(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:17
breakpoint already hit 4 timesЯвная установка счётчика в ноль сразу же активирует брейпоинт.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break 17
Breakpoint 1 at . /pdb_break.py:17(Pdb) ignore 1 2
Will ignore next 2 crossings of breakpoint 1.(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:17
ignore next 2 hits(Pdb) ignore 1 0
Will stop next time breakpoint 1 is reached.(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_break.py:17Запуск действий по брейкпоиинту
В допонение к интерактивному режиму, pdb поддерживает простое скриптование. Используя команды, вы можете определить последовательность действий интерпретатора, включая выражения Python, которые будут выполнены при попадании в какой-либо брейкпоинт. После выполнения commands с аргументом-номером строки, приглашение отладчика сменится на (com). Введите команды по одной, и в конце введите end для сохранения скрипта и возврата к основному режиму отладчика.
$ python -m pdb pdb_break.py
> . /pdb_break.py(7)()
-> def calc(i, n):
(Pdb) break 9
Breakpoint 1 at . /pdb_break.py:9(Pdb) commands 1
(com) print 'debug i =', i
(com) print 'debug j =', j
(com) print 'debug n =', n
(com) end(Pdb) continue
i = 0
debug i = 0
debug j = 0
debug n = 5
> . /pdb_break.py(9)calc()
-> print 'j =', j(Pdb) continue
j = 0
i = 1
debug i = 1
debug j = 5
debug n = 5
> . /pdb_break.py(9)calc()
-> print 'j =', jЭта функция особенно полезна для отладки кода, который использует много структур данных, переменных, т.к. позволяет выводить все значения автоматически, вместо выполнения этого вручную на каждом брейкпоинте.
Управление ходом выполнения
Команда jump позволяет управлять выполнением программы на лету, не меняя исходного кода. Вы можете перескочить часть программы вперёд, избегая выполнения некоторого кода, или назад, для повторного его выполнения. Простая программа для генерации списка чисел.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- def f(n):
- result = []
- j = 0
- for i in range(n):
- j = i * n + j
- j += n
- result.append(j)
- return result
- if __name__ == '__main__' :
- print f(5)
При запуске без вмешательства, вывод — увеличивающаяся последовательность чисел, делящихся на 5.
Переход вперёд
Переход вперёд переносит исполнение в точку, далее текущей строки, не выполняя команды, расположенные до неё. Пропуская 13-ю строку в примере ниже, значение j не инкрементируется и все последующие значения, которые зависят от него, становятся немного меньше.
$ python -m pdb pdb_jump.py
> . /pdb_jump.py(7)()
-> def f(n):
(Pdb) break 12
Breakpoint 1 at . /pdb_jump.py:12(Pdb) continue
> . /pdb_jump.py(12)f()
-> j += n(Pdb) step
> . /pdb_jump.py(13)f()
-> result.append(j)(Pdb) continue
> . /pdb_jump.py(12)f()
-> j += n(Pdb) jump 13
> . /pdb_jump.py(13)f()
-> result.append(j)(Pdb) continue
[5, 10, 25, 45, 70]The program finished and will be restarted
> . /pdb_jump.py(7)()
-> def f(n):
(Pdb)Переход назад
Переходы могут также переносить выполнение программы к выражениям, которые уже были выполнены, для повторного запуска. Тут значение j инкрементируется лишний раз так, что в результате числа выходят больше, чем были бы иначе.
$ python -m pdb pdb_jump.py
> . /pdb_jump.py(7)()
-> def f(n):
(Pdb) break 13
Breakpoint 1 at . /pdb_jump.py:13(Pdb) continue
> . /pdb_jump.py(13)f()
-> result.append(j)(Pdb) jump 12
> . /pdb_jump.py(12)f()
-> j += n(Pdb) continue
> . /pdb_jump.py(13)f()
-> result.append(j)(Pdb) continue
[10, 20, 35, 55, 80]The program finished and will be restarted
> . /pdb_jump.py(7)()
-> def f(n):
(Pdb)Запрещённые переходы
Переходы «в» и «из» некоторых выражений, управляющих потоком вычислений являются опасными или неоднозначными, и, потому, запрещены отладчиком.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- def f(n):
- if n < 0:
- raise ValueError( 'Invalid n: %s' % n)
- result = []
- j = 0
- for i in range(n):
- j = i * n + j
- j += n
- result.append(j)
- return result
- if __name__ == '__main__' :
- try :
- print f(5)
- finally :
- print 'Always printed'
- try :
- print f(-5)
- except:
- print 'There was an error'
- else :
- print 'There was no error'
- print 'Last statement'
Вы можете перейти внутрь функции, но если вы это сделаете, то аргументы не будут определены и код вряд ли заработает.
$ python -m pdb pdb_no_jump.py
> . /pdb_no_jump.py(7)()
-> def f(n):
(Pdb) break 21
Breakpoint 1 at . /pdb_no_jump.py:21(Pdb) jump 8
> . /pdb_no_jump.py(8)()
-> if n < 0:(Pdb) p n
*** NameError: NameError("name 'n' is not defined",)Вы не можете переходить внутрь таких блоков, как циклы for или выражение try:except.
$ python -m pdb pdb_no_jump.py
> . /pdb_no_jump.py(7)()
-> def f(n):
(Pdb) break 21
Breakpoint 1 at . /pdb_no_jump.py:21(Pdb) continue
> . /pdb_no_jump.py(21)()
-> print f(5)(Pdb) jump 26
*** Jump failed: can't jump into the middle of a blockКод в блоке finally должен быть обязательно выполнен, поэтому вы не можете выпрыгнуть из него.
$ python -m pdb pdb_no_jump.py
> . /pdb_no_jump.py(7)()
-> def f(n):
(Pdb) break 23
Breakpoint 1 at . /pdb_no_jump.py:23(Pdb) continue
[5, 15, 30, 50, 75]
> . /pdb_no_jump.py(23)()
-> print 'Always printed'(Pdb) jump 25
*** Jump failed: can't jump into or out of a 'finally' blockИ основное ограничение — переходы ограничены нижним фреймом стека. Если вы перейдёте вверх по стеку для просмотра переменных, вы не сможете изменить ход выполнения в этом уровне.
$ python -m pdb pdb_no_jump.py
> . /pdb_no_jump.py(7)()
-> def f(n):
(Pdb) break 11
Breakpoint 1 at . /pdb_no_jump.py:11(Pdb) continue
> . /pdb_no_jump.py(11)f()
-> j = 0(Pdb) where
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/bdb.py(379)run()
-> exec cmd in globals, locals
(1)()
. /pdb_no_jump.py(21)()
-> print f(5)
> . /pdb_no_jump.py(11)f()
-> j = 0(Pdb) up
> . /pdb_no_jump.py(21)()
-> print f(5)(Pdb) jump 25
*** You can only jump within the bottom frameПерезапуск программы
Когда отладчик доходит до конца программы, он автоматически запускает её сначала. Вы также можете самостоятельно перезапустить её, не покидая режима отладки, и не теряя брейкпоинтов или других настроек.
- #!/usr/bin/env python
- # encoding: utf-8
- #
- # Copyright (c) 2010 Doug Hellmann. All rights reserved.
- #
- import sys
- def f():
- print 'Command line args:' , sys.argv
- return
- if __name__ == '__main__' :
- f()
Запуск вышеприведённой программы в отладчике выведет имя скрипта, так как других аргументов в командной строке передано не было.
$ python -m pdb pdb_run.py
> . /pdb_run.py(7)()
-> import sys
(Pdb) continueCommand line args: ['pdb_run.py']
The program finished and will be restarted
> . /pdb_run.py(7)()
-> import sysМожно перезапустить программу с помощью run. Аргументы run парсятся с помощью shlex, и передаются программе, как-будто они из командной строки, так что вы можете перезапускать вашу программу с разными параметрами.
(Pdb) run a b c "this is a long value"
Restarting pdb_run.py with arguments:
a b c this is a long value
> . /pdb_run.py(7)()
-> import sys(Pdb) continue
Command line args: ['pdb_run.py', 'a', 'b', 'c', 'this is a long value']
The program finished and will be restarted
> . /pdb_run.py(7)()
-> import sysrun также может быть использована в любом другом месте для перезапуска программы.
$ python -m pdb pdb_run.py
> . /pdb_run.py(7)()
-> import sys
(Pdb) break 10
Breakpoint 1 at . /pdb_run.py:10(Pdb) continue
> . /pdb_run.py(10)f()
-> print 'Command line args:', sys.argv(Pdb) run one two three
Restarting pdb_run.py with arguments:
one two three
> . /pdb_run.py(7)()
-> import sysНастройка отладчика с помощью алиасов
Есть способ избежать повторный набор сложных команд — определить алиас. Раскрытие алиасов применяется к первому слову каждой команды. Тело алиаса может содержать любую команду, допустимую интерпретатором отладчика, включая команды отладчика и выражения Python. В определениях алиасов допускается рекурсия, так что одни алиасы могут вызывать другие.
$ python -m pdb pdb_function_arguments.py
> . /pdb_function_arguments.py(7)()
-> import pdb
(Pdb) break 10
Breakpoint 1 at . /pdb_function_arguments.py:10(Pdb) continue
> . /pdb_function_arguments.py(10)recursive_function()
-> if n > 0:(Pdb) pp locals().keys()
['output', 'n'](Pdb) alias pl pp locals().keys()
Выполнение alias без аргументов отображет список определённых алиасов. С одним аргументом — именем алиаса, выводится его определение.
(Pdb) alias
pl = pp locals().keys()(Pdb) alias pl
pl = pp locals().keys()
(Pdb)К аргументам алиаса можно обращаться используя %n, где n — номер аргумента, начиная с 1. Для обращения ко всем аргументам, используйте %*.
$ python -m pdb pdb_function_arguments.py
> . /pdb_function_arguments.py(7)()
-> import pdb
(Pdb) alias ph !help(%1)(Pdb) ph locals
Help on built-in function locals in module __builtin__:locals(. )
locals() -> dictionaryUpdate and return a dictionary containing the current scope's local variables.
Удалите определение алиаса с помощью unalias.
(Pdb) ph locals
*** SyntaxError: invalid syntax (, line 1)Сохранение настроек конфигурации
Отладка программы требует много повторений; запуск кода, анализ вывода, исправление кода и ввода и снова запуск. pdb старается урезать количество повторений, необходимых для процесса отладки, чтобы позволить вам сконцентрироваться на вашем коде, а не на отладчике. Чтобы помочь уменьшить количество команд, отдаваемых отладчику, pdb позволяет сохранить конфигурацию, чтобы потом снова прочесть её при старте.
Сначала читается файл ~/.pdbrc, позволяя указать личные предпочтения для всех сессий отладки. После этого читается ./.pdbrc из теущего каталога так, что вы можете указывать настройки для конкретного проекта.
$ cat ~/.pdbrc
# Show python help
alias ph !help(%1)
# Overridden alias
alias redefined p 'home definition'$ cat .pdbrc
# Breakpoints
break 10
# Overridden alias
alias redefined p 'local definition'$ python -m pdb pdb_function_arguments.py
Breakpoint 1 at . /pdb_function_arguments.py:10
> . /pdb_function_arguments.py(7)()
-> import pdb
(Pdb) alias
ph = !help(%1)
redefined = p 'local definition'(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at . /pdb_function_arguments.py:10Любые команды настройки, которые могут быть введены в шелл отладчика, могут быть сохранены в одном из этих файлов, но не могут команды, управляющие выполнением (continue, jump, ит.д.). Исключение — run, тоесть вы можете устанавливать аргументы командной строки для сессий отладки в ./.pdbrc так, что они будут неизменны на протяжении последующих запусков.