Отладка приложений Python с помощью модуля PDB

Введение В этом руководстве мы узнаем, как использовать модуль Python PDB [https://docs.python.org/3/library/pdb.html] для отладки приложений Python. Отладка относится к процессу удаления программных и аппаратных ошибок из программного приложения. PDB означает «Отладчик Python» и представляет собой встроенный интерактивный отладчик исходного кода с широким спектром функций, таких как приостановка программы, просмотр значений переменных в определенных экземплярах, изменение этих значений и т. Д.

Вступление

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

В этой статье мы рассмотрим наиболее часто используемые функции модуля PDB.

Задний план

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

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

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

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

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

 # 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) 

Отладка с точками останова

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

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

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

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

 (Pdb) break calc.py:8 

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

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

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

 (Pdb) break calc.calculator 

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

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

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

Точки останова также могут быть объявлены условием. В этом случае программа будет работать до тех пор, пока условие не станет ложным, и остановится, когда это условие станет истинным. Выполните следующую команду в командной строке 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 ; они будут очень часто использоваться, когда вы начнете отладку своих приложений, поэтому давайте узнаем, что они делают и как их можно реализовать.

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

Смущенный? Давайте посмотрим на это на примере.

Повторно запустите программу через приглашение 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 она перемещается внутри этой функции и проходит через нее «шагами», показывая нам, что именно происходит на каждом шаге.

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

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

Заключение

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

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus