- PVSM.RU - https://www.pvsm.ru -
В предыдущей статье [1] мы закончили разговор о профилировании обзором событийных профайлеров.
Сегодня я предлагаю рассмотреть методы отладки программ.
Прежде, чем начинать разговор об отладке, нам нужно определиться, что же это такое. По традиции, обратимся к Википедии [4]: «Отладка — обнаружение, локализация и устранение ошибок». Вообще, для успешной отладки программы, нам необходимо (но не всегда достаточно) две вещи: значение переменных в том месте программы, в котором произошла ошибка, а так же стектрейс: порядок вызова функций, вплоть до ошибочной. Полезно так же знать об окружении, в котором выполняется программа: наличие (или отсутствие) свободной памяти, системные ограничения (например, на количество файловых дескрипторов) и так далее, но это слегка выходит за пределы нашей статьи.
Что делает начинающий программист, когда хочет «проникнуть» внутрь программы и изучить содержимое переменных (в нужных местах) и логику работы программы (вызовы функций, выполнение условий)? Он прибегает к самому распространённому, самому простому и самому «действенному» способу отладки: расставляет по всему коду «принты» (оператор print в Python 2.x и функция print() в Python 3.x). Но не только начинающие грешат этим: продвинутые разработчики частенько ленятся использовать специальные инструменты для отладки, надеясь быстренько, за пару минут, найти причину ошибки и всё исправить, и не замечая, что поиски ошибок растягиваются на часы и даже дни. Кстати, такой подход называют «журналированием».
Сюда же относится запись всей нужной для отладки программы информации в лог-файл. Иногда других вариантов нет, например, когда скрипт работает в продакшене и ошибки возникают эпизодически, или когда ошибка проявляется только после долгой работы программы (скажем, через две-три недели после запуска).
Но мы же пишем на Python, так почему бы не воспользоваться встроенными средствами отладки или инструментами, предлагаемыми сообществом? Например, вместо обычного логгирования в файл имеет смысл использовать Sentry [5], чтобы кроме сообщения об ошибке можно было отправить дополнительную информацию: стектрейс со всеми локальными переменными, любые другие переменные и вообще всё, что посчитаете нужным.
Python имеет встроенный отладчик: модуль pdb [6]. В принципе, на этом можно было бы закончить статью, т.к. pdb — чрезвычайно мощный отладчик и всё остальное, по сути, всего лишь «украшательства» к нему. Но мы продолжим =)
Как можно заметить из названия, pdb многое взял от gdb (GNU Project debugger) — отладчика Си (и не только) программ. К слову, программы на Python можно отлаживать и с помощью gdb, просто это немножко сложнее и дольше, хотя тем, кто хочет углубиться в устройство языка, а так же тем, кто пишет сишные модули для питона без gdb никак не обойтись. Кроме того, gdb позволяет подключиться к уже работающей программе (через её pid) и заниматься её отладкой «прямо на месте».
Я сейчас не буду подробно описывать pdb, вот замечательная статья Дага Хеллманна (Doug Hellmann): pdb – Interactive Debugger [7] на очень полезном ресурсе Python Module of the Week [8], а вот её хороший перевод на хабре: pdb – Интерактивный отладчик [9], выполненный пользователем xa4a [10].
Предлагаю сразу поставить IPython [11] и модуль ipdb [12] для него:
➜ 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).
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» будет вызван отладчик в том месте программы, где произошло исключение.
Интересной заменой функции «set_trace» является модуль debug [13], который просто импортирует библиотеки ipdb [12] и see [14] (удобная альтернатива функции dir) и запускает отладку. Единственным плюсом модуля является удобство использования, достаточно в любом месте программы вставить строку:
import debug
И при выполнении этой строки будет вызван отладчик «ipdb» и импортирован модуль «see».
Ещё одна интересная и, на этот раз, полезная библиотека: nose-ipdb [15]. С её помощью можно автоматически запускать отладчик при ошибках (исключениях) или же просто при неверно отработанных тестах (я надеюсь, вы используете nose? =). Для запуска отладчика ipdb при ошибках, достаточно при запуске тестов добавить ключ "--ipdb":
➜ nosetests --ipdb
А для запуска отладчика при некорректно завершившихся тестов нужно добавить ключ "--ipdb-failures":
➜ nosetests --ipdb-failures
Конечно, можно ловить всё и сразу:
➜ nosetests --ipdb --ipdb-failures
Я каждый день использую этот модуль и просто не представляю себе жизни без него.
Потрясающий проект Армина Ронахера (Armin Ronacher [16]), автора фреймворка Flask [17] и вообще одного из крутейших [18] программистов Python называется werkzeug [19] и представляет собой сборник различных утилит для WSGI приложений. Одна из них — клёвый отладчик [20], который позволяет выводить удобный трейсбек ошибки, а так же запускать консоль Python в соответствующем месте трейсбека прямо на странице браузера:
Использовать его очень просто, достаточно обернуть приложение с помощью соответствующего middleware:
from werkzeug.debug import DebuggedApplication
from myapp import app
app = DebuggedApplication(app, evalex=True)
Говорят, что werkzeug умеет отлаживать даже Ajax-запросы, но, к сожалению, я сам лично никогда этого не делал. Буду рад любым комментариям на эту тему.
Ещё один хороший модуль, на этот раз для Django: django-pdb [21]. Он позволяет запускать отладчик при наличии соответствующего GET-параметра в запросе (например: http://127.0.0.1:8000/app/view?ipdb [22]) либо для всех запросов:
➜ python manage.py runserver --ipdb
Либо вызывать отладчик при возникновении исключений (режим «post-mortem»):
➜ python manage.py runserver --pm
или
POST_MORTEM = True
в settings.py.
Но гораздо лучше в Django использовать модуль django-extensions [23], который добавляет очень полезную команду runserver_plus [24]. С помощью этой батарейки можно подружить Django и Werkzeug (см. выше) и начать получать удовольствие от страниц с пятисотой ошибкой =)
Для использования всего этого чуда достаточно запустить девелоперский сервер с помощью команды runserver_plus:
➜ python manage.py runserver_plus
Отладка программы в девелоперском окружении это, конечно, удобно и хорошо, но самое сложное — локализовать проблему по багрепорту от живого пользователя. Иногда это бывает сложно. Несмотря на все преимущества Python [25], модуля «телепатии» не существует, и разработчик остаётся один на один со словами пользователя «ничего не работает!!!11».
Проект Sentry [5] позволяет сохранять каждую ошибку пользователя с текстом исключения, полным стектрейсом исключения и значениями всех локальных переменных в каждой из функций стектрейса, а так же много другой информации: окружение пользователя (браузер, ОС), все заголовки запроса и вообще всё, что пожелает разработчик.
Одинаковые ошибки группируются, таким образом можно наблюдать за «пульсом» проекта и чинить в первую очередь самые критичные места. Ещё один пример использования sentry — логгирование. Можно просто добавить в спорное место запись в лог сообщения с любыми интересующими разработчика переменными, и всё это окажется в sentry.
Но самый большой плюс в том, что всё это можно (и нужно!) использовать в продакшене.
Ещё один интересный отладчик: PuDB [26] представляет собой консольный дебагер с графическим интерфейсом:
Не буду много о нём писать (честно говоря, я сам активно им не пользовался), предлагаю прочитать короткую заметка на хабре: Удобный отладчик для Python/Django проектов [27] от пользователя int22h [28] или более полный обзор: Отладка Python/Django при помощи PuDB [29].
Standalone отладчик Python, на этот раз с полноценным графическим интерфейсом: Winpdb [30]:
Его разработчики утверждают, что winpdb в 20 раз быстрее pdb, а так же поддерживает работу с тредами. Очень большой и подробный туториал можно найти на этой странице: code.google.com/p/winpdb/wiki/DebuggingTutorial [31].
Отдельного слова заслуживают универсальные «комбайны» программирования: IDE, которые позволяют не выходя из редактора запустить код, отпрофилировать его или запустить встроенный отладчик. Я, пожалуй, выделю три среды, предназначенные для разработки на Python: PyCharm [32], PyDev [33] и PTVS [34].
Если честно, я не вижу особого смысла рассматривать каждую из этих IDE, достаточно знать что они есть, что они успешно справляются со своими задачами и вы можете использовать встроенный отладчик прямо из редактора, — это действительно удобно, круто и здорово.
Спасибо всем, кто дочитал и прокомментировал.
Владимир Рудных,
Технический руководитель Календаря Mail.Ru.
Автор: Dreadatour
Источник [35]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/50363
Ссылки в тексте:
[1] предыдущей статье: http://habrahabr.ru/company/mailru/blog/202832/
[2] Введение и теория: http://habrahabr.ru/company/mailru/blog/201594/
[3] Ручное и статистическое профилирование: http://habrahabr.ru/company/mailru/blog/201778/
[4] Википедии: http://ru.wikipedia.org/wiki/Отладка_программы
[5] Sentry: https://github.com/getsentry/sentry/
[6] pdb: http://docs.python.org/2/library/pdb.html
[7] pdb – Interactive Debugger: http://pymotw.com/2/pdb/
[8] Python Module of the Week: http://pymotw.com/2/
[9] pdb – Интерактивный отладчик: http://habrahabr.ru/post/104086/
[10] xa4a: http://habrahabr.ru/users/xa4a/
[11] IPython: http://ipython.org/
[12] ipdb: https://github.com/gotcha/ipdb/
[13] debug: https://github.com/narfdotpl/debug/
[14] see: https://github.com/inky/see/
[15] nose-ipdb: https://github.com/flavioamieiro/nose-ipdb
[16] Armin Ronacher: http://lucumr.pocoo.org/about/
[17] Flask: https://github.com/mitsuhiko/flask/
[18] крутейших: http://lucumr.pocoo.org/projects/
[19] werkzeug: https://github.com/mitsuhiko/werkzeug/
[20] клёвый отладчик: http://werkzeug.pocoo.org/docs/debug/
[21] django-pdb: https://github.com/tomchristie/django-pdb/
[22] http://127.0.0.1:8000/app/view?ipdb: http://127.0.0.1:8000/app/view?ipdb
[23] django-extensions: https://github.com/django-extensions/django-extensions/
[24] runserver_plus: http://django-extensions.readthedocs.org/en/latest/runserver_plus.html
[25] преимущества Python: http://xkcd.com/353/
[26] PuDB: https://pypi.python.org/pypi/pudb
[27] Удобный отладчик для Python/Django проектов: http://habrahabr.ru/post/158139/
[28] int22h: http://habrahabr.ru/users/int22h/
[29] Отладка Python/Django при помощи PuDB: http://adw0rd.com/2012/3/24/python-django-pudb/
[30] Winpdb: http://winpdb.org/about/
[31] code.google.com/p/winpdb/wiki/DebuggingTutorial: https://code.google.com/p/winpdb/wiki/DebuggingTutorial
[32] PyCharm: http://www.jetbrains.com/pycharm/
[33] PyDev: http://pydev.org/
[34] PTVS: http://pytools.codeplex.com/
[35] Источник: http://habrahabr.ru/post/205426/
Нажмите здесь для печати.