Comment utiliser le débogueur Python

introduction

En développement logiciel, le * débogage * est le processus de recherche puis de résolution des problèmes qui empêchent le logiciel de fonctionner correctement.

Le débogueur Python fournit un environnement de débogage pour les programmes Python. Il prend en charge la définition de points d’arrêt conditionnels, l’exploration pas à pas du code source, l’inspection de la pile, etc.

Travailler de manière interactive avec le débogueur Python

Le débogueur Python fait partie de la distribution Python standard en tant que module appelé + pdb +. Le débogueur est également extensible et se définit comme la classe + Pdb +. Vous pouvez lire la https://docs.python.org/3/library/pdb.html de la documentation officielle de + pdb +] pour en savoir plus.

Nous commencerons par travailler avec un programme court qui comporte deux variables globales, une https: // www.digitalocean.com/community/tutorials/how-to-define-functions-in-python-3[function] qui crée une https://www.digitalocean.com/community/tutorials/how-to-construct- for-loops-in-python-3 [loop], et la construction + if name == 'main ': + qui appellera la fonction + nested_loop () +.

looping.py

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

Nous pouvons maintenant exécuter ce programme via le débogueur Python en utilisant la commande suivante:

python -m pdb looping.py

L’indicateur de ligne de commande + -m + importera n’importe quel module Python et l’exécutera en tant que script. Dans ce cas, nous importons et exécutons le module + pdb +, que nous transmettons à la commande comme indiqué ci-dessus.

Lors de l’exécution de cette commande, vous recevrez le résultat suivant:

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

Dans la sortie, la première ligne contient le nom du module actuel (comme indiqué par + <module> +) avec un chemin de répertoire, ainsi que le numéro de la ligne imprimée qui suit (dans ce cas, il s’agit de + 1 +, mais est un commentaire ou une autre ligne non exécutable, il peut s’agir d’un nombre plus élevé). La deuxième ligne montre la ligne actuelle du code source qui est exécutée ici, car + pdb + fournit une console interactive pour le débogage. Vous pouvez utiliser la commande + help + pour apprendre ses commandes et + help + pour en savoir plus sur une commande spécifique. Notez que la console + pdb + est différente de celle du shell interactif Python.

Le débogueur Python redémarre automatiquement lorsqu’il atteint la fin de votre programme. Chaque fois que vous voulez quitter la console + pdb +, tapez la commande + quit + ou + exit +. Si vous souhaitez redémarrer explicitement un programme à n’importe quel endroit du programme, vous pouvez le faire avec la commande + run +.

Utiliser le débogueur pour parcourir un programme

Lorsque vous travaillez avec des programmes dans le débogueur Python, vous utiliserez probablement les commandes + list,` + step` et + next pour parcourir votre code. Nous allons passer en revue ces commandes dans cette section.

Dans le shell, nous pouvons taper la commande + list afin d’obtenir un contexte autour de la ligne courante. A partir de la première ligne du programme + looping.py + que nous avons affiché ci-dessus - + num_list = [500, 600, 700] + - cela ressemblera à ceci:

(Pdb)
 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)

La ligne en cours est indiquée par les caractères + → +, qui dans notre cas est la première ligne du fichier programme.

Comme il s’agit d’un programme relativement court, nous recevons la quasi-totalité du programme avec la commande + list +. Sans fournir d’arguments, la commande + list + fournit 11 lignes autour de la ligne actuelle, mais vous pouvez également spécifier les lignes à inclure, comme suit:

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

Ici, nous avons demandé que les lignes 3 à 7 soient affichées en utilisant la commande + list 3, 7 +.

Pour se déplacer ligne par ligne dans le programme, nous pouvons utiliser + step + ou + next +:

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

La différence entre + step + et + next + est que + step + s’arrête dans une fonction appelée, tandis que + next + exécute les fonctions appelées pour ne s’arrêter qu’à la ligne suivante de la fonction en cours. Nous pouvons voir cette différence lorsque nous travaillons avec la fonction.

La commande step + parcourra les boucles une fois qu’elle aura atteint le fonctionnement de la fonction, montrant exactement ce qu’elle fait, car elle imprimera d’abord un nombre avec + print (nombre) + puis passera à l’impression. les lettres avec + print (letter) +, retournez au numéro, etc:

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

La commande + next +, à la place, exécutera la fonction entière sans afficher le processus étape par étape. Quittons la session en cours avec la commande + exit + puis relançons le débogueur:

python -m pdb looping.py

Nous pouvons maintenant utiliser la commande + next +:

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

En parcourant votre code, vous voudrez peut-être examiner la valeur transmise à une variable, ce que vous pouvez faire avec la commande + pp +, qui permettra d’imprimer la valeur de l’expression à l’aide de https: //docs.python. org / 3 / library / pprint.html # module-pprint [+ pprint + module]:

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

La plupart des commandes dans + pdb + ont des alias plus courts. Pour «+ step », cette forme abrégée est « s », et pour « next », elle est « n ». La commande ` help ` listera les alias disponibles. Vous pouvez également appeler la dernière commande que vous avez appelée en appuyant sur la touche ` ENTER +` à l’invite.

Points d’arrêt

Vous travaillerez généralement avec des programmes plus volumineux que l’exemple ci-dessus, de sorte que vous souhaiterez probablement examiner des fonctions ou des lignes particulières plutôt que de parcourir un programme complet. En utilisant la commande + break + pour définir des points d’arrêt, vous exécuterez le programme jusqu’au point d’arrêt spécifié.

Lorsque vous insérez un point d’arrêt, le débogueur lui attribue un numéro. Les numéros attribués aux points d’arrêt sont des entiers successifs commençant par le chiffre 1, auquel vous pouvez vous référer lorsque vous travaillez avec des points d’arrêt.

Les points d’arrêt peuvent être placés à certains numéros de ligne en respectant la syntaxe + <fichier_programme>: <numéro_ligne> + comme indiqué ci-dessous:

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

Tapez + clear + puis + y + pour supprimer tous les points d’arrêt actuels. Vous pouvez ensuite placer un point d’arrêt où une fonction est définie:

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

Pour supprimer les points d’arrêt actuels, tapez + clear + puis + y +. Vous pouvez également définir une condition:

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

Maintenant, si nous lançons la commande + continue +, le programme s’interrompra lorsque le + nombre + + x + sera supérieur à 500 (c’est-à-dire lorsqu’il sera égal à 600 dans la deuxième itération du boucle extérieure):

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

Pour voir une liste des points d’arrêt actuellement configurés, utilisez la commande + break + sans aucun argument. Vous recevrez des informations sur les particularités des points d’arrêt que vous avez définis:

(Pdb)
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)

Nous pouvons également désactiver un point d’arrêt avec la commande + disable + et le numéro du point d’arrêt. Dans cette session, nous ajoutons un autre point d’arrêt, puis désactivons le premier:

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

Pour activer un point d’arrêt, utilisez la commande + enable +, et pour supprimer entièrement un point d’arrêt, utilisez la commande + 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)

Les points d’arrêt dans + pdb + vous fournissent beaucoup de contrôle. Certaines fonctionnalités supplémentaires incluent l’ignorance des points d’arrêt lors de l’itération actuelle du programme avec la commande + ignore + (comme dans + ignore 1 +), le déclenchement d’actions à un point d’arrêt avec la commande + orders + (comme dans + commande 1 + `) et créer des points d’arrêt temporaires qui sont automatiquement effacés lors de la première exécution du programme avec la commande + tbreak + (pour une interruption temporaire à la ligne 3, par exemple, vous pouvez taper + tbreak 3 + ` ).

Intégration de + pdb + dans les programmes

Vous pouvez déclencher une session de débogage en important le module + pdb + et en ajoutant les fonctions + pdb + + + pdb.set_trace () + `au-dessus de la ligne où vous souhaitez que la session commence.

Dans notre exemple de programme ci-dessus, nous ajouterons l’instruction + import + et la fonction à laquelle nous souhaitons entrer dans le débogueur. Pour notre exemple, ajoutons-le avant la boucle imbriquée.

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

En ajoutant le débogueur à votre code, vous n’avez pas besoin de lancer votre programme de manière spéciale ni de vous souvenir de définir des points d’arrêt.

L’importation du module + pdb + et l’exécution de la fonction + pdb.set_trace () + vous permettent de démarrer votre programme comme d’habitude et d’exécuter le débogueur pendant son exécution.

Modification du flux d’exécution du programme

Le débogueur Python vous permet de modifier le flux de votre programme à l’exécution avec la commande + jump +. Cela vous permet d’avancer pour empêcher l’exécution de certains codes ou de revenir en arrière pour exécuter le code à nouveau.

Nous allons travailler avec un petit programme qui crée une liste des lettres contenues dans la chaîne + sammy =" sammy "+:

letter_list.py

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

if __name__ == "__main__":
   print_sammy()

Si nous exécutons le programme normalement avec la commande + python letter_list.py +, nous recevrons le résultat suivant:

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

Avec le débogueur Python, montrons comment nous pouvons modifier l’exécution en commençant tout d’abord par * avancer après le premier cycle. Quand nous faisons cela, nous remarquons qu’il y a une perturbation de la https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-python-3 [+ pour + boucle]:

python -m pdb letter_list.py
> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb)
 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)
Breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb)
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
's'
(Pdb)
['s']
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
'a'
(Pdb)
Disabled breakpoint 1 at /Users/sammy/letter_list.py:5
(Pdb)
['s']
['s', 'm']
['s', 'm', 'm']
['s', 'm', 'm', 'y']

La session de débogage ci-dessus met une pause à la ligne 5 pour empêcher le code de continuer, puis continue à travers le code (avec l’impression de jolies valeurs de + lettre + pour montrer ce qui se passe). Ensuite, nous utilisons la commande + jump + pour passer à la ligne 6. A ce stade, la variable + lettre + est définie égale à la chaîne + 'a' +, mais nous sautons le code qui l’ajoute à la liste + sammy_list +. Nous désactivons ensuite le point d’arrêt pour poursuivre l’exécution comme d’habitude avec la commande + continue +, de sorte que + 'a' + n’est jamais ajouté à + ​​sammy_list +.

Ensuite, nous pouvons quitter cette première session et redémarrer le débogueur pour * revenir en arrière * dans le programme afin de réexécuter une instruction déjà exécutée. Cette fois, nous lancerons à nouveau la première itération de la boucle + pour + dans le débogueur:

> /Users/sammy/letter_list.py(1)<module>()
-> def print_sammy():
(Pdb)
 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)
Breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
's'
(Pdb)
> /Users/sammy/letter_list.py(5)print_sammy()
-> sammy_list.append(letter)
(Pdb)
> /Users/sammy/letter_list.py(6)print_sammy()
-> print(sammy_list)
(Pdb)
's'
(Pdb)
Disabled breakpoint 1 at /Users/sammy/letter_list.py:6
(Pdb)
['s', 's']
['s', 's', 'a']
['s', 's', 'a', 'm']
['s', 's', 'a', 'm', 'm']
['s', 's', 'a', 'm', 'm', 'y']

Dans la session de débogage ci-dessus, nous avons ajouté une pause à la ligne 6, puis nous sommes revenus à la ligne 5 après avoir continué. Nous avons imprimé joliment en cours de route pour montrer que la chaîne + 's' + était ajoutée à la liste + sammy_list + deux fois. Nous avons ensuite désactivé la pause à la ligne 6 et poursuivi l’exécution du programme. La sortie montre deux valeurs de + 's' + ajoutées à + ​​sammy_list +.

Le débogueur empêche certains sauts, en particulier lorsque vous entrez et sortez de certaines instructions de contrôle de flux non définies. Par exemple, vous ne pouvez pas accéder aux fonctions avant que les arguments ne soient définis ni au milieu d’une instruction + try: sauf +. Vous ne pouvez pas non plus sortir d’un bloc finally finally +.

L’instruction + jump + avec le débogueur Python vous permet de modifier le flux d’exécution lors du débogage d’un programme pour voir si le contrôle de flux peut être modifié à des fins différentes ou pour mieux comprendre quels problèmes se posent dans votre code.

Table des commandes + pdb + communes

Voici un tableau de commandes utiles + pdb + ainsi que leurs formes abrégées à garder à l’esprit lorsque vous travaillez avec le débogueur 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

Pour en savoir plus sur les commandes et l’utilisation du débogueur, consultez la documentation Python.

Conclusion

Le débogage est une étape importante de tout projet de développement logiciel. Le débogueur Python + pdb + implémente un environnement de débogage interactif que vous pouvez utiliser avec n’importe quel programme écrit en Python.

Avec des fonctionnalités qui vous permettent de suspendre votre programme, de regarder les valeurs sur lesquelles vos variables sont définies et de suivre son exécution de manière discrète, vous pouvez mieux comprendre ce que fait votre programme et rechercher les bogues existants. la logique ou résoudre les problèmes connus.