Ligne de commande
Pour l’instant nous appelons nos programmes depuis la ligne de commande en tapant python program.py
(mais nous savons aussi comment utiliser ./program.py
sous Linux).
Dans les deux cas, cela fait appel à l’interpréteur Python en lui donnant le chemin de notre programme en argument. Mais il est possible de renseigner d’autres arguments lors du lancement et ceux-ci seront transmis à notre programme.
Ils seront accessibles sous la forme d’une liste de chaînes de caractères, la liste argv
qu’il faudra importer depuis le module sys
(un module qui gère les informations sur le système).
À l’utilisation, nous recevons bien les différents arguments passés au programme.
% python program.py
['program.py']
% python program.py foo bar
['program.py', 'foo', 'bar']
% python program.py 1 2 3
['program.py', '1', '2', '3']
On voit que le premier argument est toujours le nom du programme.
Comme indiqué, il ne s’agit que de chaînes de caractères et il va donc falloir convertir les types lorsque cela est nécessaire. Par exemple avec cette mini-calculatrice.
% python addition.py 3 5
8
% python addition.py 10 -3
7
Mais attention, notre code plantera méchamment si nous ne fournissons pas suffisamment d’arguments.
% python addition.py 1
Traceback (most recent call last):
File "addition.py", line 4, in <module>
b = int(sys.argv[2])
IndexError: list index out of range
En effet, sys.argv
est une liste ordinaire, et si sa taille n’est que de 2, alors elle ne possède pas d’élément à l’index 2.
Pour nous prémunir de ce genre d’erreurs, il faut donc vérifier la taille de la liste avant d’accéder à ses éléments. Et généralement dans ces cas là, on quittera le programme en affichant un message expliquant comment l’appeler.
Pour quitter un programme à tout moment, on peut faire appel à la fonction sys.exit
.
import sys
if len(sys.argv) < 3:
print('Usage: addition.py nb1 nb2')
sys.exit()
a = int(sys.argv[1])
b = int(sys.argv[2])
print(a + b)
À l’utilisation c’est tout de suite plus propre.
% python addition.py 1 2
3
% python addition.py 1
Usage: addition.py nb1 nb2
Pour plus de généricité, on pourrait écrire print(f'Usage: {sys.argv[0]} nb1 nb2')
évitant d’inscrire en dur le nom du programme.
Le premier élément de sys.argv
sera toujours présent, notre programme n’aurait pas pu être appelé sinon.
Sortie standard et sortie d’erreur
Il y a un seul souci avec notre message d’erreur : celui-ci est imprimé sur la sortie standard.
Qu’est-ce que la sortie standard ?
Sur Linux, les programmes qui tournent sont automatiquement reliés à 3 périphériques :
- L’entrée standard, celle qui récupère le texte entré sur le terminal, accessible via
input()
. - La sortie standard, où est affiché par défaut tout ce qui sort du programme sur le terminal (avec
print
par exemple). - La sortie d’erreur, spécifiquement dédiée aux erreurs.
Les sorties standard et d’erreur sont toutes deux affichées par défaut sur le terminal, mais elles sont pourtant différentes.
Dans un shell Bash, il est possible d’utiliser l’opérateur >
pour rediriger le flux de sortie standard vers un fichier, et l’opérateur 2>
pour la sortie d’erreur.
% python addition.py > out 2> err
Si l’on inspecte nos fichiers, on constate bien que out
contient le message d’erreur et que err
est vide.
On aimerait que ce soit l’inverse en cas d’erreur.
En fait, chaque sortie correspond à un fichier ouvert par défaut par le programme.
Pour la sortie standard, il s’agit de sys.stdout
. (standard output).
On peut l’utiliser comme tout autre fichier ouvert en écriture.
>>> import sys
>>> sys.stdout.write('hello\n')
hello
6
>>> print('world', file=sys.stdout)
world
Le 6 qui apparaît n’est que le retour de l’appel à write
(6 caractères ont été écrits).
Et de façon similaire, on a sys.stderr
qui correspond à la sortie d’erreur.
>>> print('error', file=sys.stderr)
error
Bien sûr la différence n’est pas flagrante dans cet exemple, elle le sera si l’on redirige les sorties standard et d’erreur vers des fichiers différents. Ce qui est généralement fait pour la journalisation d’un programme.
% python outputs.py > out 2> err
% cat out
standard output
% cat err
error output
Ainsi, nous pouvons remplacer notre code de traitement d’erreur par le suivant.
if len(sys.argv) < 3:
print(f'Usage: {sys.argv[0]} nb1 nb2', file=sys.stderr)
sys.exit()
Parseur d'arguments
Manipuler sys.argv
ça va quand on a des arguments simples comme deux nombres ici, mais ça devient vite compliqué pour gérer les options passées à un programme.
En effet, il serait difficile de gérer manuellement les arguments d’un appel tel que python cmd.py -v -f pdf --foo=bar --foo2 42 photo.jpg
.
C’est pourquoi des outils existent pour analyser à votre place les arguments, les valider en fonction de ce qui est attendu, et les classer convenablement.
Le module argparse
de la bibliothèque standard propose l’un de ces outils.
argparse
fournit un type ArgumentParser
que l’on peut instancier pour obtenir un parseur d’arguments.
Des méthodes sont ensuite disponibles sur ce parseur pour le personnaliser et préciser les arguments que l’on attend.
Notamment la méthode add_argument
qui permet de demander à gérer un nouvel argument.
Celle-ci accepte de nombreuses options que je ne détaillerai pas ici. Sachez simplement qu’elle attend en premier le nom voulu pour l’agument.
- Si ce nom est de type
-x
alors elle gèrera l’argument comme-x VALEUR
lors de l’appel au programme. - S’il est de type
--abc
, elle gèrera--abc=VALEUR
et--abc VALEUR
. - S’il ne débute pas par un tiret, alors il s’agira d’un argument positionnel du programme.
Un paramètre action
sert à préciser quoi faire de l’argument rencontré.
store_const
permet de simplement stocker la valeur associée à l’argument.store_true
permet de stockerTrue
si l’argument est présent etFalse
sinon.
Une valeur par défaut pour l’argument peut être renseignée avec le paramètre default
.
Par défaut, la valeur de l’argument sera stockée dans l’objet résultant sous le nom de l’argument.
Il est cependant possible de choisir un autre nom pour le stockage à l’aide du paramètre dest
.
Enfin, il est possible d’utiliser le paramètre type
pour convertir automatiquement la valeur d’un argument vers le type voulu.
Voilà par exemple comment nous pourrions traiter les arguments présentés pour la commande plus haut.
% python cmd.py -v -f pdf --foo=bar --foo2 42 photo.jpg
Namespace(verbose=True, format='pdf', foo='bar', foo2=42, file='photo.jpg')
Verbose: True
Format: pdf
% python cmd.py doc.odt
Namespace(verbose=False, format='text', foo=None, foo2=None, file='doc.odt')
Verbose: False
Format: text
Pour plus d’informations au sujet du module argparse
, vous pouvez consulter sa page de documentation : https://docs.python.org/fr/3/library/argparse.html.