Initiation au Lua avec Scribunto/Mise au point d'un module

Début de la boite de navigation du chapitre

Dans le chapitre précédent, nous avons vu suffisamment de notions pour commencer à faire des petits modules faciles à utiliser. Bien souvent les modules ne seront pas nécessairement petits et faciles à utiliser. Leurs mises au point risque d’être délicates à faire. Par conséquent, avant d'aller plus loin dans l'étude du Lua avec Scribunto, nous allons consacrer ce chapitre à l'étude des moyens dont nous disposons pour faciliter la mise au point des modules.

Mise au point d'un module
Icône de la faculté
Chapitre no 2
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Premières notions
Chap. suiv. :Tables et fonctions

Exercices :

Recherche d'erreurs
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : Mise au point d'un module
Initiation au Lua avec Scribunto/Mise au point d'un module
 », n'a pu être restituée correctement ci-dessus.

Augmenter la lisibilité du programme se trouvant dans le module

modifier

Une première façon de rendre un programme plus facile à mettre au point est de l'écrire de façon à ce qu’il soit facile à relire par nous-même ou par quelqu’un d'autre. Par nous-même, car même si l’on a l'impression, sur le moment, de bien savoir ce qu’il contient, il se peut que l’on soit amené à y revenir après plusieurs mois et là, on risque d’avoir du mal à retrouver comment il fonctionne. Par les autres, car les modules écrits sur un des projets Wikimédia peuvent être améliorés par d'autres utilisateurs.

L'amélioration de la lisibilité d'un programme se base sur trois techniques:

L'indentation

modifier

C'est le fait de décaler vers la droite un bloc d'instructions pour le rendre plus lisible. On décalera vers la droite les instructions se trouvant à l'intérieur d'une fonction, d'une structure if condition then instruction end, et les autres structures de contrôle que nous verrons plus tard.

Par exemple, dans le Module:Traduction multilingue, si nous avions écrit :

local p = {}

function p.traduit(frame)
if frame.args[2] == "Anglais" then
if frame.args[1] == "Lundi" then return "Monday" end
if frame.args[1] == "Mardi" then return "Tuesday" end
if frame.args[1] == "Mercredi" then return "Wednesday" end
if frame.args[1] == "Jeudi" then return "Thursday" end
if frame.args[1] == "Vendredi" then return "Friday" end
if frame.args[1] == "Samedi" then return "Saturday" end
if frame.args[1] == "Dimanche" then return "Sunday" end
end
if frame.args[2] == "Espagnol" then
if frame.args[1] == "Lundi" then return "Lunes" end
if frame.args[1] == "Mardi" then return "Martes" end
if frame.args[1] == "Mercredi" then return "Miércoles" end
if frame.args[1] == "Jeudi" then return "Jueves" end
if frame.args[1] == "Vendredi" then return "Viernes" end
if frame.args[1] == "Samedi" then return "Sàbato" end
if frame.args[1] == "Dimanche" then return "Domingo" end
end
end

return p

Le programme aurait, tout de même, bien fonctionné mais aurait été moins lisible.

Les noms de variable explicites

modifier

Le Lua, ainsi que la plupart des langages de programmation, permettent d'écrire les variables en utilisant plusieurs caractères. On donnera donc aux variables un nom qui exprimera ce qu'elles contiennent. Par exemple, une variable destinée à mémoriser un salaire s'appellera salaire.

Les commentaires dans le programme

modifier

Nous n'en n'avons pas parlé pour raison pédagogique, au chapitre précédent, car les programmes étaient petits et des commentaires les auraient alourdis inutilement. Mais il est possible et même fortement conseillé, quand les programmes deviennent plus longs, de rajouter des commentaires à l'intérieur même des programmes pour expliquer ce que chaque partie du programme fait.

Un petit commentaire tenant sur une ligne se fera en commençant par mettre un double tirets --. On peut même mettre un commentaire après une instruction.

Par exemple, dans le Module:Faire part, on aurait pu rajouter des commentaires sur ce que réalisent les fonctions :

local p = {}

function p.naissance(frame)	-- Faire part de naissance
	return "Nous avons la joie de vous annoncer la naissance de " .. frame.args[1] .. "."
end

function p.mariage(frame)	-- Faire part de mariage
	return "Nous sommes heureux de vous annoncer le mariage de " .. frame.args[1] .. " et " .. frame.args[2] .. "."
end

function p.deces(frame)		-- Faire part de décès
	return "Nous sommes au regret de vous annoncer le décès de " .. frame.args[1] .. "."
end

return p

On peut aussi imaginer avoir besoin de plusieurs lignes pour faire un commentaire. Pour cela, on commencera le commentaire par --[[ et on le terminera par ]].

Par exemple pour Module:Exemple simple on aurait pu écrire :

local p = {}
 
function p.Salutation() -- [[mon commentaire ………………………………………………
 de plusieurs lignes]] 
    return "Coucou, c’est moi !"
end
 
return p

L'éditeur Scribunto

modifier
 
Figure 1
  Pour que les numéros de ligne de l'éditeur apparaissent, il faut cocher l'option « Activer la barre d'outils d'édition » dans les préférences de modification

Étudions de plus près l'éditeur dont nous disposons dans l’extension Scribunto. Nous remarquons, tout d’abord, que chaque ligne est numérotée. Lorsque nous écrivons une ligne, celle-ci se détache sur un fond légèrement grisé. Nous le voyons à la figure 2, ligne 16 où se trouve le curseur. L'éditeur dispose d'une première correction pour détecter les erreurs grossières dans la syntaxe des instructions. Si nous n'écrivons pas correctement une instruction, le numéro en début de ligne se retrouve précédé d'une croix dans un carré rouge. Nous le voyons, par exemple, dans la figure 1, ligne 16. Si nous regardons, de plus près la ligne 16, nous verrons que nous avons oublié de mettre le mot-clé then qui doit obligatoirement se trouver dans une structure if condition then instructions end. Mieux que cela, si un carré rouge avec croix apparaît devant un numéro de ligne, nous pouvons avoir une indication sur le type d'erreur en promenant le curseur dessus (nous voulons dire par là, que nous pointons le carré rouge avec le curseur sans toutefois cliquer dessus). Dans notre exemple, figure 1, nous avons le message « [16:33] 'then' expected near 'return' », ce qui signifie que ligne 16, position 33, then est attendu avant return.

Nous remarquons aussi, en début de certaines lignes, un symbole ▼ juste après le numéro de ligne. Ce caractère permet de masquer un bloc d'instructions. Par exemple, si nous cliquons sur le ▼ de la ligne 3 ou est déclarée la fonction p.traduit, nous voyons disparaître toutes les instructions se trouvant entre cette déclaration et le end indiquant la fin de l'écriture du contenu de la fonction. Si nous cliquons sur le ▼ de la ligne 4, nous verrons disparaître toutes les instructions du bloc if. L'utilité de cette fonctionnalité est double. On peut ainsi masquer certaines parties du programme sur lesquelles on n’est pas en train de travailler. On peut aussi, dans un programme, où il y a beaucoup de structures emboîtées et par conséquent beaucoup de end, s'assurer que l’on ne s'est pas « emmêlé les pinceaux » avec les end.

Une autre particularité intéressante de l'éditeur est que lorsque l’on clique juste après une parenthèse, un crochet ou une accolade ouvrante ou fermante, nous voyons un léger encadrement sur la parenthèse, le crochet ou l'accolade fermante ou ouvrante correspondante. Cela peut être utile dans les expressions ayant beaucoup de parenthèses, crochets et accolades pour éviter les erreurs.


 
Figure 2

Intéressons-nous maintenant à ce qui apparaît sous le cadre de visualisation. Nous n'allons pas nous intéresser à ce qui est juste en dessous du cadre de visualisation, car il n'y a là rien de bien nouveau. Nous allons nous intéresser à ce qui se trouve plus bas dans le cadre noté « Aperçu de la page avec ce modèle » (voir figure 2). En effet, nous allons pouvoir, avec cet aperçu, voir ce que va donner le module avant même de devoir l'enregistrer. Il est possible, grâce à ce cadre, de faire l'écriture et la mise au point complète du module sans faire une seule édition.

Pour cela, enregistrez tout d’abord dans une page — par exemple Bac à sable — la commande {{#invoke:''nom du module''|''nom de la fonction''|"arguments"}} concernant votre module. Dans le cadre « Aperçu de la page avec ce modèle » (voir ci-contre), écrivez le titre de la page où le module est invoqué (ici la page « Bac à sable »). Enfin, cliquez sur « Afficher l'aperçu ». L'aperçu de la page où vous avez invoqué votre module (ici l'aperçu de Bac à sable) s'affiche en haut de la page. Si ce n’est pas correct, vous pouvez corriger le module et cliquer à nouveau sur « Afficher l'aperçu » autant de fois que vous voulez, jusqu’à ce que le module soit au point. Une fois le module au point, vous pouvez cliquer sur le bouton « Enregistrer » et le module sera édité.

Le traitement des erreurs de script

modifier

Après avoir corrigé toutes les erreurs indiquées par l'éditeur, nous ne sommes peut-être pas au bout de nos peines. En essayant le programme, nous voyons apparaître le charmant message : « Erreur de script : vous devez spécifier une fonction à appeler. »

Cela signifie que nous avons malgré tout fait une erreur que l'éditeur n'a pas décelée mais qui rend l'exécution du programme impossible.

Avec l'expérience, nous pouvons éviter les principales erreurs de script. En attendant d'acquérir cette expérience, nous nous contenterons d'énumérer les principales situations qui provoquent une erreur de script.

Voici une liste à compléter éventuellement :

  • Utilisation d'une variable en croyant qu'elle contient un certain type de données, alors qu'elle en contient un autre. Exemple : comparaison d'une variable contenant une chaîne de caractères avec un nombre.
  • Utilisation d'une instruction en dehors du contexte où elle devrait être normalement utilisée. Par exemple, emploi de frame.args[1] en dehors de la fonction qui devrait normalement recueillir l'argument.
  • La fonction appelée n'existe pas. Vous avez, peut-être, fait une faute d'orthographe en écrivant son nom ou simplement oublié p. en début de nom.
  • Opération avec une variable, qui est bien du bon type, mais que l’on n'a pas initialisée et qui est donc vide au moment où on l'utilise.
  • Peut éventuellement être produit par l'oubli de l'instruction return dans une fonction (selon comment est utilisée la fonction).

Lorsqu'une erreur de script se produit, vous pouvez avoir une première indication sur la provenance de cette erreur en cliquant sur le message : « Erreur de script : vous devez spécifier une fonction à appeler. » (bien qu’il soit rouge et non bleu). L'indication vous permettra, peut-être, de corriger rapidement l'erreur.

Si, malgré tout, l'erreur de script continue à apparaître et que vous ne voyez pas d'où elle provient, vous pouvez utiliser l'astuce suivante :

Vous mettez -- progressivement au début des lignes, en commençant par celles qui paraissent les plus douteuses, jusqu'à ce que l'erreur de script disparaisse. Ces lignes commençant par -- seront alors interprétées comme étant des commentaires et ne pourront plus provoquer d'erreur de script. Vous pourrez ainsi repérer la ligne qui provoque l'erreur de script.

La recherche d'une erreur dans le programme

modifier

Vous avez écrit un module. L'éditeur n'a pas détecté d'erreur et lorsque vous lancez l'exécution, vous n'avez pas le message : Erreur de script. Le problème, c’est que ce que vous fournit le programme n’est pas conforme à votre attente. Vous avez commis une erreur en écrivant le programme ! Vous essayez donc, dans un premier temps, de relire ce que vous avez écrit pour essayer de comprendre pourquoi cela ne marche pas. Au bout d'un certain temps de réflexion, vous vous rendez à l'évidence, vous n'arrivez pas à comprendre pourquoi cela ne marche pas. Nous allons donc étudier, dans ce paragraphe, des moyens dont nous disposons pour faciliter la recherche de l'erreur.

Introduction d'une variable espion

modifier

Nous avons d’abord une technique simple qui consiste à introduire dans le programme une variable supplémentaire, que l’on appellera rapport par exemple, dans laquelle vous allez, en certains points du programme, concaténer le contenu d'autres variables. À la fin de la fonction, au lieu de retourner la variable prévue, on retournera la variable rapport qui nous fournira ainsi une information sur le contenu des variables en certains points du programme et nous permettra de localiser plus précisément dans quelle partie se trouve l'erreur. Une fois que nous avons localisé de façon plus précise la partie du programme défaillante, nous pouvons recommencer en concaténant, dans notre variable rapport, plus d'informations sur la partie fautive. Et ainsi de suite jusqu'à repérer l'instruction qui est la cause de nos soucis.

Console de débogage

modifier

Lorsque nous sommes en mode modification dans un module, nous avons vu que nous avions un certain nombre de possibilités. Si nous continuons à descendre dans la page, tout en bas, nous découvrons un encadré noté Console de débogage représenté ci-dessous :

 

Nous allons étudier comment cela fonctionne.

Nous commencerons avec le premier exemple dans le premier chapitre, c'est-à-dire la fonction p.Salutation dans le Module:Exemple simple.

Après s'être mis en modification et être descendu jusqu'à la console de débogage, taper à l'intérieur de celle-ci :

=p.Salutation()

puis appuyer sur la touche « Entrée ». Nous voyons alors que ce que l’on a écrit remonte au-dessus de la zone grisée. Puis après un léger temps d'attente apparaît, toujours au-dessus de la zone grisée : « Coucou, c’est moi ! »

Nous avons donc pu tester notre programme. À ce niveau, si quelque chose s'était mal passé, nous aurions eu un message d'erreur nous indiquant la nature de l'erreur et la ligne où l'erreur s'est produite.


Si nous avions tapé :

=p.Salutation

sans les accolades, nous aurions eu comme réponse : function. Nous pouvons avoir ainsi la nature des variables se trouvant dans le programme.


On aurait pu aussi taper :

print(p.Salutation())

ce qui est équivalent à =p.Salutation().


On peut même se servir de la console de débogage comme calculatrice. En effet, si l’on rentre :

=2+3

Elle nous répond 5.


Faisons maintenant une petite expérience et rajoutons la ligne : mw.log("Il fait beau !") dans notre programme ainsi :

local p = {}

function p.Salutation()
	mw.log("Il fait beau !")
	return "Coucou, c’est moi !"
end

return p

Dans la console de débogage tapons à nouveau :

=p.Salutation()

Après nous avoir prévenu que nous avons modifié le programme nous obtenons :

Il fait beau !
Coucou, c’est moi !

mw.log est une commande qui nous permet de transmettre des messages à la console de débogage.

L'intérêt de la fonction mw.log sur l'instruction return est que la fonction mw.log ne nous fait pas sortir du programme comme return lorsqu'elle est utilisée. On va donc pouvoir utiliser la fonction mw.log en plusieurs points du programme pour ramener plusieurs informations visibles sur la console de débogage. On peut ainsi construire tout un rapport d'exécution du programme qui apparaîtra sur la console de débogage et nous permettra ainsi de mettre au point le programme.


Ci-dessous, nous représentons la console de débogage après avoir tapé toutes les opérations décrites ci-dessus :

 


Le programme précédent était simple à étudier car c’était un programme sans paramètre.

Compliquons un peu les choses en étudiant maintenant la fonction p.traduit se trouvant dans le Module:Autre exemple.

Cette fonction, pour fonctionner, doit recevoir en argument un jour de la semaine. Pour parvenir à transmettre cet argument, nous devons taper dans la console de débogage :

frame = mw.getCurrentFrame()

puis « Entrée ». Le message tapé remonte au dessus de la zone grisée. Nous tapons ensuite :

newFrame = frame:newChild{ args = { 'Jeudi' }}

puis « Entrée ». Le second message tapé remonte au dessus de la zone grisée. Nous tapons alors :

=p.traduit( newFrame )

Puis « Entrée ». Le troisième message tapé remonte au dessus de la zone grisée mais, cette fois, apparaît en plus :

Thursday

Qui est bien la traduction de jeudi en anglais.


Ci-dessous, nous représentons la console de débogage après avoir tapé toutes les opérations décrites ci-dessus :

 


Attention, la série des deux commandes :

frame = mw.getCurrentFrame()
newFrame = frame:newChild{ args = { 'Jeudi' }}

doit être retapée si vous modifiez le programme pour faire des essais (introduction d'une fonction mw.log par exemple).


Compliquons encore les choses en essayant de faire une simulation de débogage de la fonction p.alerte2 se trouvant dans le Module:Balance.

Supposons que le programme ne marche pas (c'est pas vrai ! mais on fait semblant). Pour essayer de comprendre pourquoi le programme ne marche pas, nous allons visualiser sur la console de débogage le contenu de toutes les variables se trouvant dans le programme (en fait, ici, il n'y en a que deux).

Nous utiliserons donc la fonction mw.log à deux endroits différents pour visualiser les contenus des variables poids et reponse. Le programme sera ainsi complété :

local p = {}

function p.alerte2(frame)
	local poids = tonumber(frame.args[1])
	mw.log("Le poids rentré est ", poids)
	local reponse = "Votre poids est acceptable"
	if poids > 54 then
		reponse = "Attention, vous commencez à grossir !"
	end
	mw.log("Le contenu de la variable reponse est : ", reponse)
	return reponse
end

return p

Dans la console de débogage, nous taperons successivement les trois commandes :

frame = mw.getCurrentFrame()
newFrame = frame:newChild{ args = { '55' }}
print(p.alerte2( newFrame ))

Après ces opérations, la console de débogage se présente ainsi :

 

où nous voyons clairement apparaître le contenu des variables poids et reponse, ce qui nous permettra éventuellement de mieux comprendre d'où provient l'erreur (s'il y en avait une).