Поиск по сайту:

Как использовать отладчик Python


Введение

В разработке программного обеспечения отладка — это процесс поиска и устранения проблем, препятствующих правильной работе программного обеспечения.

Отладчик Python предоставляет среду отладки для программ Python. Он поддерживает установку условных точек останова, просмотр исходного кода по одной строке за раз, проверку стека и многое другое.

Предпосылки

У вас должен быть установлен Python 3 и настроена среда программирования на вашем компьютере или сервере. Если у вас не настроена среда программирования, вы можете обратиться к руководствам по установке и настройке среды программирования на вашем сервере, подходящей для вашей операционной системы (Ubuntu, CentOS, Debian и т. д.).

Интерактивная работа с отладчиком Python

Отладчик Python входит в состав стандартного дистрибутива Python в виде модуля с именем pdb. Отладчик также является расширяемым и определяется как класс Pdb. Вы можете прочитать официальную документацию pdb, чтобы узнать больше.

Информация: Чтобы следовать примеру кода в этом руководстве, откройте интерактивную оболочку Python в вашей локальной системе, выполнив команду python3. Затем вы можете копировать, вставлять или редактировать примеры, добавляя их после приглашения >>>.

Мы начнем с короткой программы, в которой есть два глобальных цикла и конструкция if __name__ == __main__:, которая вызовет функцию nested_loop().

num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']


def nested_loop():
    for number in num_list:
        print(number)
        for letter in alpha_list:
            print(letter)

if __name__ == '__main__':
    nested_loop()

Теперь мы можем запустить эту программу через отладчик Python, используя следующую команду:

  1. python -m pdb looping.py

Флаг командной строки -m импортирует для вас любой модуль Python и запускает его как скрипт. В этом случае мы импортируем и запускаем модуль pdb, который мы передаем в команду, как показано выше.

После запуска этой команды вы получите следующий вывод:

Output
> /Users/sammy/looping.py(1)<module>() -> num_list = [500, 600, 700] (Pdb)

В выходных данных первая строка содержит имя текущего модуля (как указано в <module>) с путем к каталогу и напечатанный номер следующей строки (в данном случае это 1, но если есть комментарий или другая неисполняемая строка, это может быть большее число). Вторая строка показывает текущую строку исходного кода, который выполняется здесь, поскольку pdb предоставляет интерактивную консоль для отладки. Вы можете использовать команду help, чтобы узнать ее команды, и help command, чтобы узнать больше о конкретной команде. Обратите внимание, что консоль pdb отличается от интерактивной оболочки Python.

Отладчик Python автоматически запустится, когда дойдет до конца вашей программы. Всякий раз, когда вы хотите выйти из консоли pdb, введите команду quit или exit. Если вы хотите явно перезапустить программу в любом месте программы, вы можете сделать это с помощью команды run.

Использование отладчика для перемещения по программе

При работе с программами в отладчике Python вы, скорее всего, будете использовать команды list, step и next для перемещения по коду. Мы рассмотрим эти команды в этом разделе.

В оболочке мы можем ввести команду list, чтобы получить контекст вокруг текущей строки. Из первой строки программы looping.py, которую мы показали выше — num_list=[500, 600, 700] — это будет выглядеть следующим образом:

(Pdb) list
  1  ->	num_list = [500, 600, 700]
  2  	alpha_list = ['x', 'y', 'z']
  3  	
  4  	
  5  	def nested_loop():
  6  	    for number in num_list:
  7  	        print(number)
  8  	        for letter in alpha_list:
  9  	            print(letter)
 10  	
 11  	if __name__ == '__main__':
(Pdb) 

Текущая строка обозначается символами ->, что в нашем случае является первой строкой файла программы.

Поскольку это относительно короткая программа, мы получаем почти всю программу с помощью команды list. Без указания аргументов команда list предоставляет 11 строк вокруг текущей строки, но вы также можете указать, какие строки следует включить, например так:

(Pdb) list 3, 7
  3  	
  4  	
  5  	def nested_loop():
  6  	    for number in num_list:
  7  	        print(number)
(Pdb) 

Здесь мы запросили отображение строк 3-7 с помощью команды list 3, 7.

Чтобы перемещаться по программе построчно, мы можем использовать step или next:

(Pdb) step
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb) 
(Pdb) next
> /Users/sammy/looping.py(2)<module>()
-> alpha_list = ['x', 'y', 'z']
(Pdb) 

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

Команда step будет перебирать циклы, как только дойдет до выполнения функции, показывая, что именно делает цикл, так как сначала будет напечатано число с помощью print(number) затем перейдите к печати букв с помощью print(letter), вернитесь к номеру и т. д.:

(Pdb) step
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb) step
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb) step
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb) step
--Call--
> /Users/sammy/looping.py(5)nested_loop()
-> def nested_loop():
(Pdb) step
> /Users/sammy/looping.py(6)nested_loop()
-> for number in num_list:
(Pdb) step
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb) step
500
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb) step
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb) step
x
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb) step
> /Users/sammy/looping.py(9)nested_loop()
-> print(letter)
(Pdb) step
y
> /Users/sammy/looping.py(8)nested_loop()
-> for letter in alpha_list:
(Pdb)

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

  1. python -m pdb looping.py

Теперь мы можем работать с командой next:

(Pdb) next
> /Users/sammy/looping.py(5)<module>()
-> def nested_loop():
(Pdb) next
> /Users/sammy/looping.py(11)<module>()
-> if __name__ == '__main__':
(Pdb) next
> /Users/sammy/looping.py(12)<module>()
-> nested_loop()
(Pdb) next
500
x
y
z
600
x
y
z
700
x
y
z
--Return--
> /Users/sammy/looping.py(12)<module>()->None
-> nested_loop()
(Pdb)  

Просматривая свой код, вы можете захотеть проверить значение, переданное переменной, что вы можете сделать с помощью команды pp, которая будет красиво печатать значение выражения, используя pprint модуль:

(Pdb) pp num_list
[500, 600, 700]
(Pdb) 

Большинство команд в pdb имеют более короткие псевдонимы. Для step эта короткая форма — s, а для nextn. Команда help выводит список доступных псевдонимов. Вы также можете вызвать последнюю команду, которую вы вызвали, нажав клавишу ENTER в командной строке.

Контрольные точки

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

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

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

(Pdb) break looping.py:5
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb)

Введите clear, а затем y, чтобы удалить все текущие точки останова. Затем вы можете разместить точку останова там, где определена функция:

(Pdb) break looping.nested_loop
Breakpoint 1 at /Users/sammy/looping.py:5
(Pdb) 

Чтобы удалить текущие точки останова, введите clear, а затем y. Вы также можете установить условие:

(Pdb) break looping.py:7, number > 500
Breakpoint 1 at /Users/sammy/looping.py:7
(Pdb)     

Теперь, если мы выполним команду continue, программа прервется, когда число x оценивается как большее 500 (то есть, когда он устанавливается равным 600 во второй итерации внешнего цикла):

(Pdb) continue
500
x
y
z
> /Users/sammy/looping.py(7)nested_loop()
-> print(number)
(Pdb) 

Чтобы просмотреть список точек останова, которые в данный момент настроены на выполнение, используйте команду break без каких-либо аргументов. Вы получите информацию об особенностях установленных вами точек останова:

(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /Users/sammy/looping.py:7
	stop only if number > 500
	breakpoint already hit 2 times
(Pdb) 

Мы также можем отключить точку останова с помощью команды disable и номера точки останова. В этом сеансе мы добавляем еще одну точку останова, а затем отключаем первую:

(Pdb) break looping.py:11
Breakpoint 2 at /Users/sammy/looping.py:11
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep no    at /Users/sammy/looping.py:7
	stop only if number > 500
	breakpoint already hit 2 times
2   breakpoint   keep yes   at /Users/sammy/looping.py:11
(Pdb) 

Чтобы включить точку останова, используйте команду enable, а чтобы полностью удалить точку останова, используйте команду clear:

(Pdb) enable 1
Enabled breakpoint 1 at /Users/sammy/looping.py:7
(Pdb) clear 2
Deleted breakpoint 2 at /Users/sammy/looping.py:11
(Pdb) 

Точки останова в pdb предоставляют вам широкие возможности управления. Некоторые дополнительные функции включают игнорирование точек останова во время текущей итерации программы с помощью команды ignore (как в ignore 1), инициирование действий, выполняемых в точке останова с помощью commands (как в command 1) и создания временных точек останова, которые автоматически сбрасываются при первом выполнении программы с помощью команды tbreak (для временный разрыв в строке 3, например, вы можете ввести tbreak 3).

Интеграция pdb в программы

Вы можете инициировать сеанс отладки, импортировав модуль pdb и добавив функцию pdb pdb.set_trace() над строкой, где вы хотите сеанс для начала.

В нашем примере программы выше мы добавим оператор import и функцию, которую мы хотели бы ввести в отладчик. Для нашего примера добавим его перед вложенным циклом.

# Import pdb module
import pdb

num_list = [500, 600, 700]
alpha_list = ['x', 'y', 'z']


def nested_loop():
    for number in num_list:
        print(number)

        # Trigger debugger at this line
        pdb.set_trace()
        for letter in alpha_list:
            print(letter)

if __name__ == '__main__':
    nested_loop()

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

Импортируя модуль pdb и запуская функцию pdb.set_trace(), вы можете запускать свою программу как обычно и запускать отладчик во время ее выполнения.

Изменение потока выполнения программы

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

Мы будем работать с небольшой программой, которая создает список букв, содержащихся в строке sammy=sammy:

def print_sammy():
    sammy_list = []
    sammy = "sammy"
    for letter in sammy:
        sammy_list.append(letter)
        print(sammy_list)

if __name__ == "__main__":
    print_sammy()

Если мы запустим программу как обычно с помощью команды python letter_list.py, мы получим следующий вывод:

Output
['s'] ['s', 'a'] ['s', 'a', 'm'] ['s', 'a', 'm', 'm'] ['s', 'a', 'm', 'm', 'y']

С помощью отладчика Python давайте покажем, как мы можем изменить выполнение, забегая вперед после первого цикла. Сделав это, мы заметим нарушение цикла for:

  1. python -m pdb letter_list.py
> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb) list
  1  ->	def print_sammy():
  2  	    sammy_list = []
  3  	    sammy = "sammy"
  4  	    for letter in sammy:
  5  	        sammy_list.append(letter)
  6  	        print(sammy_list)
  7  	
  8  	if __name__ == "__main__":
  9  	    print_sammy()
 10  	
 11  	
(Pdb) break 5
Breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb) continue
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) pp letter
's'
(Pdb) continue
['s']
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) jump 6
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
'a'
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb) continue
['s']
['s', 'm']
['s', 'm', 'm']
['s', 'm', 'm', 'y']

Вышеприведенный сеанс отладки ставит разрыв в строке 5, чтобы предотвратить продолжение кода, затем продолжается по коду (наряду с красивой печатью некоторых значений letter, чтобы показать, что происходит). Затем мы используем команду jump, чтобы перейти к строке 6. В этот момент переменная letter устанавливается равной строке a, но мы пропускаем код, который добавляет это в список sammy_list. Затем мы отключаем точку останова, чтобы продолжить выполнение, как обычно, с помощью команды continue, поэтому a никогда не добавляется к sammy_list.

Затем мы можем выйти из этого первого сеанса и перезапустить отладчик, чтобы вернуться в программу и повторно запустить оператор, который уже был выполнен. На этот раз мы снова запустим первую итерацию цикла for в отладчике:

> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb) list
  1  ->	def print_sammy():
  2  	    sammy_list = []
  3  	    sammy = "sammy"
  4  	    for letter in sammy:
  5  	        sammy_list.append(letter)
  6  	        print(sammy_list)
  7  	
  8  	if __name__ == "__main__":
  9  	    print_sammy()
 10  	
 11  	
(Pdb) break 6
Breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb) continue
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
's'
(Pdb) jump 5
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb) continue
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb) pp letter
's'
(Pdb) disable 1
Disabled breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb) continue
['s', 's']
['s', 's', 'a']
['s', 's', 'a', 'm']
['s', 's', 'a', 'm', 'm']
['s', 's', 'a', 'm', 'm', 'y']

В приведенном выше сеансе отладки мы добавили разрыв в строке 6, а затем, продолжив, вернулись к строке 5. По пути мы красиво напечатали, чтобы показать, что строка s дважды добавлялась в список sammy_list. Затем мы отключили разрыв в строке 6 и продолжили выполнение программы. Вывод показывает два значения s, добавленные к sammy_list.

Некоторые переходы предотвращаются отладчиком, особенно при входе и выходе из определенных операторов управления потоком, которые не определены. Например, вы не можете перейти к функциям до того, как аргументы определены, и вы не можете перейти в середину оператора try:except. Вы также не можете выйти из блока finally.

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

Таблица общих команд pdb

Вот таблица полезных команд pdb вместе с их краткими формами, о которых следует помнить при работе с отладчиком Python.

Command Short form What it does
args a Print the argument list of the current function
break b Creates a breakpoint (requires parameters) in the program execution
continue c or cont Continues program execution
help h Provides list of commands or help for a specified command
jump j Set the next line to be executed
list l Print the source code around the current line
next n Continue execution until the next line in the current function is reached or returns
step s Execute the current line, stopping at first possible occasion
pp pp Pretty-prints the value of the expression
quit or exit q Aborts the program
return r Continue execution until the current function returns

Подробнее о командах и работе с отладчиком можно прочитать в документации по отладчику Python.

Заключение

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

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