Модуль PDB в Python

В этом руководстве мы узнаем, как использовать модуль PDB  для отладки приложений Python. Отладка относится к процессу удаления программных и аппаратных ошибок из программного приложения.

PDB представляет собой встроенный интерактивный отладчик исходного кода с широким спектром функций, таких как приостановка программы, просмотр значений переменных в определенных экземплярах, изменение этих значений и т.д.

Отладка – одно из самых нежелательных занятий, но в то же время это одна из самых важных задач в жизненном цикле разработки программного обеспечения. На каком-то этапе каждый программист должен отлаживать свой код, если только он не разрабатывает очень простое программное приложение.

Есть много разных способов отладки программного приложения. Очень часто используемый метод – это использование операторов print в разных экземплярах вашего кода, чтобы увидеть, что происходит во время выполнения.

Однако этот метод имеет много проблем, таких как добавление дополнительного кода, который используется для печати значений переменных и т.д. Хотя этот подход может работать для небольшой программы, отслеживание этих изменений кода в большом приложении с большим количеством строк, разбросанные по разным файлам, могут стать огромной проблемой. Отладчик решает эту проблему за нас. Это помогает нам находить источники ошибок в приложении с помощью внешних команд, поэтому код не меняется.

Примечание. Как упоминалось выше, PDB – это встроенный модуль Python, поэтому нет необходимости устанавливать его из внешнего источника.

Ключевые команды

Чтобы понять основные команды или инструменты, имеющиеся в нашем распоряжении в PDB, давайте рассмотрим базовую программу Python, а затем попробуем отладить ее с помощью команд. Таким образом, мы увидим на примере, что именно делает каждая команда.

# Filename: calc.py

operators = ['+', '-', '*', '/']
numbers = [10, 20]

def calculator():
    print("Operators available: ")
    for op in operators:
        print(op)

    print("Numbers to be used: ")
    for num in numbers:
        print(num)

def main():
    calculator()

main()

Вот результат выполнения скрипта выше:

Operators available:
+
-
*
/
Numbers to be used:
10
20

Я не добавлял никаких комментариев в приведенный выше код, так как он удобен для начинающих и вообще не включает сложных концепций или синтаксиса. Не важно пытаться понять «задачу», которую решает этот код, так как его цель состояла в том, чтобы включить определенные вещи, чтобы все команды PDB могли быть протестированы на нем.

Использование PDB требует использования интерфейса командной строки (CLI), поэтому вы должны запускать приложение из терминала или командной строки.

Выполните следующую команду в своем интерфейсе командной строки:

$ python -m pdb calc.py

В приведенной выше команде имя моего файла – «calc.py», поэтому вам нужно вставить сюда свое собственное имя файла.

Примечание. -M – это флаг, который уведомляет исполняемый файл Python о том, что модуль необходимо импортировать. За этим флагом следует имя модуля, которым в нашем случае является pdb.

Вывод команды выглядит так:

> /Users/junaid/Desktop/calc.py(3)<module>()
-> operators = [ '+', '-', '*', '/' ]
(Pdb)

Вывод всегда будет иметь одинаковую структуру. Он начнется с пути к нашему файлу исходного кода. Затем в скобках будет указан номер строки из того файла, на который в настоящее время указывает PDB, в нашем случае это «(3)». Следующая строка, начинающаяся с символа «->», указывает текущую строку.

Чтобы закрыть приглашение PDB, просто введите quit или exit в приглашении PDB.

Еще несколько замечаний: если ваша программа принимает параметры в качестве входных данных, вы также можете передать их через командную строку. Например, если бы наша программа, приведенная выше, требовала от пользователя трех входных данных, то наша команда выглядела бы так:

$ python -m pdb calc.py var1 var2 var3

Двигаясь дальше, если вы ранее закрыли приглашение PDB с помощью команды quit или exit, то перезапустите файл кода через PDB. После этого выполните следующую команду в PDB:

(Pdb) list

Результат выглядит так:

  1     # Filename: calc.py
  2
  3  -> operators = ['+', '-', '*', '/']
  4     numbers = [10, 20]
  5
  6     def calculator():
  7         print("Operators available: ")
  8         for op in operators:
  9             print(op)
 10
 11         print("Numbers to be used: ")
(Pdb)

Это покажет вам первые 11 строк вашей программы, причем «->» указывает на текущую строку, выполняемую отладчиком. Затем попробуйте эту команду в командной строке PDB:

(Pdb) list 4,6

Эта команда должна отображать только выбранные строки, которые в данном случае являются строками с 4 по 6. Вот результат:

  4     numbers = [10, 20]
  5
  6     def calculator():
(Pdb)

С использованием breakpoints

Следующая важная вещь, о которой мы узнаем, – это breakpoints, и обычно используются для более крупных программ, но чтобы лучше понять их, мы увидим, как они работают, на нашем базовом примере. Точки прерывания – это определенные места, которые мы объявляем в нашем коде. Наш код доходит до этого места, а затем останавливается. PDB автоматически присваивает этим точкам номера.

У нас есть следующие различные варианты создания точек прерываний:

  1. По номеру строки.
  2. По объявлению функции.
  3. По условию.

Чтобы объявить breakpoints по номеру строки, выполните в командной строке PDB следующую команду:

(Pdb) break calc.py:8

Эта команда вставляет breakpoints в 8-ю строку кода, которая приостанавливает выполнение программы при достижении этой точки. Результат этой команды показан как:

Breakpoint 1 at /Users/junaid/Desktop/calc.py: 8
(Pdb)

Чтобы объявить точки прерывания для функции, выполните следующую команду в командной строке PDB:

(Pdb) break calc.calculator

Чтобы вставить breakpoints таким образом, вы должны объявить ее, используя имя файла, а затем имя функции. Это выводит следующее:

Breakpoint 2 at /Users/junaid/Desktop/calc.py:6

Как видите, breakpoints автоматически был присвоен номер 2, и также отображается номер строки, т.е. 6, в которой объявлена функция.

Точки прерывания также могут быть объявлены условием. В этом случае программа будет работать до тех пор, пока условие не станет false, и остановится, когда это условие станет true. Выполните следующую команду в командной строке PDB:

(Pdb) break calc.py:8, op == "*"

Это будет отслеживать значение переменной op на протяжении всего выполнения и прерывать работу только тогда, когда ее значение равно «*» в строке 8.

Чтобы увидеть все точки прерывания, которые мы объявили в виде списка, выполните следующую команду в приглашении PDB:

(Pdb) break

Результат выглядит так:

Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py: 8
2   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py: 6
    breakpoint already hit 1 time
3   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py: 8
    stop only if op == "*"
(Pdb)

Наконец, давайте посмотрим, как мы можем отключить, включить и очистить конкретную точку прерывания в любом случае. Выполните следующую команду в командной строке PDB:

(Pdb) disable 2

Это отключит точку 2, но не удалит ее из нашего экземпляра отладчика.

В выводе вы увидите номер отключенной точки прерывания.

Disabled breakpoint 2 at /Users/junaid/Desktop/calc.py:6
(Pdb)

Давайте снова распечатаем список всех точек, чтобы увидеть значение Enb для точки 2:

(Pdb) break

Вывод:

Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:8
2   breakpoint   keep no    at /Users/junaid/Desktop/calc.py:4 # you can see here that the "ENB" column for #2 shows "no"
    breakpoint already hit 1 time
3   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:8
    stop only if op == "*"
(Pdb)

Чтобы снова включить точку прерывания 2, выполните следующую команду:

(Pdb) enable 2

И снова вот результат:

Enabled breakpoint 2 at /Users/junaid/Desktop/calc.py:6

Теперь, если вы снова распечатаете список всех точек, значение столбца «Enb» для точки 2 должно снова показать «да».

Теперь давайте очистим точку 1, которая удалит все вместе.

(Pdb) clear 1

Результат выглядит следующим образом:

Deleted breakpoint 1 at /Users/junaid/Desktop/calc.py:8
(Pdb)

Если мы повторно распечатаем список точек прерываний, теперь он должен отображать только две строки. Посмотрим, что выводит команда «break»:

Num Type         Disp Enb   Where
2   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:4
    breakpoint already hit 1 time
3   breakpoint   keep yes   at /Users/junaid/Desktop/calc.py:8
    stop only if op == "*"

Именно то, что мы ожидали.

Прежде чем мы перейдем к этому разделу, я хочу показать вам все, что отображается, когда мы фактически запускаем код до указанной точки прерывания. Для этого очистим все предыдущие точки и объявим еще одну через приглашение PDB:

1. Удалите все точки прерывания.

(Pdb) clear

После этого введите «y» и нажмите «Enter». Вы должны увидеть такой вывод:

Deleted breakpoint 2 at /Users/junaid/Desktop/calc.py:6
Deleted breakpoint 3 at /Users/junaid/Desktop/calc.py:8

2. Объявите новую точку.

Мы хотим добиться, чтобы код выполнялся до тех пор, пока значение переменной num не станет больше 10. Итак, по сути, программа должна приостановить работу до того, как будет напечатано число «20».

(Pdb) break calc.py:13, num > 10

3. Выполните код до этой точки.

Чтобы запустить код, используйте команду «continue», которая будет выполнять код до тех пор, пока не достигнет точки прерывания или не завершится:

(Pdb) continue

Вы должны увидеть следующий результат:

Operators available:
+
-
*
/
Numbers to be used:
10
> /Users/junaid/Desktop/calc.py(13)calculator()
-> print(num)

Это именно то, что мы ожидали, программа выполняется до этого момента, а затем приостанавливается, теперь нам решать, хотим ли мы что-либо изменить, проверить переменные или запустить скрипт до завершения. Чтобы заставить его работать до завершения, снова запустите команду «продолжить». Результат должен быть следующим:

20
The program finished and will be restarted
> /Users/junaid/Desktop/calc.py(3)<module>()
-> operators = [ '+', '-', '*', '/' ]

Из вышеприведенного вывода видно, что программа продолжается с того места, где она была остановлена, выполняет оставшуюся часть, а затем перезапускается, чтобы мы могли отладить ее дальше, если захотим. Теперь перейдем к следующему разделу.

Важное примечание: прежде чем двигаться дальше, очистите все точки, выполнив команду «clear», а затем введите «y» в командной строке PDB.

Функции next и step

И последнее, но не менее важное: давайте изучим функции next и step. Они будут очень часто использоваться, когда вы начнете отладку своих приложений, поэтому давайте узнаем, что они делают и как их можно реализовать.

Функции step и next используются для итерации кода построчно, между ними очень небольшая разница. Во время итерации, если функция step встречает вызов функции, она переместится к первой строке определения этой функции и покажет нам, что именно происходит внутри функции. Тогда как, если следующая функция встречает вызов функции, она запускает все строки этой функции за один раз и делает паузу при следующем вызове функции.

Повторно запустите программу через приглашение PDB, используя следующую команду:

$ python -m pdb calc.py

Теперь введите continue в командной строке PDB и продолжайте делать это, пока программа не дойдет до конца. Я собираюсь показать часть всей последовательности ввода и вывода ниже, чего достаточно, чтобы объяснить суть. Полная последовательность довольно длинная и может еще больше запутать вас, поэтому она будет опущена.

> /Users/junaid/Desktop/calc.py(1)<module>()
-> operators = [ '+', '-', '*', '/' ]
(Pdb) step
> /Users/junaid/Desktop/calc.py(2)<module>()
-> numbers = [ 10, 20 ]
.
.
.
.
> /Users/junaid/Desktop/calc.py(6)calculator()
-> print("Operators available: " )
(Pdb) step
Operators available:
> /Users/junaid/Desktop/calc.py(8)calculator()
-> for op in operators:
(Pdb) step
> /Users/junaid/Desktop/calc.py(10)calculator()
-> print(op)
(Pdb) step
+
> /Users/junaid/Desktop/calc.py(8)calculator()
-> for op in operators:
(Pdb) step
> /Users/junaid/Desktop/calc.py(10)calculator()
-> print(op)

.
.
.
.

Теперь перезапустите всю программу, но на этот раз используйте команду «следующий» вместо «шаг». Я также показал трассировку ввода и вывода для этого.

> /Users/junaid/Desktop/calc.py(3)<module>()
-> operators = ['+', '-', '*', '/']
(Pdb) next
> /Users/junaid/Desktop/calc.py(4)<module>()
-> numbers = [10, 20]
(Pdb) next
> /Users/junaid/Desktop/calc.py(6)<module>()
-> def calculator():
(Pdb) next
> /Users/junaid/Desktop/calc.py(15)<module>()
-> def main():
(Pdb) next
> /Users/junaid/Desktop/calc.py(18)<module>()
-> main()
(Pdb) next
Operators available:
+
-
*
/
Numbers to be used:
10
20
--Return--

Хорошо, теперь, когда у нас есть трассировка вывода для обеих этих функций, давайте посмотрим, чем они отличаются. Что касается функции step, вы можете видеть, что при вызове функции calculator она перемещается внутри этой функции и проходит через нее «шагами», показывая нам, что именно происходит на каждом шаге.

Однако, если вы видите трассировку вывода для следующей функции, при вызове «основной» функции она не показывает нам, что происходит внутри этой функции (т.е. последующий вызов функции calculator), а затем напрямую выводит конец результат за один шаг.

Эти команды полезны, если вы выполняете итерацию программы и хотите пошагово выполнять определенные функции, но не другие, и в этом случае вы можете использовать каждую команду для своих целей.

Заключение

В этом руководстве мы узнали о сложной технике отладки приложений Python с использованием встроенного модуля PDB. Мы углубились в различные команды устранения неполадок, которые предоставляет нам PDB, включая операторы next и step, breakpoints и т.д. Мы также применили их к базовой программе, чтобы увидеть их в действии.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *