web-dev-qa-db-fra.com

Vous ne pouvez pas obtenir argparse pour lire la chaîne entre guillemets avec des tirets?

Existe-t-il un moyen de faire argparse reconnaître quoi que ce soit entre deux guillemets comme un seul argument? Il semble continuer de voir les tirets et supposer 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"

Ça me donne:

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

Si je laisse le premier tiret, cela fonctionne très bien, mais il est crucial que je puisse passer une chaîne avec des tirets. J'ai essayé de l'échapper avec \, ce qui le fait réussir mais ajoute le\à la chaîne d'argument. Quelqu'un sait-il comment contourner cela? Cela se produit, que -s soit ou non 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 fait pas.

EDIT3: On dirait que c'est en fait un bogue qui est déjà débattu: http://www.gossamer-threads.com/lists/python/bugs/89529 , http: // python .6.x6.nabble.com/issue9334-argparse-n'accepte-pas-les-options-de-prise-des-arguments-commençant-avec-un-tiret-regression-from-optp-td578790.html . C'est seulement en 2.7 et non en optparse.

EDIT4: Le rapport de bogue ouvert actuel est: http://bugs.python.org/issue9334

44
sfendell

Vous pouvez commencer l'argument avec un espace python tst.py -e ' -e blah' Comme solution de contournement très simple. Simplement lstrip() l'option de le remettre à la normale, si vous le souhaitez.

Ou, si le premier "sous-argument" n'est pas également un argument valide pour la fonction d'origine, vous ne devriez rien faire du tout. 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 pour tst.py.

De plus, le module optparse , désormais obsolète, fonctionne sans aucun problème.

13
William

Réponse mise à jour:

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

python Application.py -env="-env"

Réponse originale:

Moi aussi, j'ai eu du mal à faire ce que vous essayez de faire, mais il existe une solution de contournement dans argparse, qui est la méthode parse_known_args . Cela permettra à tous les arguments que vous n'avez pas définis de passer par l'analyseur avec l'hypothèse que vous les utiliseriez pour un sous-processus. Les inconvénients sont que vous n'obtiendrez 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 les options de votre 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"

puis vous changez le "+" en "-" dans le post-traitement avant de passer à votre sous-processus.

47
SethMMorton

Ce problème est traité en profondeur dans http://bugs.python.org/issue9334 . La plupart de l'activité a eu lieu en 2011. J'ai ajouté un correctif l'année dernière, mais il y a tout un arriéré de correctifs argparse.

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

optparse fait une simple analyse de gauche à droite. La première --env est un indicateur d'option qui prend un argument, donc il consomme le suivant, peu importe à quoi il ressemble. argparse, d'autre part, boucle deux fois les chaînes. Tout d'abord, il les classe comme "O" ou "A" (indicateur ou argument d'option). Sur la deuxième boucle, il les consomme, en utilisant un re comme un modèle de correspondance pour gérer les variables nargs. Dans ce cas, il semble que nous ayons OO, deux drapeaux et aucun argument.

La solution lors de l'utilisation de argparse est de s'assurer qu'une chaîne d'arguments ne sera pas confondue avec un indicateur d'option. Les possibilités qui ont été montrées ici (et dans le problème de bogue) incluent:

--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 non suivi par d'autres chaînes. Mais "-env one two" a des problèmes car il peut être analysé comme ['-e','nv one two'], un drapeau `` -e '' suivi d'une chaîne (ou même plus d'options).

-- et nargs=argparse.PARSER peut également être utilisé pour forcer argparse à afficher toutes les chaînes suivantes comme arguments. Mais ils ne fonctionnent qu'à la fin des listes d'arguments.

Il existe un correctif proposé dans issue9334 pour ajouter un args_default_to_positional=True mode. Dans ce mode, l'analyseur classe uniquement les chaînes comme indicateurs d'option s'il peut clairement les faire correspondre avec des arguments définis. Ainsi '--one' dans '--env --one' serait classé comme argument. Mais le deuxième "--env" dans "--env --env" serait toujours classé comme indicateur d'option.


Développer le cas connexe dans

tilisation d'argparse avec des valeurs d'argument commençant 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 car le -foo est interprété comme -f drapeau plus extras non spécifiés. Il s'agit de la même action qui permet à -fe à interpréter comme ['-f','-e'].

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

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

Tous les cas fonctionnent. Notez que la valeur est une liste. Et les devis 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 est comme '*', sauf qu'il prend tout ce qui suit, qu'il ressemble ou non à un drapeau. argparse.PARSER ressemble plus à '+', en ce sens qu'il attend d'abord un argument semblable à positional. C'est le nargs que subparsers utilise.

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

14
hpaulj
paser.add_argument("--argument_name", default=None, nargs=argparse.REMAINDER)

python_file.py --argument_name "--abc = 10 -a = 1 -b = 2 cdef"

Remarque: les valeurs d'argument doivent être transmises uniquement entre guillemets doubles et cela ne fonctionne pas avec les guillemets simples

0
Gowtham MS