Компания
886,41
рейтинг
10 декабря 2013 в 02:07

Разработка → Профилирование и отладка 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

в settings.py.

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.
Автор: @Dreadatour

Комментарии (26)

  • +4
    На следующей строке заголовка ожидалось «и профилирование Python» :)
    • +1
      Но нет! =))
  • +1
    Есть еще django-devserver от того-же разработчика что и Sentry сделал — Дэвида Крамера из Disqus.
    • +1
      Точно! Совсем про него забыл. Хорошее дополнение к статье, спасибо!

      Девид вообще молодец, пишет замечательные проекты, его выступления на конференциях одни из лучших.
  • +2
    К сожалению, лично я сколько не пытался, так и не смог пересилить себя и променять скорость, удивительную гибкость и удобство работы с консолью и любимым текстовым редактором
    Эти показатели будут постоянно увеличиваться со временем работы с IDE.
    Однажды вы скажете: «И как это я раньше мог думать, что IDE перегруженная и в ней есть что-то лишнее?»
    • 0
      Не буду спорить, всё может быть =)
      Когда-то я недоумевал, как можно работать в OS X, ведь линукс намного круче, но сейчас сижу за макбуком и не понимаю, как может быть иначе.
  • +1
    К списку IDE можно добавить Wing IDE. Там быстрый графический отладчик, который корректно работает с библиотекой matplotlib.
    • 0
      Спасибо за дополнение, добавил ссылку на Wing IDE в статью.
    • 0
      А как именно можно некорректно работать с библиотекой matplotlib?
      • +1
        Цикл обработки сообщений matplotlib не позволяет продолжать (блокирует) выполнение кода после вызова show(). Интерактивный режим не работает, после завершения исполняемого процесса python, все фигуры matplotlib'а закрываются. В IPython и Wing IDE эту проблему обошли, там цикл обработки сообщений matplotlib'а выполняется независимо, то есть аналогично тому как это сделано в Matlab. В PyCharm с этой проблемой делать ничего в ближайшее время не собираются, что очень печально.
        Вы вроде имеете отношение к разработке PTVS, как там с этим дело обстоит?
        • +1
          А, понял, о чем вы.

          В PTVS есть «IPython REPL» — как можно догадаться из названия, под капотом там IPython + pylab, а графика отображается непосредственно в окне REPL, так что show не нужен:

          PTVS IPython REPL
          • 0
            В режиме отладки это тоже работает? То есть, я могу поставить точку остановки в коде после вывода графика (plot), например, с tk/qt бэкендом, а затем добавить на созданные оси новый график прямо из REPL, вызвав ещё раз функцию plot()?
            • 0
              В режиме отладки REPL только обычный, поэтому там графики непосредственно отображаться не будут, и нужен show. Но можно остановиться внутри show (без брейкпоинта, просто нажать паузу в отладчике — окно графика на это время замерзнет), сделать новый plot в REPL, и запустить выполнение дальше, чтобы новый график отобразился в окне.
  • +1
    А можно чуть подробнее про Sentry? Правильно ли я понимаю, что вы подняли свой сервер Sentry и шлете данные в него, а не пользуетесь их платной подпиской? Нужна ли лицензия в таком случае?
    Сколько данных прокачиваете в Sentry и на каком сервере?
    • +1
      Да, мы подняли sentry на своём сервере, это совсем не сложно. Кроме того, политика нашей компании запрещает отправлять любые внутренние данные наружу, в сторонние сервисы.

      Не совсем понял, о какой лицензии идёт речь? Sentry — open-source продукт, им можно пользоваться совершенно бесплатно: github.com/getsentry/sentry/blob/master/LICENSE

      К сожалению, я не вспомню точно характеристики машины, на которой у нас крутится sentry (если нужно — могу позже уточнить у наших админов), но там совершенно точно самый обычный сервер без изысков. Сейчас с нашего проекта приходит, в среднем, около 100 сообщений в час — это ошибки и разного рода логгирование. Пару раз мы не совсем удачно раскатывались в бой и в sentry попадало порядка нескольких тысяч сообщений в минуту — сервер выдержал и никаких проблем не было. Кроме того, там можно настроить рейт-лимиты, тогда некоторые ошибки будут игнорироваться, зато проблем с нагрузкой не будет.

      Вот тут я писал про инфраструктуру нашего проекта: habrahabr.ru/company/mailru/blog/196184/ там есть раздел про sentry.
  • +3
    > Если честно, я не вижу особого смысла рассматривать каждую из этих IDE, достаточно знать что они есть, что они успешно справляются со своими задачами и вы можете использовать встроенный отладчик прямо из редактора

    Рассматривать все-таки имеет смысл, потому что функциональность, помимо базовой, там может весьма сильно различаться. Например, PyCharm умеет дебажить Google AppEngine и автоматически аттачиться к дочерним процессам. PTVS — аттачиться к уже запущеным питоновским процессам (в т.ч. кастомным хостам, а не только python.exe) и отлаживать нативный код одновременно с питоновским, и предоставляет полноценный отладочный REPL, работающий «внутри» отлаживаемого процесса. Оба умеют отлаживать шаблоны Django (ставить в них брейкпоинты, ходить пошагово etc). Не берусь сказать, что уникального есть в PyDev или Wing, но наверняка какие-то интересные вещи можно найти.
    • 0
      Так я же не спорю, это всё замечательные продукты и у всех них есть свои плюсы и минусы (например, PTVS — только под Windows и это сразу очень сильно ограничивает возможности его применения).

      Я не люблю писать о том, чего я сам лично не пробовал, поэтому не стал юлить и честно обошёл этот вопрос стороной. Было бы здорово, кстати, если бы кто-нибудь (может, ты? ;) написал обо всех этих возможностях IDE в режиме отладки и профилирования. Чем больше информации, тем лучше — каждый найдёт что-нибудь себе по душе.
      • +2
        У меня, как у разработчика одного из этих продуктов, будут проблемы с предвзятостью :)

        Вообще, детальный сторонний разбор имеющихся питоновских IDE — отладки и профилирования в т.ч., но также и редактирования, и поддержки веб-фреймворков, и всяких экзотических фич типа редактирования Cython или нативной отладки, мог бы быть очень интересной статьей, имхо. Благо, что хороших вариантов за последние пару лет стало неприлично много, и отличий там тоже накопилось довольно много. И при этом большинство разработчиков про них не в курсе — как правило, люди знают подробности о «своей» IDE, но не о других.
        • +1
          Ну что ж, будем ждать желающего написать пост =)
        • +1
          Иметь возможность отлаживать Cython код графическим отладчиком было бы здорово! Я, например, не в курсе, существуют ли какие-то инструменты для этого.

          А PyCharm, кстати, использует отладчик PyDev.

          И ещё было бы интересно почитать про возможности отладки сишных модулей расширений. Я когда-то пробовал собирать pyd-файлы с отладочной информацией и аттачиться к python-процессу с помощью VisualStudio, но ничего не вышло. Хотя в Matlab c mex-файлами такой способ работает отлично.
          • +1
            Мне неизвестно о специальной поддерже отладки Cython в какой-либо IDE. В PyCharm есть поддержка Cython в редакторе, причем он в т.ч. использует объявления типов для автозавершения, а также генерация кода при сборке. Больше вроде бы ни у кого никакой специальной поддержки нет.

            В PTVS, теоретически, можно отлаживать Cython как C — если включить вывод #line в сгенерированном коде, то нативный отладчик будет при этом показывать Cython-исходник, но локальные переменные будут отображаться из сгенерированного кода, и вычисление выражений в этих фреймах тоже будет сишным. Хотя PTVS умеет в режиме смешанной отладки показывать в нативных фреймах PyObject* как питоновские объекты, но это все-таки не совсем то.

            Идеальным вариантом было бы взять редактирование от PyCharm :) а отладку нативного кода — от PTVS, и к последней прикрутить явную поддержку Cython — теоретически, это возможно, я уже исследовал немного эту тему, но в ближайшем релизе этой фичи не будет точно. В следующем за ним — очень вероятно.

            Про сборку и отладку .pyd трудно сказать что-либо конкретное, не зная, какие именно у вас были проблемы. Посмотрите доку по соответствующей фиче в PTVS; если не поможет — можете кидать вопросы мне в личку.
  • +1
    Я, пожалуй, выделю три среды, предназначенные для разработки на Python: PyCharm, PyDev, Wing IDE и PTVS.


    Все люди делятся на три категории: те, которые умеют считать и те, которые нет.
    • +2
      Fixed. Спасибо за полезный комментарий =)
  • +2
    Спасибо за интересную статью. Кстати о тестах. А что думаете о pyVows? (Тут рассказывают как использовать в Django.) Пока это моя любимая либа для тестов.
    • 0
      Не пробовал, спасибо за ссылку!
      Обязательно изучу и попробую, асинхронность выглядит крайне заманчиво =)
  • +2
    Если что-то свалилось, а мы в IPython, то запустить %debug.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка