web-dev-qa-db-fra.com

Python argparse groupe mutuel exclusif

Ce dont j'ai besoin c'est:

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

J'ai essayé ça mais ça ne marche pas. Est-ce que quelqu'un peut 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!

74
Sean

add_mutually_exclusive_group ne rend pas un groupe entier mutuellement exclusif. Cela rend les options 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 jeu 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 de la sous-commande 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')

# 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')

Essaye-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(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.

89
Jonathan

Bien que réponse de Jonathan convient parfaitement aux options complexes, il existe une solution très simple qui fonctionnera pour les cas simples, par exemple. 1 option exclut 2 autres options comme dans

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 le ferais:

parser = argparse.ArgumentParser()

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

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

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

Alors maintenant, je lance parser.parse_args() et vérifie son contenu:

args = parser().parse_args()

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

Bien sûr, ce petit bidouillage ne fonctionne que pour des cas simples et il deviendrait un cauchemar de vérifier toutes les options possibles si vous avez plusieurs options et groupes mutuellement exclusifs. Dans ce cas, vous devriez casser vos options pour commander des groupes comme suggéré par Jonathan.

26
Oz123

Il existe un correctif python (en 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. Donc, usage pourrait ressembler à:

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

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

Dans argparse, les groupes d'actions n'affectent pas l'analyse. Ils ne sont qu'un outil de formatage help. Dans le help, les groupes mutuellement exclusifs n'affectent que la ligne usage. Lors de l'analyse, parser utilise les groupes mutuellement exclusifs pour construire un dictionnaire des conflits potentiels (a ne peut pas se produire avec b ou c, b ne peut pas se produire avec a, etc.), puis génère une erreur en cas de conflit.

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 a et b ont des valeurs différentes de la valeur par défaut), et créez votre propre erreur. Vous pouvez même utiliser le mécanisme d'erreur du parseur.

parser.error('custom error message')
4
hpaulj