Argparse ne peut pas lire une chaîne de caractères avec des tirets?

y a-t-il un moyen pour que argparse reconnaisse quoi que ce soit entre deux citations comme un seul argument? Il semble continuer de voir les tirets et en supposant que c'est le début d'une nouvelle option

j'ai quelque chose comme:

mainparser = argparse.ArgumentParser()
subparsers = mainparser.add_subparsers(dest='subcommand')
parser = subparsers.add_parser('queue')
parser.add_argument('-env', '--extraEnvVars', type=str,
                        help='String of extra arguments to be passed to model.')
...other arguments added to parser...

mais quand je cours:

python Application.py queue -env "-s WHATEVER -e COOL STUFF"

il me donne:

Application.py queue: error: argument -env/--extraEnvVars: expected one argument

si je laisse tomber le premier tiret, ça marche très bien, mais c'est un peu crucial que je puisse passer dans une chaîne de caractères avec des tirets. J'ai essayé de l'échapper avec, ce qui lui permet de réussir, mais ajoute le à la chaîne d'arguments. quelqu'un sait-il comment contourner cela? Cela se produit si oui ou non-s est un argument dans l'analyseur.

EDIT: j'utilise Python 2.7.

EDIT2:

python Application.py -env " -env"

fonctionne parfaitement bien, mais

python Application.py -env "-env"

ne fonctionne pas.

Edit 3: On dirait que c'est en fait un bug déjà débattu: http://www.gossamer-threads.com/lists/python/bugs/89529 , http://python.6.x6.nabble.com/issue9334-argparse-does-not-accept-options-taking-arguments-beginning-with-dash-regression-from-optp-td578790.html . C'est seulement en 2.7 et pas dans optparse.

edit 4: le rapport de Bogue ouvert actuel est: http://bugs.python.org/issue9334

36
demandé sur hpaulj 2013-04-23 20:55:18

3 réponses

vous pouvez démarrer l'argument avec un espace python tst.py -e ' -e blah' comme une solution très simple. Tout simplement lstrip() l'option de le remettre à la normale, si vous le souhaitez.

Ou, si le premier "sous-argument" n'est pas un argument valide pour la fonction d'origine, alors vous ne devriez pas besoin de faire quoi que ce soit. C'est-à-dire que la seule raison pour laquelle python tst.py -e '-s hi -e blah' ne fonctionne pas est que -s est une option valide de tst.py .

aussi, le le module optparse , désormais obsolète, fonctionne sans problème.

12
répondu William 2013-04-23 17:13:31

mise à jour de la réponse:

Vous pouvez mettre un signe égal quand vous l'appelez:

python Application.py -env="-env"

réponse originale:

j'ai moi aussi eu des problèmes à faire ce que vous essayez de faire, mais il y a une solution de contournement intégrée dans argparse, qui est la méthode parse_known_args . Cela laissera passer tous les arguments que vous n'avez pas définis grâce à l'analyseur avec l'hypothèse que vous utilisez pour un sous-processus. Les inconvénients sont que vous ne recevrez pas de rapport d'erreur avec de mauvais arguments, et vous devrez vous assurer qu'il n'y a pas de collision entre vos options et vos sous-processus.

une autre option pourrait être de forcer l'utilisateur à utiliser un plus au lieu d'un moins:

python Application.py -e "+s WHATEVER +e COOL STUFF"

et ensuite vous changez le " + " En " - " dans le traitement post avant de passer à votre sous-processus.

37
répondu SethMMorton 2014-02-19 16:15:07

cette question est examinée en profondeur dans http://bugs.python.org/issue9334 . L'essentiel de l'activité était en 2011. J'ai ajouté un patch l'année dernière, mais il y a tout un arriéré de patches argparse .

est en cause l'ambiguïté potentielle dans une chaîne comme '--env' , ou "-s WHATEVER -e COOL STUFF" quand elle suit une option qui prend un argument.

optparse est-ce un simple gauche à droite analyser. Première --env est un drapeau d'option qui prend un argument, donc il consomme le suivant, peu importe à quoi il ressemble. argparse , d'autre part, boucle deux fois les cordes. Tout d'abord, il les catégorise comme " O " ou " A " (option flag ou argument). Sur la deuxième boucle, il les consomme, en utilisant un re comme modèle correspondant à gérer la variable nargs valeurs. Dans ce cas , il semble que nous avons OO , deux drapeaux et aucun argument.

la solution en utilisant argparse est de s'assurer qu'une chaîne d'argument ne sera pas confondue avec un drapeau d'option. Les possibilités qui ont été montrées ici (et dans la question des bogues) comprennent:

--env="--env"  # clearly defines the argument.

--env " --env"  # other non - character
--env "--env "  # space after

--env "--env one two"  # but not '--env "-env one two"'

par lui-même '--env' ressemble à un drapeau (même lorsqu'il est cité, voir sys.argv ), mais lorsqu'il est suivi par d'autres chaînes, il ne le fait pas. Mais "-env one two" a des problèmes parce qu'il peut être interprété comme" 1519180920`, un drapeau "- e' suivi d'une chaîne (ou encore plus d'options).

-- et nargs=argparse.PARSER peut également être utilisé à la force argparse pour afficher toutes les chaînes de caractères suivantes en tant qu'arguments. Mais ils ne travaillent à la fin des listes d'arguments.

un correctif est proposé dans l'émission 9334 pour ajouter le mode args_default_to_positional=True . Dans ce mode, l'analyseur ne classifie les chaînes de caractères comme options que s'il peut les apparier avec des arguments définis. Ainsi, '--' --env --' est considéré comme comme un argument. Mais le second "--env' dans '--env -- env' serait encore classé comme option.


développement sur le cas lié dans

utilisant argparse avec des valeurs d'argument qui commencent par un tiret ("-")

parser = argparse.ArgumentParser(prog="PROG")
parser.add_argument("-f", "--force", default=False, action="store_true")
parser.add_argument("-e", "--extra")
args = parser.parse_args()
print(args)

produit

1513:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra='--foo one', force=False)
1513:~/mypy$ python3 stack16174992.py --extra "-foo one"
usage: PROG [-h] [-f] [-e EXTRA]
PROG: error: argument -e/--extra: expected one argument
1513:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra='-bar one', force=False)
1514:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra='one', force=True)

le cas "- foo one" échoue parce que le -foo est interprété comme le drapeau -f plus les extras non spécifiés. C'est la même chose action qui permet d'interpréter -fe comme ['-f','-e'] .

si je change le nargs en REMAINDER (pas PARSER ), tout après -e est interprété comme arguments pour ce drapeau:

parser.add_argument("-e", "--extra", nargs=argparse.REMAINDER)

tous les cas fonctionnent. Notez que la valeur est une liste de. Et les citations ne sont pas nécessaires:

1518:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra=['--foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-foo one"
Namespace(extra=['-foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra=['-bar one'], force=False)
1519:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra=['one'], force=True)
1520:~/mypy$ python3 stack16174992.py --extra --foo one
Namespace(extra=['--foo', 'one'], force=False)
1521:~/mypy$ python3 stack16174992.py --extra -foo one
Namespace(extra=['-foo', 'one'], force=False)

argparse.REMAINDER , c'est comme '*', sauf qu'il prend tout ce qui suit, si elle ressemble un drapeau ou pas. argparse.PARSER est plus comme '+', en ce qu'il attend un argument positional comme premier. C'est le nargs que subparsers utilise.

cette utilisation de REMAINDER est documentée, https://docs.python.org/3/library/argparse.html#nargs

12
répondu hpaulj 2017-08-07 22:28:52