Не удается заставить argparse читать цитированную строку с тире в ней?
Есть ли способ заставить argparse распознавать что-либо между двумя кавычками как один аргумент? Кажется, что он продолжает видеть тире и полагает, что это начало новой опции
У меня есть что-то вроде:
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...
Но когда я запускаю:
python Application.py queue -env "-s WHATEVER -e COOL STUFF"
Это дает мне:
Application.py queue: error: argument -env/--extraEnvVars: expected one argument
Если я отойду от первого тире, он будет работать отлично, но это очень важно, чтобы я мог передать строку с тире в ней. Я попытался убежать от него с \, что приводит к его успеху, но добавляет строку\в строку аргументов. Кто-нибудь знает, как обойти это? Это происходит независимо от того, является или нет аргументом ss в парсере.
EDIT: я использую Python 2.7.
EDIT2:
python Application.py -env " -env"
работает отлично, но
python Application.py -env "-env"
нет.
EDIT3: Похоже, это на самом деле ошибка, которая уже обсуждалась: 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. Это только в 2.7, а не в optparse.
EDIT4: Текущий отчет об ошибке: http://bugs.python.org/issue9334
Ответы
Ответ 1
Вы можете запустить аргумент с пробелом python tst.py -e ' -e blah'
как очень простой способ обхода. Просто lstrip()
возможность вернуть его в нормальное состояние, если хотите.
Или, если первый "вспомогательный аргумент" также не является допустимым аргументом исходной функции, вам не нужно ничего делать вообще. То есть единственная причина, по которой python tst.py -e '-s hi -e blah'
не работает, состоит в том, что -s
является допустимой опцией tst.py
.
Кроме того, модуль optparse, теперь устаревший, работает без каких-либо проблем.
Ответ 2
Обновленный ответ:
Вы можете поместить знак равенства, когда вы его назовете:
python Application.py -env="-env"
Оригинальный ответ:
У меня тоже были проблемы, делающие то, что вы пытаетесь сделать, но в argparse есть обходное решение, которое является методом parse_known_args. Это позволит всем аргументам, которые вы не определили, пройти через парсер с предположением, что вы будете использовать их для подпроцесса. Недостатки в том, что вы не получите сообщение об ошибках с плохими аргументами, и вам нужно будет убедиться, что между вашими параметрами и вашими параметрами подпроцесса нет конфликта.
Другой вариант может заключаться в том, чтобы заставить пользователя использовать плюс вместо минуса:
python Application.py -e "+s WHATEVER +e COOL STUFF"
а затем вы поменяете "+" на "-" в пост-обработке перед тем, как перейти к вашему подпроцессу.
Ответ 3
Эта проблема подробно обсуждается в http://bugs.python.org/issue9334. Большая часть активности была в 2011 году. Я добавил патч в прошлом году, но там довольно отставание от argparse
патчей.
Возникает потенциальная двусмысленность в строке типа '--env'
или "-s WHATEVER -e COOL STUFF"
, когда она следует за опцией, которая принимает аргумент.
optparse
выполняет простой анализ слева направо. Первый --env
- это флаг опции, который принимает один аргумент, поэтому он потребляет следующий, независимо от того, как он выглядит. argparse
, с другой стороны, дважды проходит строки. Сначала он классифицирует их как "O" или "A" (флаг или аргумент опций). Во втором цикле он потребляет их, используя сопоставление типа re
, подходящее для обработки значений переменной nargs
. В этом случае мы видим, что у нас есть OO
, два флага и никаких аргументов.
Решение при использовании argparse
состоит в том, чтобы убедиться, что строка аргумента не будет путаться для флажка параметра. Возможности, которые были показаны здесь (и в проблеме с ошибкой), включают:
--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"'
Сам по себе '--env'
выглядит как флаг (даже при цитировании, см. sys.argv
), но если следовать за другими строками, это не так. Но "-env one two"
имеет проблемы, потому что он может быть проанализирован как ['-e','nv one two']
, флаг `-e ', за которым следует строка (или даже больше параметров).
--
и nargs=argparse.PARSER
также могут использоваться, чтобы заставить argparse
просматривать все следующие строки в качестве аргументов. Но они работают только в конце списка аргументов.
В выпуске 9334 есть предлагаемый патч для добавления режима args_default_to_positional=True
. В этом режиме анализатор только классифицирует строки как флаги опций, если он может четко соответствовать им с определенными аргументами. Таким образом, "-one" в "--env -one" будет классифицирован как аргумент. Но второй '--env' в '--env --env' по-прежнему будет классифицироваться как флаг опции.
Расширение на связанном случае в
Использование argparse с значениями аргументов, начинающимися с тире ( "-" )
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)
производит
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)
Случай "-foo one" терпит неудачу, потому что -foo
интерпретируется как флаг -f
плюс неуказанные дополнения. Это то же самое действие, которое позволяет -fe
интерпретироваться как ['-f','-e']
.
Если я изменяю nargs
на REMAINDER
(не PARSER
), все после -e
интерпретируется как аргументы для этого флага:
parser.add_argument("-e", "--extra", nargs=argparse.REMAINDER)
Все дела работают. Обратите внимание, что это список. И цитаты не нужны:
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
похож на '*', за исключением того, что он принимает все, что следует, независимо от того, выглядит он как флаг или нет. argparse.PARSER
больше похож на "+", так как он сначала ожидает один аргумент positional
. Это nargs
, который использует subparsers
.
Используется REMAINDER
, https://docs.python.org/3/library/argparse.html#nargs