扩展argparse以在可选参数选择的帮助文本中写入集名称,并在结尾处定义这些集合

时间:2012-03-14 12:57:12

标签: python 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'}。
  • 在帮助文本的末尾显示一个部分,说明每个选项名称包含哪些元素。

所以我问:

  1. 这是否可以使用argparse?
  2. 我必须继承哪些类以及我需要覆盖哪些方法?
  3. 我曾尝试查看argparse的源代码,但由于这种修改感觉非常先进,我不知道该如何开始。

3 个答案:

答案 0 :(得分:18)

我的回答并不是试图扩展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}"

这种方法的好处在于,您可以随意为每个标志命名metavar,并且您可以手动设置epilog的格式以反映任何格式描述。不需要子类化。

答案 1 :(得分:10)

根据现在已删除的赏金捐赠者的请求,与其他答案形成鲜明对比的完全通用的解决方案:

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 :(得分:1)

我不完全确定我明白你想要什么。 。 。,类似于以下内容:

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关键字。格式化程序的更改是使epilog以您想要的方式显示。一般来说,我建议不要使用它。

修改

根据您的python版本,您可能有RawDescriptionHelpFormatter可用,我建议您使用此问题。