Обработка аргументов для подпроцессов с использованием argparse: «Ожидается один аргумент»

Прежде всего, я прошу прощения за заголовок, который не дает полного представления о моей проблеме, я как бы боролся с этим, поэтому я собираюсь более четко проиллюстрировать это воспроизводимым примером:

У меня есть следующий код:

пример.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' со всеми прикрепленными файлами из-за того, что программа его получает.

Вы знаете какой-нибудь обходной путь?

Кавычки говорят bash держать строку вместе, но они не влияют на то, что argparse видит и делает. python example.py -e --repeat такой же, как ваш третий пример.

hpaulj 05.05.2022 17:52
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
44
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я нашел уродливый обходной путь, но если он выйдет, мне понравится лучшее решение.

Я добавил следующую функцию:

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)

Не идеальный, но простой обходной путь.

Дело в том, что я не хочу, чтобы пользователь знал об обходном пути, это усложняет для него задачу.

Zangdar 05.05.2022 18:32

Я понимаю это, и они не должны, особенно потому, что вы раскрываете обходной путь ошибки в спецификации ваших потребителей. Лично я бы указал, что дополнительные аргументы должны быть без тире, как в `python example.py -e "repeat=0 foo=1 bar=2"`. Это полностью приемлемо для потребителей, ваш код может легко добавить тире перед передачей дополнительных аргументов в подпроцесс. Это может иметь положительный побочный эффект, заключающийся в более четком различении аргументов основного процесса и аргументов подпроцесса. Ваш внутренний программист может кричать, чувствуя поражение, но иногда прагматичность — лучший выбор.

Giuseppe Romagnuolo 06.05.2022 11:00

Я мог бы быть, за исключением того, что подпроцесс не соблюдает никаких стандартов, у некоторых аргументов есть --, у некоторых их нет. Поэтому я не могу принять это как должное. Я нашел способ обрабатывать дополнительные аргументы, редактируя sys.argv перед синтаксическим анализом, а затем возвращая аргумент к правильному значению после анализа. Так что все хорошо, на потребителя никак не влияет

Zangdar 06.05.2022 16:05

Обработка значений, начинающихся с «--», представляет собой проблему с 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 — идеальное решение.

Другие вопросы по теме