Débogage visuel
Objectifs
Afin de déboguer un programme, il est nécessaire de pouvoir introspecter l'état de celui-ci.
L'état du programme est par définition le contenu de sa mémoire, c'est à dire les instances ainsi que les liens entre celles-ci.
Jusqu'ici, pour pouvoir afficher des morceaux de cet état, nous avons simplement ajouté des appels à la fonction print
dans le programme ou alors utilisé le débogueur Pdb
.
Dans cet exercice, nous allons utiliser un petit outil maison de débogue : traceur.py
disponible ici.
Comme son nom l'indique, cet outil est lui-même un module python.
ATTENTION ce petit module est fourni dans le but de vous aider à mettre au point vos programmes utilisant des listes chaînées : si son utilisation ne vous semble pas évidente ou que vous n'en voyez pas l'intérêt, ne perdez pas de temps là-dessus... la technique ancestrale consistant à dessiner vos listes à la main sur une feuille de papier reste toujours fonctionnelle !
ATTENTION pour que le traceur
fonctionne il faut installer graphviz
sur votre machine (que nous avons déjà utilisé dans le mini-projet "Mots suivants").
Tracer une fonction
Le traceur
permet de tracer graphiquement tous les appels à une fonction.
Pour s'en servir, il suffit :
- d'ajouter
from traceur import trace
au début de votre code ; - d'ajouter une ligne
@trace
avant chaque déclaration de fonction que vous voulez tracer (techniquement, on parle d'annotation de fonction dans le jargon Python) ; - d'exécuter votre programme dans
terminology
(terminal capable d'afficher des images).
Le traceur
va alors afficher deux images pour chaque appel à chaque fonction tracée :
- une image avec les arguments de la fonctions (ce sont des variables, et donc des références) ainsi que toutes les instances accessibles depuis ces arguments ;
- une image avec l'instance retournée par la fonction ainsi que toutes les instances accessibles depuis cette instance.
Si vous ne disposez pas de terminology
, il est toujours possible d'utiliser le traceur
.
Pour cela, il suffit de donner la valeur False
à l'argument visualize
dans l'annotation de fonction : @trace(visualize=False)
.
Dans ce cas, le traceur
va générer dans le répertoire courant les images correspondant aux appels des fonctions tracées.
Deux images sont générées telles que décrites ci-dessus.
Le nom des images générées est affiché sur la sortie standard.
Dans cet exercice, il est demandé dans premier temps de dessiner à la main sur papier toutes les instances accessibles depuis les arguments de l'appel de fonction ajoute_valeurs_cellules(cell41, cell42)
dans le programme disponible ici et affiché ci-dessous :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Ensuite, il est demandé d'installer le module traceur
sur votre machine (exercice Installation d'un module) et d'exécuter test_traceur.py
pour vérifier votre dessin ainsi que pour vous assurer d'avoir un module traceur
opérationnel qui sera essentiel pour la suite des travaux pratiques. Si vous ne disposez pas de terminology
il vous faudra modifier les annotations de fonctions dans test_traceur.py
comme indiqué ci-dessus.
Correction
Cliquez ici pour révéler la correction.
Le résultat du traceur
est le suivant :
Sur ce schéma, les couleurs ont la sémantique suivante :
- vert : les arguments donnés à la fonction avec leur nom. Ces derniers sont des variables locales à la fonction, donc des références vers des instances ;
- gris : les instances avec leur type et leur contenu ;
- rose : l'instance
None
.
Nous retrouvons sur ce schéma les deux arguments cell1
et cell2
donnés à la fonction ajoute_valeurs_cellules
.
cell1
est une référence vers une cellule dont :
- la
valeur
est une référence vers l'entier41
; - le
suivant
est une référence vers une autre cellule, qui se trouve être la même que celle référencée parcell2
.
cell2
est une référence vers une cellule dont :
- la
valeur
est une référence vers l'entier42
; - le
suivant
est une référence versNone
.
Tracer les variables et les instances qui nous intéressent
Il est également possible d'utiliser le traceur
pour afficher l'instance de notre choix ainsi que toutes les instances accessible depuis celle-ci.
Pour cela il faut utiliser la fonction display_instance
du module traceur
.
Il est maintenant demandé de lire la documentation de cette fonction dans votre interpréteur interactif à l'aide de la fonction help
puis de jouer avec.
Correction
Cliquez ici pour révéler la correction.
Voici un petit exemple d'utilisation de la fonction display_instance
:
1 2 3 4 5 6 7 8 9 |
|
Ce programme n'affiche pas d'image (visualize=False
) et spécifie le nom du fichier image qui sera créé (image_name="my_nice_instance"
sachant que l'extension .svg
est ajoutée automatiquement par le traceur
) dont voici le contenu :
Enfin, il est possible d'utiliser le traceur
pour afficher un ensemble de variables, donc un ensemble de références, ainsi que toutes les instances accessibles depuis ces variables.
Pour cela il faut utiliser la fonction display_vars
du module traceur
.
Il est maintenant demandé de lire la documentation de cette fonction dans votre interpréteur interactif à l'aide de la fonction help
puis de jouer avec.
Correction
Cliquez ici pour révéler la correction.
Voici un petit exemple d'utilisation de la fonction display_vars
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Ce programme n'affiche pas d'image (visualize=False
) et spécifie le nom du fichier image qui sera créé (image_name="my_nice_vars"
sachant que l'extension .svg
est ajoutée automatiquement par le traceur
) dont voici le contenu :
Les variables, qui sont des références, sont affichées en vert.
Le traceur ne peut pas retrouver le nom d'une variable à partir d'une référence quelconque.
C'est pourquoi ces derniers doivent être spécifiés explicitement via la création d'instances de la classe traceur.Variable
(c'est le premier paramètre du constructeur).