Подтвердить что ты не робот

Расширьте argparse, чтобы записывать имена заданий в тексте справки для выбора опциональных аргументов и определять эти наборы один раз в конце

Пример проблемы

Если у меня есть список допустимых строк параметров, который разделяется между несколькими аргументами, список записывается в нескольких местах в строке справки. Сложнее читать:

def main():
    elements = ['a', 'b', 'c', 'd', 'e', 'f']

    parser = argparse.ArgumentParser()
    parser.add_argument(
        '-i',
        nargs='*',
        choices=elements,
        default=elements,
        help='Space separated list of case sensitive element names.')
    parser.add_argument(
        '-e',
        nargs='*',
        choices=elements,
        default=[],
        help='Space separated list of case sensitive element names to '
        'exclude from processing')

    parser.parse_args()

При запуске указанной функции с аргументом командной строки --help он показывает:

usage: arguments.py [-h] [-i [{a,b,c,d,e,f} [{a,b,c,d,e,f} ...]]]
                    [-e [{a,b,c,d,e,f} [{a,b,c,d,e,f} ...]]]

optional arguments:
  -h, --help            show this help message and exit
  -i [{a,b,c,d,e,f} [{a,b,c,d,e,f} ...]]
                        Space separated list of case sensitive element names.
  -e [{a,b,c,d,e,f} [{a,b,c,d,e,f} ...]]
                        Space separated list of case sensitive element names
                        to exclude from processing

Что было бы приятно

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

def main_optionlist():
    elements = ['a', 'b', 'c', 'd', 'e', 'f']

    # Two instances of OptionList are equal if and only if they
    # have the same name (ALFA in this case)

    ol = OptionList('ALFA', elements)

    parser = argparse.ArgumentParser()
    parser.add_argument(
        '-i',
        nargs='*',
        choices=ol,
        default=ol,
        help='Space separated list of case sensitive element names.')
    parser.add_argument(
        '-e',
        nargs='*',
        choices=ol,
        default=[],
        help='Space separated list of case sensitive element names to '
        'exclude from processing')

    parser.parse_args()

И при запуске указанной выше функции с аргументом командной строки --help будет отображаться нечто похожее на:

usage: arguments.py [-h] [-i [ALFA [ALFA ...]]]
                    [-e [ALFA [ALFA ...]]]

optional arguments:
  -h, --help            show this help message and exit
  -i [ALFA [ALFA ...]]
                        Space separated list of case sensitive element names.
  -e [ALFA [ALFA ...]]
                        Space separated list of case sensitive element names
                        to exclude from processing
sets in optional arguments:
  ALFA                  {a,b,c,d,e,f}

Вопрос

Мне нужно:

  • Замените в необязательных аргументах символы {'l', 'i', 's', 't', 's'} с именем опции.
  • В конце текста справки показан раздел, объясняющий, какие элементы имеют каждое имя параметра.

Поэтому я спрашиваю:

  • Возможно ли это с помощью argparse?
  • Какие классы мне нужно наследовать и какие методы мне нужно переопределить?

Я попытался взглянуть на источник для argparse, но поскольку эта модификация кажется довольно продвинутой, я не знаю, как это сделать.

4b9b3361

Ответ 1

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

import argparse
from operator import itemgetter

class OptionListGroup(object):
  class GroupAction(object):
    def __init__(self, left, right):
      self.help = right
      self.option_strings = [left]
      self.nargs = 0

  def __init__(self, lists):
    self.description = None
    self.title = "referenced sets"
    self._group_actions = [self.GroupAction(name, self.format_list(lst))
                           for name, lst in sorted(lists)]

  def format_list(self, lst):
    return '{%s}' % ', '.join(map(str, lst))

class MyArgParser(argparse.ArgumentParser):
  def __init__(self, *args, **kwargs):
    self._option_lists = {}
    super(MyArgParser, self).__init__(*args, **kwargs)

  def parse_args(self, *args, **kw):
    self._action_groups.append(OptionListGroup(self._option_lists.values()))
    return super(MyArgParser, self).parse_args(*args, **kw)

  def add_option_list(self, name, lst):
    if name in map(itemgetter(0), self._option_lists.values()):
      raise ValueError, "Name already existing"
    self._option_lists[id(lst)] = (name, lst)

  def add_argument(self, *args, **kw):
    name_list = self._option_lists.get(id(kw.get('choices')))
    if name_list:
      kw['metavar'] = name_list[0]
    return super(MyArgParser, self).add_argument(*args, **kw)

Пример использования:

alfa = ['a', 'b', 'c', 'd', 'e', 'f']
num = [1, 2, 3]

parser = MyArgParser()

parser.add_option_list('ALFA', alfa)
parser.add_option_list('NUM', num)

parser.add_argument(
  '-a',
  nargs='*',
  choices=alfa,
  default=alfa,
  help='Characters (defaults to include all)')

parser.add_argument(
  '-e',
  nargs='*',
  choices=num,
  default=[],
  help='Digits (defaults to exclude all)')

parser.parse_args()

Выход справки:

usage: argparse-optionlist.py [-h] [-a [ALFA [ALFA ...]]]
                              [-e [NUM [NUM ...]]]

optional arguments:
  -h, --help            show this help message and exit
  -a [ALFA [ALFA ...]]  Characters (defaults to include all)
  -e [NUM [NUM ...]]    Digits (defaults to exclude all)

referenced sets:
  ALFA                  {a, b, c, d, e, f}
  NUM                   {1, 2, 3}

Ответ 2

Мой ответ не пытается распространять argparse вообще, а использует доступные параметры argparse, как есть... Решает ли ваша ситуация ваша ситуация?

import argparse
import textwrap

def main():
    elements = ['a', 'b', 'c', 'd', 'e', 'f']

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog = textwrap.dedent('''\
            sets in optional arguments: 
                ALFA\t\t{a,b,c,d,e,f}"
            '''))

    parser.add_argument(
        '-i',
        nargs='*',
        choices=elements,
        default=elements,
        metavar="ALFA",
        help='Space separated list of case sensitive element names.')
    parser.add_argument(
        '-e',
        nargs='*',
        choices=elements,
        default=[],
        metavar="ALFA",
        help='Space separated list of case sensitive element names to '
        'exclude from processing')

    parser.parse_args()

Выход

usage: test.py [-h] [-i [ALFA [ALFA ...]]] [-e [ALFA [ALFA ...]]]

optional arguments:
  -h, --help            show this help message and exit
  -i [ALFA [ALFA ...]]  Space separated list of case sensitive element names.
  -e [ALFA [ALFA ...]]  Space separated list of case sensitive element names
                        to exclude from processing

sets in optional arguments: 
    ALFA                {a,b,c,d,e,f}"

Самое приятное в этом подходе заключается в том, что вы можете свободно указывать метавару, что хотите для каждого флага, и вы можете вручную отформатировать эпилог, чтобы отразить любое описание формата. Не требуется подклассов.

Ответ 3

Я не совсем уверен, что понимаю, чего ты хочешь.,., Как насчет чего-то похожего на следующее:

import argparse

elements = ['a', 'b', 'c', 'd', 'e', 'f']

class myparser(argparse.ArgumentParser):
    def add_argument(self,*args,**kwargs):
        choice=kwargs.get('choices',None)
        if(choice is elements):
            kwargs['metavar']='ALFA'

        return argparse.ArgumentParser.add_argument(self,*args,**kwargs)

def main():

    epilog="""sets in optional arguments:
  ALFA                  %s"""%(str(elements).replace('[','{').replace(']','}'))

    parser = myparser(epilog=epilog,formatter_class=argparse.RawTextHelpFormatter) 
    parser.add_argument(
        '-i',
        nargs='*',
        choices=elements,
        default=elements,
        help='Space separated list of case sensitive element names.')
    parser.add_argument(
        '-e',
        nargs='*',
        choices=elements,
        default=[],
        help='Space separated list of case sensitive element names to '
        'exclude from processing')

    parser.parse_args()


main()

При желании вы можете просто использовать ключевое слово metavar. Изменение форматирования заключается в том, чтобы сделать эпилог таким, каким вы его хотите. В общем, я бы посоветовал не использовать его.

ИЗМЕНИТЬ

В зависимости от вашей версии python у вас может быть RawDescriptionHelpFormatter, который я бы посоветовал использовать для этой проблемы.