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 code

Dé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é !

Découvrir Génies du code