Créer des scripts Python
Nous allons voir aujourd’hui comment transformer une fonction en script Python. Pour cela, nous allons utiliser la bibliothèque docopt
qui permet de créer des interfaces en ligne de commande.
Remarque : Peut-être l’année prochaine je vais utiliser la bibliothèque fire
que je ne connais pas encore, mais qui semble plus simple à utiliser que docopt
. Et peut-être que je vais aussi enseigner la programmation objet, car c’est fort pratique pour créer des scripts.
docopt
Le principe de docopt
est que l’on écrit une documentation pour notre script, et docopt
se charge de transformer cette documentation en interface en ligne de commande. Voici un exemple de documentation pour un script qui prend un nombre en argument et qui affiche si ce nombre est pair ou impair :
"""
This script checks if a number is even or odd.
Usage:
odd_even.py [--] <number>
Options:
-h --help Display this help message
<number> The number to check
"""
from docopt import docopt
arguments = docopt(__doc__)
try:
number = int(arguments['<number>'])
except ValueError:
print("The number must be an integer")
print(__doc__)
exit(1)
if number % 2 == 0:
print(f"{number} is even")
else:
print(f"{number} is odd")
Quelques remarques sur ce script :
- Ce script est disponible dans le fichier odd_even1.py dans ce répertoire.
- La documentation est écrite en format
docopt
. C’est un format très simple qui permet de décrire les options et arguments que prend le script. Une option est un argument qui commence par un tiret (et qui correspond à un paramètre « nommé »), et un argument est un paramètre qui ne commence pas par un tiret (et qui correspond à un paramètre « positionnel »). - La fonction
docopt
prend en argument la documentation du script, et retourne un dictionnaire qui contient les valeurs des options et arguments. - La variable
__doc__
contient la documentation du module ou de la fonction dans laquelle elle est utilisée. Dans le cas du script,__doc__
contient la documentation du script. - On peut utiliser ChatGPT ou Copilot pour générer la documentation de notre script.
- L’argument
--
est utilisé pour indiquer que les arguments qui suivent sont des arguments positionnels, et non des options. Cela permet d’éviter les ambiguïtés si un argument commence par un tiret. Par exempleodd_even.py -- -5
est interprété commenumber = -5
, alors que si on utiliseodd_even.py -5
le-5
est interprété comme une option, et le script affiche une erreur. - Malheureusement,
docopt
est fait pour de l’anglais. On peut écrire presque toute la documentation en français, mais les mots-clésUsage
(etOptions
, mais bon…) doit être en anglais. De plus les messages d’erreur générés pardocopt
sont en anglais. Donc au final, il est préférable d’écrire toute la documentation en anglais pour ne pas se retrouver avec un mélange de français et d’anglais.
On peut réaliser ce script simple sans bibliothèque externe. Et pour un cas aussi simple, c’est probablement plus approprié. Cependant, pour des scripts plus complexes, docopt
permet de gérer automatiquement les erreurs de syntaxe des options et arguments, et de générer une aide automatique pour le script.
Voici le même script sans docopt
:
import sys
if len(sys.argv) != 2:
print("Usage: odd_even.py <number>")
exit(1)
try:
number = int(sys.argv[1])
except ValueError:
print("The number must be an integer")
exit(1)
if number % 2 == 0:
print(f"{number} is even")
else:
print(f"{number} is odd")
Ce script est disponible dans le fichier odd_even2.py dans ce répertoire.
Exemple plus complexe
Transformons la fonction table(n, format='d')
qui était à faire en exercice lors de la séance 5 en script Python. Voici la documentation que nous pourrions écrire pour ce script :
"""
Usage:
table.py [--format=<f>] <number>
Print the multiplication table up to <number> in the format <f>
Options:
-h --help Display this help message
<number> The maximal number in the table
--format=<f> The format of the numbers, one of d, x, o, b [default: d]
"""
On peut voir le script complet dans le fichier table.py dans ce répertoire.
Quelques astuces
Pour effacer l’écran
Il existe des commandes pour effacer l’écran dans les terminaux. Par exemple, la commande clear
efface l’écran dans un terminal Unix. On peut utiliser cette commande dans un script Python pour effacer l’écran. Mais on peut aussi imprimer la chaîne de caractères "\033c"
qui efface l’écran dans la plupart des terminaux.
def clear_screen():
print("\033c", end="")
Pour lire la réponse de l’utilisateur
En général on utilise la fonction input
pour lire la réponse de l’utilisateur. Cependant, cette fonction n’est pas très pratique quand on attend une réponse d’une seule lettre.
Pour récupérer une seule lettre, on peut utiliser la bibliothèque readchar
. Voici un exemple de script qui lit une seule lettre sans attendre que l’utilisateur appuie sur la touche Entrée
.
from readchar import readkey, key
def get_key(allowed_keys):
"""
Lit une touche du clavier et retourne la touche si elle est dans la chaîne `allowed_keys`.
Sinon, affiche un message d'erreur et redemande une touche.
"""
while True:
k = readkey()
if k in allowed_keys:
return k
print(k)
print(f"Touche invalide. Les touches autorisées sont : {allowed_keys}.")
Les inconvénients de cette méthode sont :
- Elle ne fonctionne pas dans un notebook Jupyter.
- Elle ne fonctionne pas dans certains terminaux.
- Elle ne laisse pas l’utilisateur effacer la réponse s’il se trompe.
Pour utiliser des couleurs dans le terminal
On peut utiliser différent bibliothèques pour afficher du texte en couleur dans le terminal. Par exemple, la bibliothèque colorama
ou rich
permettent de faire cela.
Mais on peut utiliser des codes de couleurs pour afficher du texte en couleur dans le terminal. Voici un exemple de script qui affiche du texte en couleur :
def color_text(text, color):
"""
Retourne le texte `text` coloré avec la couleur `color`.
"""
colors = {
"black": "\033[30m",
"red": "\033[31m",
"green": "\033[32m",
"yellow": "\033[33m",
"blue": "\033[34m",
"magenta": "\033[35m",
"cyan": "\033[36m",
"white": "\033[37m",
"reset": "\033[0m"
}
c = colors.get(color, "")
return c + text + colors["reset"]
Inconvénients de cette méthode :
- Elle ne fonctionne pas dans un notebook Jupyter.
- Elle ne fonctionne pas dans certains terminaux.