Du notebook au programme#
Cette partie fait passer le code du notebook recette.ipynb dans un
programme, recette.py, lancé depuis un terminal. Elle présente argparse,
qui lit les valeurs du programme sur la ligne de commande. Le programme se
construit par étapes, un commit git par étape. Un TD l’accompagne, le TD 3f ;
il est présenté en fin de page, avec la fonction main, qu’il ajoute en
bonus.
Du notebook à la ligne de commande#
Dans un notebook, les valeurs du programme sont écrites dans une cellule. Changer une valeur oblige à relancer les cellules qui en dépendent, dans l’ordre. Le script du TD 3f reçoit les valeurs sur la ligne de commande, et une seule commande produit la page.
Dans un notebook, une valeur modifiée demande de relancer les cellules qui en dépendent.#
> python recette.py pate_pizza -p 6 -u US
…\sortie\pate_pizza.html : 6 personne(s), unités US
Dans le notebook |
Sur la ligne de commande |
|---|---|
|
|
|
|
|
|
Les arguments d’un script avec argparse#
argparse, de la bibliothèque standard, lit les arguments écrits après le nom
du script : il les vérifie, les convertit, et construit l’aide de --help.
Documentation :
docs.python.org/fr/3/library/argparse.html.
Le script minimal lit un argument, le nom de la recette :
import argparse
analyseur = argparse.ArgumentParser()
analyseur.add_argument("nom")
options = analyseur.parse_args()
print("Recette demandée :", options.nom)
> python recette.py crepes
Recette demandée : crepes
> python recette.py
usage: recette.py [-h] nom
recette.py: error: the following arguments are required: nom
options.nom porte le nom donné à add_argument. Sans argument,
parse_args affiche l’usage et arrête le script, avant le print.
Le programme de la recette lit trois arguments :
analyseur = argparse.ArgumentParser()
# obligatoire ; une valeur de la liste
analyseur.add_argument("nom", choices=recettes_disponibles)
# option ; convertie en entier ; 4 si absente
analyseur.add_argument("-p", "--personnes", type=int, default=4)
# option ; deux valeurs possibles
analyseur.add_argument("-u", "--unites", choices=("SI", "US"), default="SI")
# lit la ligne de commande
options = analyseur.parse_args()
Un argument sans tiret est obligatoire et positionnel. Un argument avec des
tirets est une option, qui a une valeur par défaut. type=int convertit la valeur en
entier et rejette six. choices rejette ce qui n’est pas dans la liste ; la
liste des recettes vient de iterdir() sur le dossier des recettes, comme à
la section 3.4 de recette.ipynb.
Commande |
Ce qu’elle produit |
|---|---|
|
la page de la pâte à pizza, pour 6 personnes, en unités US |
|
|
|
l’aide, construite à partir des |
Un commit par étape#
Le programme se construit par étapes, et chaque étape se termine par un
commit git. Le dépôt est créé dans travail/, avec un fichier .gitignore.
.gitignore est un fichier texte : chaque ligne y nomme un fichier ou un
dossier que git ne suit pas. La ligne sortie/ exclut les pages produites par
le programme, qu’il reproduit à chaque lancement.
Les postes de la salle sont partagés : le nom et l’adresse qui signent les
commits se règlent dans le dépôt, sans --global (page Git et Git
Bash).
git diff affiche les lignes modifiées depuis le dernier commit. VS Code
affiche la même comparaison : l’icône du contrôle de code source, dans la
barre de gauche, ouvre un panneau qui liste les fichiers modifiés, et un clic
sur un fichier ouvre l’éditeur de comparaison, le dernier commit à gauche, le
fichier modifié à droite. Par défaut, cet éditeur ignore les espaces en début
et en fin de ligne, alors que git diff les affiche.
Les trois arguments s’ajoutent sur une branche, arguments, un commit par
argument, puis la branche est fusionnée dans master. VS Code affiche la
branche courante en bas à gauche de la fenêtre, dans la barre d’état.
En bonus du TD 3f : la fonction main#
Les lignes du programme sont placées dans une fonction, main. La fin du
fichier appelle main seulement si le fichier est lancé avec
python recette.py :
# les fonctions utiles, sans changement
def lire_ingredients(chemin):
...
# le programme, dans une fonction : ses lignes sont indentées
def main():
ingredients = lire_ingredients(...)
...
# vrai si le fichier est lancé avec python
if __name__ == "__main__":
main()
Le résultat de python recette.py est le même qu’avant le passage dans
main.
La lecture d’un script par Python#
Lancé ou importé, un fichier est lu de haut en bas, et chaque ligne est
exécutée. Un def crée la fonction sans exécuter son corps : le corps ne
s’exécute qu’à l’appel. __name__ est une variable que Python définit dans
chaque fichier : elle vaut "__main__" dans le fichier lancé, et le nom du
module, ici "recette", dans un fichier importé.
Ligne de |
|
|
|---|---|---|
|
exécutée : |
exécutée : |
|
la fonction est créée |
la fonction est créée |
|
la fonction est créée |
la fonction est créée |
|
|
|
|
exécutée : la page est produite |
ignorée |
Sans le test, main() serait aussi exécutée à l’import : récupérer
lire_ingredients pour la réutiliser ou la tester produirait la page et
lancerait pandoc. Un fichier de tests, par exemple, appelle
lire_ingredients sur un CSV connu et vérifie le résultat ; le projet 7 en
écrit un.
Le dossier du script#
Un chemin relatif part du dossier courant : celui du notebook, ou celui du
terminal qui lance le script. Path(__file__).parent désigne le dossier du
script lui-même, d’où qu’on le lance. L’étape 5 du TD 3f, facultative, range
le code dans src/ et les données dans data/, et fait partir les chemins
des données de ce dossier.
TD de la partie#
TD 3f, dans le dossier
cours3/3f_cli/de l’archive, 45 minutes : écrirerecette.pyà partir du notebook, puis les trois arguments sur une branche, un README et, en bonus, une fonctionmain; un commit par étape. Les étapes 5 et 6, facultatives, rangent le projet ensrc/etdata/, puis l’installent comme une commande. Le guide détaillé du TD est livré dans le même dossier, en PDF, en page HTML et en notebook ; le code se copie depuis la page HTML ou le notebook.
Étape |
Ce qu’on fait |
Commits à la fin |
|---|---|---|
0 |
préparer le dossier |
0 |
1 |
écrire |
1 |
2 |
lire la recette, le nombre de personnes et les unités sur la ligne de commande, sur une branche |
4 |
3 |
écrire un |
5 |
4 (bonus) |
mettre le programme dans une fonction |
6 |
5 (facultatif) |
ranger le code dans |
7 |
6 (facultatif) |
installer le programme comme une commande |
8 |
Le TD 3f est celui du parcours avancé. Le parcours standard fait à la place le TD 3e, une recette en Markdown. Le projet 4 revoit ces notions pour les deux parcours.
Les TD des autres parties sont dans Travaux dirigés de la séance 3.