REPL и отладчик: ipython, pdb, breakpoint
В первом уроке мы видели базовый REPL Python. Этого достаточно для разовых проверок, но при разработке и отладке хочется больше: автодополнение, удобная навигация, пошаговое исполнение. В этом уроке - инструменты, которые сильно экономят время каждый день.
ipython - улучшенный REPL
Базовый python3 REPL аскетичен: нет цветов, плохое автодополнение, не запоминает многострочный ввод. ipython это исправляет:
pip install ipython
ipython
Что даёт:
- Автодополнение по Tab для имён переменных, методов, путей
- Цветная подсветка ввода и трейсбэков
- Введение многострочных блоков без отступов вручную (он сам форматирует)
- Magic-команды, начинающиеся с
% - История между сессиями (
Up/Downстрелки) - Help через
?и исходник через??
Пример сессии:
In [1]: data = {"name": "Alice", "age": 30}
In [2]: data.<TAB>
clear() fromkeys() items() pop() setdefault() values()
copy() get() keys() popitem() update()
In [3]: data.keys?
Signature: data.keys()
Docstring: D.keys() -> a set-like object providing a view on D's keys
Type: builtin_function_or_method
In [4]: import requests
In [5]: requests.get??
[выводит исходник функции]
Magic-команды ipython
Команды, начинающиеся с % (line magic) или %% (cell magic):
| Команда | Что делает |
|---|---|
%timeit expr | Замеряет время выполнения выражения |
%run script.py | Запускает скрипт в текущем окружении |
%load script.py | Загружает код скрипта в текущую ячейку |
%edit | Открывает редактор, после сохранения выполняет код |
%who / %whos | Список объявленных переменных |
%reset | Сбрасывает все переменные |
%paste | Вставляет из буфера обмена, разбираясь с отступами |
%cd path | Меняет рабочую директорию |
%history | История команд |
%timeit особенно полезен для микро-бенчмарков:
In [1]: %timeit sum(range(1000))
8.42 µs ± 23.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [2]: %timeit [x for x in range(1000)]
13.5 µs ± 142 ns per loop
Скрипт + REPL: python3 -i
Полезный трюк - запустить скрипт и остаться в REPL с его контекстом:
python3 -i script.py
Скрипт выполняется как обычно, но после завершения вместо выхода открывается REPL, в котором доступны все переменные и функции скрипта:
# script.py
def greet(name):
return f"Привет, {name}!"
users = ["Alice", "Bob"]
$ python3 -i script.py
>>> users
['Alice', 'Bob']
>>> greet("Carol")
'Привет, Carol!'
Это удобно для проверки результатов: запустил программу - и можно покопаться в её состоянии, не добавляя print() повсюду.
breakpoint() - встроенная точка останова
С Python 3.7 в языке есть встроенная функция breakpoint(). Вставляешь её в код - и интерпретатор останавливается в этой точке, открывая отладчик:
def compute_total(items):
total = 0
for item in items:
breakpoint() # остановится здесь на каждой итерации
total += item.price
return total
При запуске откроется отладчик (по умолчанию pdb):
> /path/script.py(4)compute_total()
-> total += item.price
(Pdb)
Внутри отладчика можно:
(Pdb) p item # печатает значение item
(Pdb) p item.__dict__ # все атрибуты объекта
(Pdb) n # next - следующая строка
(Pdb) s # step - зайти внутрь функции
(Pdb) c # continue - продолжить до следующей breakpoint()
(Pdb) l # list - показать код вокруг
(Pdb) w # where - стек вызовов
(Pdb) q # quit - выйти
pdb - стандартный отладчик
breakpoint() запускает pdb (Python Debugger) из стандартной библиотеки. Его можно запустить и явно:
python3 -m pdb script.py
Программа остановится на первой строке. Все команды pdb доступны.
Удобные команды pdb:
| Команда | Полная | Что делает |
|---|---|---|
h | help | Список команд |
n | next | Следующая строка |
s | step | Зайти внутрь функции |
c | continue | Продолжить до точки останова |
r | return | До конца текущей функции |
b 42 | break 42 | Установить точку останова на строке 42 |
b file.py:42 | Точка останова в другом файле | |
cl | clear | Удалить точку останова |
p var | Показать значение | |
pp var | pretty print | Красиво напечатать (для больших dict) |
l | list | Показать код |
ll | long list | Показать всю функцию |
w | where | Стек вызовов |
u | up | На уровень выше в стеке |
d | down | На уровень ниже |
q | quit | Выйти |
args | args | Аргументы текущей функции |
interact | Открыть REPL с текущим контекстом |
interact особенно мощный: позволяет писать произвольный Python-код в контексте остановленной функции.
pdb++ и альтернативы
Pdb из коробки минималистичен. Альтернативы лучше:
pdb++(илиpdbpp) - drop-in замена с подсветкой, sticky-режимом, лучшим интерфейсом:
pip install pdbpp
Просто pip install - и стандартный breakpoint() начнёт открывать улучшенный отладчик.
ipdb- pdb с фичами ipython (автодополнение, цвета):
pip install ipdb
Чтобы breakpoint() использовал ipdb:
export PYTHONBREAKPOINT=ipdb.set_trace
- Встроенный отладчик IDE - VS Code, PyCharm имеют GUI-отладчики, обычно удобнее командной строки для сложных сценариев.
Логирование вместо print
print() - тоже инструмент отладки, но в долгосрочной перспективе хуже логирования:
print(f"DEBUG: user={user}") # быстро, но
# - нет уровней
# - не отключается
# - попадёт в продакшн вывод
Альтернатива:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug("user=%s", user)
В продакшене можно поставить level=logging.INFO - и debug-сообщения молча отключатся. Подробнее в уроке про logging.
Полезный flow: исследуй в ipython, переноси в код
Типичный сценарий для backend-задачи:
- Открываешь ipython
- Импортируешь библиотеку, читаешь её документацию через
? - Тестируешь API в REPL
- Когда понятно как работает - копируешь в файл
Это часто быстрее чем сразу писать код и потом дебажить.
Мини-задание
- Установи ipython в venv своего проекта:
pip install ipython
ipython
В REPL попробуй:
data = {"name": "Alice", "age": 30}
data.<TAB> # увидь автодополнение
data.keys? # документация
%timeit sum(range(1000))
- Создай
debug_demo.py:
def divide(a, b):
breakpoint()
return a / b
result = divide(10, 0)
print(result)
Запусти python3 debug_demo.py. В pdb попробуй:
p a- что увидишьp b- что увидишьn- перейти на следующую строку (получишь ZeroDivisionError)q- выйти
- Запусти скрипт через
-i:
python3 -i script_with_function.py
Создай небольшой скрипт, и после его выполнения проверь переменные из скрипта в REPL.
Что дальше
Освоили инструменты для разработки. Модуль 1 завершён - у тебя есть Python, venv, понимание зависимостей и инструменты для экспериментов. В следующем модуле переходим к языку: типы данных, переменные, изменчивость и type hints.