Transformer un calcul de notebook en script testé
Ton calcul fonctionne dans une cellule, mais tu veux le relancer sans dépendre
des cellules exécutées avant. On va mettre le calcul dans un script, un fichier
.py que Python peut exécuter, l’appeler à partir d’un autre et vérifier un cas
choisi à la main.
Tu travailles ici à rendre un traitement relançable avec les mêmes fichiers, un geste du bloc BC01.
Le cas est fictif : quatre préparations ont duré 8, 12, 15 et 5 minutes. On compte celles terminées en 12 minutes ou moins. Avant de lancer le code, écris le résultat attendu et les durées qui comptent. La durée exactement égale à la limite doit-elle compter ?
| Ce que tu veux vérifier | Où travailler |
|---|---|
| Relancer le calcul hors du notebook | Du début à « Faire tourner les trois fichiers ensemble » |
| Faire échouer puis réparer un test | « Vérifier une règle » |
| Écrire un cas de test qui détecte vraiment une erreur | « Pour aller plus loin » puis « Changer les données » |
| Contrôler ce qui se passe à l'import | « Essai facultatif » |
Exécute le code au fil de la fiche. Si tu lis depuis plus de vingt minutes sans avoir modifié puis relancé un des trois fichiers, ferme la page et reprends l'essai en cours.
Si tu utilises un notebook avec le formateur
Le notebook compagnon à télécharger reprend les trois mêmes scripts dans un dossier temporaire distinct, sans installation supplémentaire. Il guide la prédiction, l’exécution, l’échec volontaire et le retour aux tests réussis. Exporte les scripts et sauvegarde le notebook avant de quitter : le stockage d’une session peut être éphémère. Le compagnon est vérifié localement ; son utilisation dans Colab reste à observer avec le formateur, sans choix d’environnement imposé.
Faire tourner les trois fichiers ensemble
Crée un dossier de travail vide, puis télécharge ces trois fichiers dans ce même dossier :
mesures.py, la fonction de calcul ;rapport.py, les données et l’affichage ;test_mesures.py, les deux tests.
Tu peux aussi les créer avec les blocs ci-dessous. Garde les noms exacts, sans
ajouter .txt ; ne nomme pas ton fichier unittest.py.
Un module est ici un fichier Python dont on peut réutiliser une fonction.
Dans mesures.py, la fonction reçoit les données et la limite :
def compter_dans_delai(durees, limite):
nombre = 0
for duree in durees:
if duree <= limite:
nombre += 1
return nombreCe calcul attend des durées entières positives ou nulles et une limite de même
nature. La vérification de données importées n’est pas traitée dans cet essai.
return renvoie le nombre à celui qui appelle la fonction, sans l’afficher.
Dans rapport.py, on importe cette fonction puis on lui donne les valeurs :
from mesures import compter_dans_delai
def main():
durees = [8, 12, 15, 5]
limite = 12
resultat = compter_dans_delai(durees, limite)
print(f"Préparations de {limite} minutes ou moins : {resultat} sur {len(durees)}")
if __name__ == "__main__":
main()from mesures import ... cherche la fonction dans mesures.py placé à côté.
Le dernier if lance main() quand tu exécutes directement rapport.py ;
importer ce fichier depuis un autre programme ne lance pas son affichage.
Ouvre un terminal, la fenêtre où tu saisis les commandes, dans ce dossier. Vérifie la commande qui lance Python 3 déjà installé, puis utilise-la pour les étapes suivantes. Ces commandes vont dans le terminal, pas dans une cellule Python :
| Poste | Vérifier Python | Lancer le rapport |
|---|---|---|
Windows, si le lanceur py est disponible |
py -3 --version |
py -3 rapport.py |
macOS ou Linux, si python3 est disponible |
python3 --version |
python3 rapport.py |
Si la commande est introuvable, demande à utiliser l’environnement prévu avec
le formateur. Aucun outil supplémentaire n’est nécessaire à ces fichiers.
Si Python ne trouve pas mesures, vérifie que les trois fichiers sont dans le
même dossier, que le terminal y est ouvert et que le nom est mesures.py,
pas mesures.py.txt. Enregistre tes modifications avant de relancer.
Compare l’affichage à ta prédiction : Préparations de 12 minutes ou moins : 3 sur 4.
Ferme le terminal, rouvre-le dans le même dossier et relance. Le script apporte
ses données à chaque exécution ; une ancienne cellule de notebook n’intervient pas.
Vérifier une règle
Dans test_mesures.py, voici deux cas dont on connaît le résultat sans appeler
la fonction pour fabriquer la réponse attendue :
import unittest
from mesures import compter_dans_delai
class TestCompterDansDelai(unittest.TestCase):
def test_duree_egale_a_la_limite_comptee(self):
self.assertEqual(compter_dans_delai([11, 12, 13], 12), 2)
def test_liste_vide(self):
self.assertEqual(compter_dans_delai([], 12), 0)
if __name__ == "__main__":
unittest.main()unittest est fourni avec Python. Ce fichier regroupe les cas dans une classe ;
garde cette structure pour l’essai. assertEqual compare le résultat obtenu
au résultat attendu. Les noms commençant par test_ permettent de retrouver
les vérifications à exécuter.
Lance python3 -m unittest -v test_mesures ; sous Windows avec le lanceur,
utilise py -3 -m unittest -v test_mesures. Le résultat doit indiquer 2 tests
et OK. Si tu lis « Ran 0 tests », tu n’as pas vérifié ces deux cas.
Maintenant, dans mesures.py, remplace seulement <= par <. Prédis quel test
va échouer, puis relance les tests. Un échec est attendu ici : la durée 12 ne
compte plus et le premier cas obtient 1 au lieu de 2. Rétablis <= et vérifie
le retour à OK. Un test qui passe ne prouve que les cas effectivement testés.
Pour aller plus loin : repérer un test qui se donne raison
Fais une copie du dossier de travail, puis ouvre le terminal dans cette
copie. Dans le fichier copié, remplace seulement le premier
assertEqual de test_mesures.py par ces deux lignes, au même retrait :
attendu = compter_dans_delai([11, 12, 13], 12)
self.assertEqual(compter_dans_delai([11, 12, 13], 12), attendu)Prédis ce qui va se passer si tu remplaces de nouveau <= par < dans
mesures.py de cette copie. Lance les tests avec la même commande. Est-ce
qu’un résultat OK permet cette fois de repérer la durée oubliée ? Explique
comment la valeur attendu a été obtenue, puis rétablis le premier test avec
sa réponse calculée à la main. Relance avant de corriger mesures.py.
Vérifier ce que ce test contrôle vraiment
Avec <, les deux appels renvoient 1 : le test compare 1 à 1 et passe. La
liste vide passe aussi ; les deux tests affichent donc OK, malgré la règle
incorrecte. Le nom attendu ne rend pas cette valeur indépendante du calcul.
Avec l’attendu manuel 2, le premier test échoue à nouveau : 1 n’est pas 2.
Rétablis enfin <= dans la copie et vérifie les deux tests OK. Pour la
suite, rouvre le terminal dans ton dossier initial, avec la règle inclusive et
l’attendu manuel conservés.
Changer les données, puis ajouter ton propre contrôle
Dans rapport.py, remplace la limite par 8. Écris ta prédiction, relance, puis
explique pourquoi les tests existants ne changent pas : ils fournissent leurs
propres données et leur propre limite à la fonction.
Ajoute ensuite un test pour un nouveau relevé : [0, 7, 7, 9], limite 7.
Place cette nouvelle méthode dans la classe, au même retrait que les deux
autres, avant le dernier if. Choisis son nom et calcule la réponse attendue
à la main. N’obtiens pas cette
réponse en exécutant la fonction testée. Lance les tests et explique pourquoi
les deux durées identiques doivent chacune compter.
Si tu ne sais pas où ajouter ce test, place-le dans la classe
TestCompterDansDelai, avant le dernier if, avec le même retrait que les
deux méthodes dont le nom commence par test_.
Comparer après tes modifications
Le rapport avec limite 8 compte deux préparations sur quatre : 8 et 5. Le nouveau relevé avec limite 7 doit compter trois durées : 0, 7 et 7. Ta suite doit maintenant exécuter trois tests. Ce cas complète ceux fournis, il ne valide pas toutes les formes de données possibles.
Garde les trois fichiers, ta prédiction et la sortie d’échec volontaire suivie de la sortie corrigée. Montre dans quel fichier tu changes la règle, dans lequel tu changes les données du rapport et dans lequel tu ajoutes un cas.
Essai facultatif : importer sans lancer le rapport
Pour cet essai, repars des trois fichiers fournis, avant tes modifications, dans
une nouvelle copie du dossier. Ouvre le terminal dans cette copie. Tu veux
réutiliser rapport.main sans afficher le rapport dès son import.
Prédis combien de lignes chacune de ces commandes affichera, puis lance-les
séparément. -c demande à Python d'exécuter le code entre guillemets ; chaque
commande ouvre une nouvelle exécution Python.
python3 -c "import rapport"
python3 -c "import rapport; rapport.main()"Sous Windows avec le lanceur, remplace python3 par py -3 dans ces deux
commandes. Explique la différence entre importer le module rapport et
appeler sa fonction main.
Dans rapport.py de la copie, remplace seulement les deux dernières lignes
(if __name__ == "__main__": et son appel indenté) par main() sans retrait.
Enregistre. Prédis de nouveau les deux affichages, puis rejoue les commandes.
Lance aussi python3 -m unittest -v test_mesures (ou
py -3 -m unittest -v test_mesures). Ces tests permettent-ils de repérer
l'affichage déclenché à l'import ? Retrouve le fichier qu'ils importent.
Comparer l'import, l'appel et les tests
Dans la version fournie, l'import n'affiche aucune ligne ; l'appel explicite à
rapport.main() en affiche une. Sans la condition finale, l'import exécute
main() : la première commande affiche une ligne et la seconde deux, car
elle appelle ensuite la fonction une autre fois.
Les deux tests restent OK : ils importent mesures, pas rapport, et
contrôlent le calcul, pas le déclenchement de l'affichage. Leur réussite ne
vérifie donc pas ce nouveau comportement. Un module peut exécuter des
instructions lors de son import ; ici, la condition finale réserve l'appel
à main() à l'exécution directe de rapport.py.
Rétablis les deux lignes finales d'origine dans la copie, puis vérifie : import sans affichage, appel explicite avec une ligne, tests OK. Garde ta prédiction et ce qui s'est passé. Termine dans le dossier initial, laissé intact par cet essai.