Как закодировать комбинационные параметры argparse в python

#python #argparse

#python #argparse

Вопрос:

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

Требование:

    test2.py [-c/-v] -f
  

Использование или правила:

  1. -c (compare) принимает 2 параметра.

    -v (verify) принимает 1 параметр.

    Любой из этих двух должен присутствовать, но не оба.

  2. -f является обязательным параметром (имя выходного файла).

Вывод:

Я могу получить желаемый результат, как показано ниже

 kp@kp:~/Study/scripts$ ./test.py -c P1 P2 -f p
kp@kp:~/Study/scripts$ ./test.py -v P1 -f p
kp@kp:~/Study/scripts$ ./test.py -v P1 
usage: test.py <functional argument> <ouput target argument>
test.py: error: argument -f/--file is required
kp@kp:~/Study/scripts$ ./test.py -c P1 P2 
usage: test.py <functional argument> <ouput target argument>
test.py: error: argument -f/--file is required
kp@kp:~/Study/scripts$ 
  

Проблема в:

При использовании, test.py -h ,
1. В выходных данных не будет указано, что -c /-v любой из них является обязательным, но не оба . Это указывает на то, что все аргументы являются необязательными.
2. В выходных данных будет указана опция -f в необязательных аргументах, что неверно. -f является обязательным аргументом, и я хочу отобразить внешние необязательные аргументы.

Как изменить скрипт, чтобы вывод опции -h был более удобным для пользователя (без какой-либо внешней проверки)

 usage: test.py <functional argument> <ouput target argument>

Package Compare/Verifier tool.

optional arguments:
  -h, --help            show this help message and exit
  -f outFileName, --file outFileName
                        File Name where result is stored.
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.
kiran@kiran-laptop:~/Study/scripts$ 
  

Код:

Я использую приведенный ниже код для достижения результата,

 #!/usr/bin/python

import sys
import argparse

def main():
    usage='%(prog)s <functional argument> <ouput target argument>'
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(usage=usage,description=description)

    parser.add_argument('-f','--file',action='store',nargs=1,dest='outFileName',help='File Name where result is stored.',metavar="outFileName",required=True)


    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-c','--compare',action='store',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',action='store',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')
    args = parser.parse_args()

if __name__ == "__main__":
    main()
  

Комментарии:

1. В дополнение к замене -f позиционным аргументом, я бы сделал compare и verify две подкоманды, а не пару взаимоисключающих обязательных параметров. ( test.py compare P1 P2 output.txt и test.py verify P1 output.txt ; обратите внимание, что позиционные аргументы должны быть указаны для каждого подразделителя. В качестве альтернативы, создайте -f параметр, который по умолчанию использует стандартный вывод; вы можете сохранить параметры в главном анализаторе, чтобы они применялись независимо от того, какая подкоманда задана).

Ответ №1:

Задайте имя файла в качестве позиционного аргумента, и пусть argparse устанавливается собственное сообщение об использовании:

 $ python so.py --help
usage: so.py [-h] [-c Package1 Package2 | -v Package] outFileName
  

Имя файла должно быть позиционным, и вы должны позволить argparse написать собственное сообщение об использовании.

Код

 #!/usr/bin/python

import sys
import argparse

def main():
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(description=description,
                                     epilog='--compare and --verify are mutually exclusive')

    parser.add_argument('f',action='store',nargs=1,
                        help='File Name where result is stored.',
                        metavar="outFileName")

    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('-c','--compare',action='store',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',action='store',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')

    args = parser.parse_args()

if __name__ == "__main__":
    main()
  

Справочное сообщение

 $ python so.py --help
usage: so.py [-h] [-c Package1 Package2 | -v Package] outFileName

Package Compare/Verifier tool.

positional arguments:
  outFileName           File Name where result is stored.

optional arguments:
  -h, --help            show this help message and exit
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.

--compare and --verify are mutually exclusive
  

Комментарии:

1. Это было близко, но также технически мне нужно -f из необязательных аргументов в файле справки. Поскольку заголовок «необязательные аргументы:» вводит в заблуждение, когда -f является обязательным аргументом.

2. Обновлен мой ответ, файл теперь является позиционным аргументом.

3. @Matan, Но с измененным кодом — python so.py out.txt работает. Что неверно — поскольку -c /-v (один из них должен присутствовать).

4. если вы установите required=True , решит ли это проблему?

Ответ №2:

Какой точный вывод вы ищете? Мне неизвестен какой-либо стандартный синтаксис для обозначения взаимной исключительности в выводе —help, и, вероятно, это сбило бы с толку ваших пользователей, если бы вы его придумали. Также я предполагаю, что argparse не поддерживает синтаксис для него (поскольку, если бы это было так, он бы уже работал).

Я предлагаю вам упростить это и просто объяснить своим пользователям взаимное исключение в справке для каждого из аргументов. Итак, измените их строки справки следующим образом:

 -c Package1 Package2, --compare Package1 Package2
                      Compare two packages (may not be used with -v).
-v Package, --verify Package
                      Verify Content of package (may not be used with -c).
  

Это чрезвычайно очевидно и достаточно лаконично.

Другой альтернативой было бы просто не упоминать об этом и позволить пользователю обнаружить, что они взаимоисключающие, пытаясь использовать их одновременно (argparse автоматически генерирует удобную для пользователя ошибку, такую как « PROG: error: argument -c: not allowed with argument -v «).

Комментарии:

1. после недельного ожидания не получили правильного ответа, ваш ответ кажется наиболее практичным. похоже, другого технического способа решения нет.

Ответ №3:

Я думаю, что основная жалоба связана с именами групп по умолчанию positional arguments и optional arguements . В help , optional arguments означает: requires a flag like -f or --file ; positional arguments означает it is identified by position . Значения по умолчанию positionals действительно обязательны и optionals действительно необязательны (не обязательны). Но пользователь может изменить это с помощью required атрибута, что приводит к запутанной терминологии.

Способ обойти это — определить свои собственные группы аргументов. Эти группы влияют на макет help , но не влияют на синтаксический анализ. Они также не влияют на usage строку.

 def main():
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(usage=None,description=description)

    maingroup = parser.add_argument_group(title='required')
    maingroup.add_argument('-f','--file',nargs=1,dest='outFileName',help='File Name where result is stored.',metavar="outFileName",required=True)
    exgroup = parser.add_argument_group(title='one or the other')

    group = exgroup.add_mutually_exclusive_group(required=True)
    group.add_argument('-c','--compare',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')
    args = parser.parse_args()
  

выдает:

 usage: stack5603364.py [-h] -f outFileName (-c Package1 Package2 | -v Package)

Package Compare/Verifier tool.

optional arguments:
  -h, --help            show this help message and exit

required:
  -f outFileName, --file outFileName
                        File Name where result is stored.

one or the other:
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.
  

mutually_exclusive_group Влияет только на usage строку.

 (-c Package1 Package2 | -v Package)
  

отображает группу, в которой требуется один из вариантов.

 [-c Package1 Package2 | -v Package]
  

это была бы необязательная группа. [] используются для обозначения необязательных (в смысле «не обязательных») аргументов. Обратите внимание, как -h продолжают помечаться.

http://bugs.python.org/issue9694 это связанная с Python проблема, в которой argparse автор предпочитает этот argument_group подход.