109 votes

Python argparse groupe mutuel exclusif

Ce dont j'ai besoin, c'est :

pro [-a xxx | [-b yyy -c zzz]]

J'ai essayé mais cela ne fonctionne pas. Quelqu'un pourrait-il m'aider ?

group= parser.add_argument_group('Model 2')
group_ex = group.add_mutually_exclusive_group()
group_ex.add_argument("-a", type=str, action = "store", default = "", help="test")
group_ex_2 = group_ex.add_argument_group("option 2")
group_ex_2.add_argument("-b", type=str, action = "store", default = "", help="test")
group_ex_2.add_argument("-c", type=str, action = "store", default = "", help="test")

Merci !

121voto

Jonathan Points 1529

add_mutually_exclusive_group ne rend pas un groupe entier mutuellement exclusif. Il rend les options au sein du groupe mutuellement exclusives.

Ce que vous cherchez, c'est sous-commandes . Au lieu de prog [ -a xxxx | [-b yyy -c zzz]], vous auriez :

prog 
  command 1 
    -a: ...
  command 2
    -b: ...
    -c: ...

Pour invoquer avec le premier ensemble d'arguments :

prog command_1 -a xxxx

Pour invoquer avec le deuxième ensemble d'arguments :

prog command_2 -b yyyy -c zzzz

Vous pouvez également définir les arguments des sous-commandes comme positionnels.

prog command_1 xxxx

Un peu comme git ou svn :

git commit -am
git merge develop

Exemple de travail

# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='help for foo arg.')
subparsers = parser.add_subparsers(help='help for subcommand', dest="subcommand")

# create the parser for the "command_1" command
parser_a = subparsers.add_parser('command_1', help='command_1 help')
parser_a.add_argument('a', type=str, help='help for bar, positional')

# create the parser for the "command_2" command
parser_b = subparsers.add_parser('command_2', help='help for command_2')
parser_b.add_argument('-b', type=str, help='help for b')
parser_b.add_argument('-c', type=str, action='store', default='', help='test')

Testez-le

>>> parser.print_help()
usage: PROG [-h] [--foo] {command_1,command_2} ...

positional arguments:
  {command_1,command_2}
                        help for subcommand
    command_1           command_1 help
    command_2           help for command_2

optional arguments:
  -h, --help            show this help message and exit
  --foo                 help for foo arg.
>>>

>>> parser.parse_args(['command_1', 'working'])
Namespace(subcommand='command_1', a='working', foo=False)
>>> parser.parse_args(['command_1', 'wellness', '-b x'])
usage: PROG [-h] [--foo] {command_1,command_2} ...
PROG: error: unrecognized arguments: -b x

Bonne chance.

42voto

Oz123 Points 4677

Alors que La réponse de Jonathan est parfaitement adapté aux options complexes, il existe une solution très simple qui fonctionnera pour les cas simples, par exemple, une option exclut deux autres options comme dans l'exemple ci-dessous.

command [- a xxx | [ -b yyy | -c zzz ]] 

ou même comme dans la question initiale :

pro [-a xxx | [-b yyy -c zzz]]

Voici comment je m'y prendrais :

parser = argparse.ArgumentParser()

# group 1 
parser.add_argument("-q", "--query", help="query")
parser.add_argument("-f", "--fields", help="field names")

# group 2 
parser.add_argument("-a", "--aggregation", help="aggregation")

J'utilise ici les options données à un wrapper de ligne de commande pour interroger un mongodb. Le site collection peut soit appeler la méthode aggregate ou la méthode find avec deux arguments facultatifs query y fields Vous voyez donc pourquoi les deux premiers arguments sont compatibles et le dernier ne l'est pas.

Donc maintenant je cours parser.parse_args() et vérifier son contenu :

args = parser.parse_args()

if args.aggregation and (args.query or args.fields):
    print "-a and -q|-f are mutually exclusive ..."
    sys.exit(2)

Bien sûr, cette petite astuce ne fonctionne que pour les cas simples et cela deviendrait un cauchemar de vérifier toutes les options possibles si vous avez beaucoup d'options et de groupes mutuellement exclusifs. Dans ce cas, vous devriez diviser vos options en groupes de commandes comme Jonathan l'a suggéré.

4voto

hpaulj Points 6132

Il existe un patch python (en cours de développement) qui vous permettrait de le faire.
http://bugs.python.org/issue10984

L'idée est de permettre le chevauchement de groupes mutuellement exclusifs. Ainsi, usage pourrait ressembler :

pro [-a xxx | -b yyy] [-a xxx | -c zzz]

Modifier le code argparse pour pouvoir créer deux groupes comme ceci était la partie facile. Changer le code usage Le code de formatage a nécessité l'écriture d'un HelpFormatter .

En argparse Les groupes d'action n'affectent pas l'analyse syntaxique. Ils sont juste un help outil de formatage. Dans l'écran help les groupes mutuellement exclusifs n'affectent que le usage ligne. Lors de l'analyse syntaxique, le parser utilise les groupes mutuellement exclusifs pour construire un dictionnaire des conflits potentiels ( a ne peut pas se produire avec b o c , b ne peut pas se produire avec a etc.), puis soulève une erreur si un conflit survient.

Sans ce patch argparse, je pense que votre meilleur choix est de tester l'espace de noms produit par parse_args vous-même (par exemple, si les deux a y b n'ont pas de valeurs par défaut), et soulever votre propre erreur. Vous pouvez même utiliser le mécanisme d'erreur propre à l'analyseur.

parser.error('custom error message')

0voto

Edward Spencer Points 21

Si vous ne voulez pas de sous-parsers, il est actuellement possible de le faire avec des groupes mutuellement exclusifs, mais attention, cela implique d'accéder à des variables privées, alors utilisez-le à vos risques et périls. L'idée est que vous voulez -a pour être mutuellement exclusifs avec -b y -c mais -b y -c ne veulent pas être mutuellement exclusives l'une de l'autre

import argparse
p = argparse.ArgumentParser()

# first set up a mutually exclusive group for a and b
g1 = p.add_mutually_exclusive_group()
arg_a = g1.add_argument('-a')  # save this _StoreAction for later
g1.add_argument('-b')

# now set up a second group for a and c 
g2 = p.add_mutually_exclusive_group()
g2.add_argument('-c')
g2._group_actions.append(arg_a)  # this is the magic/hack

Maintenant, nous avons -a exclusif pour les deux -c y -b .

a = p.parse_args(['-a', '1'])
# a.a = 1, a.b = None, a.c = None

a = p.parse_args(['-a', '1', '-b', '2'])
# usage: prog.py [-h] [-a A | -b B] [-c C]
# prog.py: error: argument -b: not allowed with argument -a

Notez que cela perturbe le message d'aide, mais vous pouvez probablement passer outre, ou simplement l'ignorer parce que vous avez obtenu la fonctionnalité que vous voulez, ce qui est probablement plus important de toute façon.

Si vous voulez vous assurer que si nous utilisons l'un ou l'autre des éléments b et c, nous devons utiliser les deux, il suffit d'ajouter l'attribut required=True Le mot-clé arg lors de l'instanciation des groupes mutuellement exclusifs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X