10.6. Manipuler les arguments de la ligne de commande
Python supporte complètement la création de programmes qui peuvent être lancés en ligne de commande, à l'aide d'arguments et de
drapeaux longs ou cours pour spécifier diverses options. Cela n'est nullement spécifique à XML, mais comme ce script fait grand usage du traitement en ligne de commande, il est très à propos d'y faire ici mention.
Il est difficile de parler du traitement en ligne de commande sans aborder la façon dont les arguments sont passés au programme
Python, commencez donc par un petit programme en guise d'introduction.
#argecho.pyimport sys
for arg in sys.argv: print arg
Chaque argument de ligne de commande passé au programme est ajouté à sys.argv, qui est un objet liste. Ici le script affiche chaque argument sur une ligne séparée.
Ce qu'il faut d'abord retenir de l'objet sys.argv est qu'il contient le nom du script que vous appelez. Vous en tirerez avantage plus tard, dans le Chapitre 16, Programmation fonctionnelle. Ne vous en souciez pas pour le moment.
Les arguments de la ligne de commande sont séparés par des espaces et chacun se présente comme un élément distinct dans la
liste sys.argv.
Les drapeaux de la ligne de commande, comme --help, se présentent également comme des éléments propres dans la liste sys.argv.
Pour corser le tout, certains drapeaux de la ligne de commande prennent eux-mêmes des arguments. Par exemple, vous avez ici
un drapeau (-m) qui prend un argument (kant.xml). Aussi bien le drapeau que son argument sont présentés comme des éléments distincts dans la liste sys.argv. Rien n'est fait pour les associer; vous n'obtenez rien de plus qu'une liste.
Comme vous pouvez le voir maintenant, vous disposez indiscutablement de toutes les informations passées à la ligne de commande,
mais, de nouveau, il apparaît que tout ne sera pas forcément facile à utiliser. Pour des programme simples qui ne nécessite
qu'un seul argument et pas de drapeau, vous pouvez simplement utiliser sys.argv[1] pour accéder à l'argument. Aucune honte à avoir; je fais ça tout le temps. Pour des programmes plus complexes, il vous faut
recourir au module getopt.
Tout d'abord, regardez au bas de l'exemple et remarquez que vous appelez la fonction main avec sys.argv[1:]. Rappelez-vous, sys.argv[0] est le nom du script en cours; vous n'avez pas à vous en soucier pour le traitement en ligne de commande, aussi le supprimez-vous
et envoyez-vous le reste de la liste.
C'est ici que se trouve la partie intéressante du traitement. La fonction getopt du module getopt prend trois paramètres : la liste des arguments (que vous obtenez à partir de sys.argv[1:]), une chaîne contenant tous les drapeaux courts possibles acceptés par le programme et une liste des drapeaux plus longs
qui correspondent aux versions courtes. C'est bien confus à première vue, mais l'explication détaillée vient plus bas.
Si un dysfonctionnement survient au moment d'analyser les drapeaux de ligne de commande, getopt déclenche une exception, que vous récupérez ensuite. Comme vous avez indiqué à getopt tous les drapeaux que vous connaissiez, il y a fort à parier que l'utilisateur final a passé des drapeaux de ligne de commande
qui vous sont inconnus.
Comme il est de coutume dans le monde UNIX, quand le script reçoit des drapeaux qu'il ne connaît pas, vous mettez fin au programme de la manière la plus élégante qui
soit, en fournissant un résumé des règles de bon usage. Remarquez que je n'ai pas présenté ici la fonction usage. Vous aurez encore besoin d'ajouter dans un coin quelques lignes de code pour afficher le résumé approprié; ce n'est pas
automatique.
Quels sont donc tous ces paramètres que vous passez à la fonction getopt ? Et bien, le premier est simplement la liste brute des arguments et des drapeaux de ligne de commande (à l'exception du
premier élément, le nom du script, que vous avez éliminé avant d'appeler la fonction main). Le deuxième est la liste des drapeaux courts acceptés par le script.
"hg:d"
-h
affiche les règles d'usage
-g ...
utilise le fichier de grammaire ou l'URL spécifié
-d
montre l'information de débogage au cours du traitement
Le premier et le troisième drapeaux fonctionnent de manière autonome; vous les spécifiez ou non et le cas échéant ils exécutent
une action (affichage l'aide) ou changent un état (actionnement du débogage). Au contraire, le deuxième drapeau (-g) doit être suivi par un argument, qui est le nom du fichier de grammaire à analyser. En fait, ce peut être un nom de fichier ou
une adresse web, et vous ne savez pas encore lequel (vous l'apprendrez plus tard); mais vous êtes certains qu'il doit bien
y avoir quelque chose. Aussi le signalez-vous à getopt en ajoutant deux points après g, le second paramètre de la fonction getopt.
Pour compliquer davantage les choses, le script accepte soit des drapeaux courts (comme -h) soit des drapeaux longs (comme --help) et vous voulez que ces derniers effectuent la même chose. D'où l'utilité du troisième paramètre de getopt : spécifier une liste de drapeaux longs qui correspondent aux drapeaux courts du second paramètre.
["help", "grammar="]
--help
affiche les règles d'usage
--grammar ...
utilise le fichier de grammaire ou l'URL spécifié
Trois choses à remarquer ici :
Tous les drapeaux longs sont précédés par deux tirets sur la ligne de commande, mais vous n'avez pas besoin d'inclure ces
tirets quand vous appelez getopt. Ils sont implicites.
Le drapeau --grammar doit toujours être suivi par un argument additionnel, comme pour le drapeau -g. C'est indiqué par un signe égal, "grammar=".
La liste des drapeaux longs est plus courte que la liste des drapeaux courts, parce que le drapeau -d n'a pas de version longue correspondante. Très bien; seul -d activera le débogage. Mais l'ordre des drapeaux courts et des drapeaux longs a besoin d'être le même, aussi vous devez d'abord
spécifier tous les drapeaux courts qui possèdent un drapeau long correspondant, puis tout le reste des drapeaux courts.
Encore confus ? Tournez-vous vers le code en question et voyez si, dans ce contexte, cela fait sens.
Exemple 10.23. Manipuler les arguments de la ligne de commande dans kgp.py
def main(argv):
grammar = "kant.xml"try:
opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts: if opt in ("-h", "--help"):
usage()
sys.exit()
elif opt == '-d': global _debug
_debug = 1
elif opt in ("-g", "--grammar"):
grammar = arg
source = "".join(args)
k = KantGenerator(grammar, source)
print k.output()
La variable grammar gardera une trace du fichier de grammaire utilisé. Vous l'initialisez ici au cas où elle ne serait pas spécifiée à la ligne
de commande (en utilisant les drapeaux -g ou --grammar).
La variable opts que vous récupérez à partir de getopt contient une liste de tuples : flag et argument. Si le drapeau ne prend pas d'argument, alors arg vaudra simplement None. Cela facilite le parcours des drapeaux.
La fonction getopt valide les drapeaux de ligne de commande qui sont acceptables, mais elle ne fait aucune sorte de conversion entre les drapeaux
courts et les drapeaux longs. Si vous spécifiez le drapeau -h, opt contiendra "-h"; si vous spécifiez le drapeau --help, opt contiendra "--help". Aussi avez-vous besoin de tester les deux.
Rappelez-vous, le drapeau -d n'avait pas de drapeau long correspondant; aussi n'avez-vous besoin que de tester la forme courte. Si vous le détectez,
vous déclarez une variable globale à laquelle vous vous référerez plus tard pour afficher les informations de débogage. (Je
l'ai utilisé au cours du développement de ce script. Vous ne pensiez tout de même pas que ces exemples ont fonctionné du
premier coup ?)
Si vous trouvez un fichier de grammaire, indiqué par les drapeaux -g ou --grammar, vous conservez l'argument qui le suit (stocké dans arg) dans la variable grammar, écrasant alors la valeur par défaut que vous aviez initialisée au début de la fonction main.
C'est tout. Vous avez parcouru et traité les drapeaux de la ligne de commande. Ce qui signifie qu'il ne peut rester alors
que des arguments de ligne de commande. Ils sont retournés par la fonction getopt et placés dans la variable args. Dans ce cas, vous les traitez comme une source de données pour l'analyseur. Si aucun argument de ligne de commande n'est
spécifié, args sera une liste vide et source contiendra au final une chaîne vide.