Прежде всего, я прошу прощения за заголовок, который не дает полного представления о моей проблеме, я как бы боролся с этим, поэтому я собираюсь более четко проиллюстрировать это воспроизводимым примером:
У меня есть следующий код:
пример.py:
import argparse
def parse_args():
"""Command line argument parser
Returns:
The extra arguments
"""
parser = argparse.ArgumentParser(description = "Arguments for test runner")
parser.add_argument('-e', '--extra_args', type=str, default = "", help = "Extra arguments. Enter as a single line encapsulated by quotes. Example: -e \"--repeat=0 otherstuff -x anotherpositional\"")
arguments = parser.parse_args()
return arguments
if __name__ == "__main__":
args = parse_args()
print(args.extra_args)
Затем аргумент --extra_args
будет использоваться в качестве позиционного аргумента для вызова подпроцесса.
Аргумент, который мне нужно передать, следующий: --repeat=0
. Это не работает, я получил следующие результаты:
python example.py -e "toto" # This print toto
python example.py -e "--repeat = 0" # This print --repeat = 0
python example.py -e "--repeat" # error: argument -e/--extra_args: expected one argument
python example.py -e "--repeat=0" # error: argument -e/--extra_args: expected one argument
Я понимаю, что синтаксический анализатор обрабатывает --repeat
как аргумент, не находит для него значения и ломается. Но, к сожалению, у меня нет другого выбора, кроме как написать '--repeat=0' со всеми прикрепленными файлами из-за того, что программа его получает.
Вы знаете какой-нибудь обходной путь?
Я нашел уродливый обходной путь, но если он выйдет, мне понравится лучшее решение.
Я добавил следующую функцию:
def __workaround_for_extra_utr_args():
"""This workaround is necessary because utr can receives args at \"--repeat=0\" which are not processable by arg parse, even as a string"""
import sys
index_of_extra_args = [sys.argv.index(v) for v in ['-e', '--extra_utr_args'] if v in sys.argv]
space = " "
if index_of_extra_args:
index_of_extra_args = index_of_extra_args[0]
if space not in sys.argv[index_of_extra_args + 1]:
sys.argv[index_of_extra_args + 1] = sys.argv[index_of_extra_args + 1] + space
Вызовите его перед вызовом парсера, и он сможет вернуть строку, тогда можно удалить лишний пробел после разбора.
Кажется, есть проблема с тем, как parse_args
интерпретирует двойное тире в начале строки arg.
Если вы добавите префикс, это может быть один символ, например пробел в строке, это может быть обходным путем.
python example.py -e " --repeat=0" # OK
python example.py -e ":--repeat=0" # OK
python example.py -e " --repeat=0 --foo=1 --boo=2" # OK
Тогда в вашем коде было бы легко удалить extra_args первого символа или любого другого префикса.
import argparse
def parse_args():
"""Command line argument parser
Returns:
A tuple containing the toolset version and a force flag
"""
parser = argparse.ArgumentParser(description = "Arguments for test runner")
parser.add_argument('-e', '--extra_args', type=str, default = "", help = "Extra arguments. Enter as a single line encapsulated by quotes. Example: -e \"--repeat=0 otherstuff -x anotherpositional\"")
arguments = parser.parse_args()
arguments.extra_args = arguments.extra_args[1:]
return arguments
if __name__ == "__main__":
args = parse_args()
print(args.extra_args)
Не идеальный, но простой обходной путь.
Дело в том, что я не хочу, чтобы пользователь знал об обходном пути, это усложняет для него задачу.
Я понимаю это, и они не должны, особенно потому, что вы раскрываете обходной путь ошибки в спецификации ваших потребителей. Лично я бы указал, что дополнительные аргументы должны быть без тире, как в `python example.py -e "repeat=0 foo=1 bar=2"`. Это полностью приемлемо для потребителей, ваш код может легко добавить тире перед передачей дополнительных аргументов в подпроцесс. Это может иметь положительный побочный эффект, заключающийся в более четком различении аргументов основного процесса и аргументов подпроцесса. Ваш внутренний программист может кричать, чувствуя поражение, но иногда прагматичность — лучший выбор.
Я мог бы быть, за исключением того, что подпроцесс не соблюдает никаких стандартов, у некоторых аргументов есть --, у некоторых их нет. Поэтому я не могу принять это как должное. Я нашел способ обрабатывать дополнительные аргументы, редактируя sys.argv перед синтаксическим анализом, а затем возвращая аргумент к правильному значению после анализа. Так что все хорошо, на потребителя никак не влияет
Обработка значений, начинающихся с «--», представляет собой проблему с argparse
. Идея о том, что он отмечает флаг (как в случае с вашим --extra_args
), настолько неотъемлема от argparse
, что ее трудно обойти.
Просто добавление кавычек не помогает. Они управляют тем, как оболочка обрабатывает строки, но никак иначе не меняют то, что находится в sys.argv
.
https://docs.python.org/3/library/argparse.html#arguments-содержащий
описывает использование псевдоаргумента "--", но это работает только для позиционных аргументов. (Мне пришлось бы снова просмотреть код, чтобы понять, почему он ничего не делает с необязательными параметрами.)
Итак, с:
parser = argparse.ArgumentParser(description = "Arguments for test runner")
parser.add_argument('extra_args', nargs='*')
arguments = parser.parse_known_args()
print(arguments)
Я получил:
919:~/mypy$ python3 stack72129874.py -- --repeat
argv ['stack72129874.py', '--', '--repeat']
(Namespace(extra_args=['--repeat']), [])
0919:~/mypy$ python3 stack72129874.py -- --repeat=0
argv ['stack72129874.py', '--', '--repeat=0']
(Namespace(extra_args=['--repeat=0']), [])
0920:~/mypy$ python3 stack72129874.py -- --repeat = 0
argv ['stack72129874.py', '--', '--repeat', '=', '0']
(Namespace(extra_args=['--repeat', '=', '0']), [])
0920:~/mypy$ python3 stack72129874.py -- "--repeat = 0"
argv ['stack72129874.py', '--', '--repeat = 0']
(Namespace(extra_args=['--repeat = 0']), [])
Есть nargs=argparse.REMAINDER
, который работает как псевдоаргумент, но опять же только для позиционных. И есть некоторые проблемы с его использованием, поэтому теперь он недокументирован.
Добавление пробелов, таких как « --repeat = 0 », было упомянуто в предыдущем SO по этой теме.
И если вам на самом деле не нужна остальная часть механизма argparse
(помощь, обработка ошибок, другие аргументы), собственный разбор sys.argv
— идеальное решение.
Кавычки говорят
bash
держать строку вместе, но они не влияют на то, чтоargparse
видит и делает.python example.py -e --repeat
такой же, как ваш третий пример.