Débugger un programme Python
Retour à la liste des articles
05 avril 2019
La cause des erreurs qui surviennent lors de l'exécution d'un programme peut être complexe à identifier à partir d'un simple message d'erreur. Cela demande un exercice qui consiste à recréer mentalement l'état du programme et son déroulement jusqu'au moment où l'erreur s'est produite, pour enfin mettre le doigt sur l'opération problématique qui a amené au bug.
Heureusement pour nous, le module PDB de Python est d'une grande aide pour suivre le cheminement de l'exécution d'un programme et afficher son état.
Prérequis pour cet article
Assure-toi d'avoir les connaissances nécessaires pour cet article en terminant la lecture du chapitre 6 « Un programme dynamique avec Python », ainsi que la lecture de l'article « Inspecter les objets Python ».
Tu ne possèdes pas encore Génies du code ? Il s'agit d'une méthode illustrée, adaptée à tous les niveaux, qui te fera découvrir la programmation à travers la réalisation de ton propre site web de A à Z. Les deux premiers chapitres sont disponibles gratuitement dans leur intégralité !
Découvrir Génies du codeDébugger en pas à pas avec PDB
PDB (pour Python Debugger) permet d'interrompre l'exécution d'un programme pour examiner les variables et suivre l'exécution des instructions. Le processus de débuggage d'un programme consiste en général à avancer dans l'exécution du code, à afficher le contenu d'une variable, avancer encore dans l'exécution, afficher la valeur d'une variable, et ainsi de suite jusqu'à comprendre ce qui cloche ou jusqu'à jeter de rage son ordinateur par la fenêtre (ne fais pas ça s'il te plaît).
Plutôt que de se perdre dans des explications techniques, regardons comment ça fonctionne. Télécharge le code du programme du chapitre 6, au point de contrôle 7, enregistre-le (par exemple sur ton bureau), puis extrais l'archive.
Ouvre le fichier quiz_2.py
dans ton éditeur et ajoute les deux instructions
suivantes au-dessus de l'instruction for question in questions:
pour que le
code ressemble à ça :
import pdb
pdb.set_trace()
for question in questions:
# ...
Exécute le programme en ouvrant un terminal, en te rendant dans le dossier qui
contient le script (avec la commande cd
) et en entrant la commande python3
quiz_2.py
. Tu verras que l'exécution du programme s'est interrompue et que des
informations étranges sont affichées :
> /home/julie/Bureau/web/quiz_2.py(30)<module>()
-> for question in questions:
(Pdb) █
Ce qui s'est passé, c'est que Python a exécuté l'instruction pdb.set_trace()
,
ce qui a provoqué le lancement de l'invite de commandes PDB, reconnaissable à la
mention (Pdb) █
. Voici en détail la signification des informations affichées :
- La première ligne indique le fichier dans lequel l'exécution s'est arrêtée ;
- La deuxième ligne indique l'instruction que Python s'apprête à exécuter ;
- La troisième ligne est la ligne de commande PDB, qui te permet d'entrer des commandes PDB, dont je vais maintenant te parler.
Examiner le contexte : l
Tu peux examiner le code autour de la ligne courante grâce à la commande l .
(il s'agit de la lettre L minuscule, pour list, suivie d'un espace et d'un
point). Tu verras alors le code autour de la ligne courante, et la ligne que
Python s'apprête à exécuter préfixée par une flèche (->
) . C'est très utile
pour te repérer dans l'exécution du programme :
(Pdb) l .
25 ["Les poissons ont-ils tous des écailles?", {"Oui": False, "Non": True}]
26 ]
27
28 import pdb
29 pdb.set_trace()
30 -> for question in questions:
31 is_correct = show_question(question[0], question[1])
32
33 if is_correct:
34 score += 1
35
Afficher des valeurs : p
La commande p
(pour print) permet d'examiner le contenu des variables :
(Pdb) p score
0
(Pdb) p question
*** NameError: name 'question' is not defined
La variable question
n'est pas encore définie parce que la ligne sur laquelle
se trouve la flèche n'a pas encore été exécutée, et on va devoir avancer dans
l'exécution du programme pour que cette variable soit créée. Note que tu peux
combiner cette commande avec les fonctions dir
ou vars
(vues dans l'article
précédent)
pour examiner en détail un objet, en écrivant par exemple p dir(monobjet)
.
Contrôler l'exécution : n
, s
, c
et q
Les commandes n
(pour next), s
(pour step), c
(pour continue) et q
(pour quit) permettent de contrôler l'exécution du programme. Essaie la
commande n
:
> /home/julie/Bureau/web/quiz_2.py(30)<module>()
-> for question in questions:
(Pdb) n
> /home/julie/Bureau/web/quiz_2.py(31)<module>()
-> is_correct = show_question(question[0], question[1])
(Pdb) █
Python a exécuté la ligne qui préfixée par la flèche (ligne 30), et est passé à la
ligne suivante (ligne 31), qui se trouve à l'intérieur de la boucle. La variable
question
devrait donc maintenant être définie :
(Pdb) p question
["Quelle est la vitesse de pointe d'un thon?", {'20 km/h': False, '40 km/h': False, '80 km/h': True}]
Si tu entres de nouveau la commande n
, tu verras apparaître l'affichage de la
question et la demande de réponse :
(Pdb) n
Quelle est la vitesse de pointe d'un thon?
- 20 km/h
- 40 km/h
- 80 km/h
Entrez une réponse:
40 km/h
La réponse n'est pas correcte
> /home/julie/Bureau/web/quiz_2.py(33)<module>()
-> if is_correct:
(Pdb) █
L'affichage de la question a été provoqué par l'exécution de la fonction
show_question
à la ligne 31, dont le code a été exécuté sans que le debugger
s'arrête dedans. C'est le comportement de la commande n
: exécuter le code
jusqu'à atteindre la ligne suivante. Si on avait voulu entrer dans la fonction
show_question
, pour examiner son exécution pas à pas, on aurait dû utiliser la
commande s
.
Utilise la commande n
pour continuer l'exécution jusqu'à arriver de nouveau
sur l'instruction qui va exécuter la fonction show_question
.
Astuce: si tu n'entres aucune commande et appuies juste sur la touche
Entrée, PDB réutilise la dernière commande. Tu peux donc entrer une fois la
commande n
, puis appuyer sur Entrée pour continuer à avancer dans
l'exécution.
Une fois sur cette instruction, utilise la commande s
pour rentrer dans
l'exécution de la fonction :
(Pdb) n
> /home/julie/Bureau/web/quiz_2.py(31)<module>()
-> is_correct = show_question(question[0], question[1])
(Pdb) s
--Call--
> /home/julie/Bureau/web/quiz_2.py(1)show_question()
-> def show_question(question, options):
Tu peux maintenant examiner l'exécution de la fonction show_question
avec la
commande n
.
Une fois que tu as eu toutes les informations que tu voulais, tu peux utiliser
la commande c
pour continuer l'exécution normale du programme, ou q
pour
quitter l'exécution.
Spécificités liées à Django
Tu peux tout à fait utiliser PDB dans le code d'un projet Django. Dans ce cas,
il aura pour effet de bloquer le processus runserver
. En pratique, cela
signifie que quand tu chargeras une page dans ton navigateur qui déclencherait
un invite PDB (par l'exécution de l'instruction pdb.set_trace()
), ton
navigateur semblera charger indéfiniment la page. Cela est dû à l'invite PDB qui
attend des commandes pour continuer l'exécution du code.
Il faut donc que tu te rendes dans le terminal où tu as lancé la commande
runserver
et que tu utilises les commandes PDB pour examiner l'état du
programme, et que tu termines avec la commande c
pour continuer l'exécution et
renvoyer une réponse HTTP au navigateur.
En résumé
L'utilisation de PDB est un excellent moyen de remonter à la source des erreurs car il permet d'examiner l'état du programme et son comportement, ainsi que de suivre le cheminement de son exécution. L'activation de PDB se fait en insérant les lignes suivantes :
import pdb
pdb.set_trace()
PDB dispose de tout un ensemble de commandes qui n'ont pas été couvertes ici,
mais qui peuvent être affichées en utilisant la commande PDB ?
. La
documentation du module PDB
couvre aussi en détail son utilisation.
Tu veux en savoir plus ?
Génies du code est une méthode illustrée, adaptée à tous les niveaux, qui t'initiera à la programmation à travers la réalisation de ton propre site web de A à Z. Les deux premiers chapitres sont disponibles gratuitement dans leur intégralité !
Et aussi, fais un tour sur les autres articles, tous plus intéressants les uns que les autres, en toute modestie.