Python argparse:大量选项导致帮助信息难以阅读

66

我有这段代码,总体上我很满意:

import argparse

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer",
            "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ]

parser = argparse.ArgumentParser(description="A program to update components on servers.")
group = parser.add_mutually_exclusive_group()
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components')
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components')
parser.add_argument('-o', '--only', nargs='*', choices=servers, help='Space separated list of case sensitive server names to process')
parser.add_argument('-s', '--skip', nargs='*', choices=servers, help='Space separated list of case sensitive server names to exclude from processing')
args = parser.parse_args()

我喜欢choice=servers可以帮我验证输入的服务器名称,这样我就不必自己验证了。然而,有太多有效选项会让帮助输出看起来很糟糕:

usage: args.py [-h] [-l | -u]
               [-o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]]
               [-s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]]

A program to update components on servers.

optional arguments:
  -h, --help            show this help message and exit
  -l, --list            list server components
  -u, --updatepom       update server components
  -o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --only [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]
                        Space separated list of case sensitive server names to
                        process
  -s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --skip [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]
                        Space separated list of case sensitive server names to
                        exclude from processing
我该选择哪种方式,如果我想要:
  • 漂亮的(大部分是)自动生成的帮助输出
  • 验证给定到 -o 或 -s 选项的条目是否在 "servers" 中。

额外奖励:

  • 是否可能对服务器名称进行不区分大小写的字符串匹配?

添加

我尝试使用 michaelfilms 的建议,在上述输出中删除 -o -s 选项,并添加此部分:

server optional arguments:
  Valid server names are: ApaServer, BananServer, GulServer, SolServer,
  RymdServer, SkeppServer, HavsServer, PiratServer, SvartServer,
  NattServer, SovServer

我认为它看起来相当不错,但我确实需要为-o-s选项提供帮助,否则用户将不会知道它们。因此,如果使用这种方法,我还没有完全达到目标。

7个回答

66

我基本上在重复Ernest所说的 - 为了避免丑陋的长选项列表,在基于选择的参数中设置metavar =''(虽然它无法消除参数和逗号之间的空格(例如 -o ,而不是-o, )).

然后,您可以在一般描述中详细描述可用的选项(如果您希望它们以明显缩进的形式列出,则RawDescriptionHelpFormatter在这里非常有用)。

这段代码:

import argparse

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer",
            "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ]

parser = argparse.ArgumentParser(description="A program to update components on servers.")
group = parser.add_mutually_exclusive_group()
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components')
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components')
parser.add_argument('-o', '--only', choices=servers, help='Space separated list of case sensitive server names to process.  Allowed values are '+', '.join(servers), metavar='')
parser.add_argument('-s', '--skip', choices=servers, help='Space separated list of case sensitive server names to exclude from processing.  Allowed values are '+', '.join(servers), metavar='')
args = parser.parse_args()
产生以下帮助输出:
usage: run.py [-h] [-l | -u] [-o] [-s]

A program to update components on servers.

optional arguments:
  -h, --help       show this help message and exit
  -l, --list       list server components
  -u, --updatepom  update server components
  -o , --only      Space separated list of case sensitive server names to
                   process. Allowed values are ApaServer, BananServer,
                   GulServer, SolServer, RymdServer, SkeppServer, HavsServer,
                   PiratServer, SvartServer, NattServer, SovServer
  -s , --skip      Space separated list of case sensitive server names to
                   exclude from processing. Allowed values are ApaServer,
                   BananServer, GulServer, SolServer, RymdServer, SkeppServer,
                   HavsServer, PiratServer, SvartServer, NattServer, SovServer

我希望您需要的是这个。


将 metavar 设置为 '' 或 None 将导致完全不显示帮助信息。move_parser.add_argument('old_host', action='store', choices=distinct_host, help="旧主机"+ ','.join(distinct_host), metavar= None) - RamPrasadBismil
3
现在似乎metavar=''会导致错误。把它设置为除空字符串以外的任何内容(比如参数名称)就可以解决这个问题了。 - user3995702
尝试使用无效的选项仍会导致打印所有选项集合: 无效选项:500(可从0、1、2、3、4、5、6、7、8、9、10、11中选择) - Michael Anderson

31

不需要子类化任何东西。只需使用metavar参数并传递您希望出现在帮助消息中的字符串即可。

有关详细信息,请参见argparse文档


12

我也遇到了相同的问题,作为一种解决方法,我使用epilog来描述每个选项的选择。我不得不使用argparse.RawTextHelpFormatter,它允许您指定epilog是预先格式化的。

def choicesDescriptions():
   return """
Choices supports the following: 
   choice1         - the FIRST option
   choice2         - the SECOND option
   ...
   choiceN         - the Nth option
"""

def getChoices():
   return ["choice1", "choice2", ..., "choiceN"]

parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, epilog=choicesDescriptions())
parser.add_argument(
   'choices', 
   choices=getChoices(),
   help='Arg choice.  See the choices options below'
   )

args = parser.parse_args()
print(args)

7

在选项列表非常长的情况下(如原问题中),这种方法并不适用,但对于像我这样的人,他们正在寻找一种将中等长度的选项字符串分成两行的方法,这里是我的解决方案:

import argparse

class CustomFormatter(argparse.HelpFormatter):
    """Custom formatter for setting argparse formatter_class. Identical to the
    default formatter, except that very long option strings are split into two
    lines.
    """

    def _format_action_invocation(self, action):
        if not action.option_strings:
            metavar, = self._metavar_formatter(action, action.dest)(1)
            return metavar
        else:
            parts = []
            # if the Optional doesn't take a value, format is:
            #    -s, --long
            if action.nargs == 0:
                parts.extend(action.option_strings)
            # if the Optional takes a value, format is:
            #    -s ARGS, --long ARGS
            else:
                default = action.dest.upper()
                args_string = self._format_args(action, default)
                for option_string in action.option_strings:
                    parts.append('%s %s' % (option_string, args_string))
            if sum(len(s) for s in parts) < self._width - (len(parts) - 1) * 2:
                return ', '.join(parts)
            else:
                return ',\n  '.join(parts)

这段代码覆盖了默认的argparse.HelpFormatter方法_format_action_invocation,除了最后四行外与默认实现相同。
默认格式化程序的行为:
parser = argparse.ArgumentParser(description="Argparse default formatter.")
parser.add_argument('-a', '--argument', help='not too long')
parser.add_argument('-u', '--ugly', choices=range(20), help='looks messy')
parser.print_help()

输出:

usage: test.py [-h] [-a ARGUMENT]
               [-u {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}]

Argparse default formatter.

optional arguments:
  -h, --help            show this help message and exit
  -a ARGUMENT, --argument ARGUMENT
                        not too long
  -u {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}, --ugly {0,1,2,3,4,5,6,
7,8,9,10,11,12,13,14,15,16,17,18,19}
                        looks messy

自定义格式化程序的行为:

parser = argparse.ArgumentParser(description="Argparse custom formatter.",
                                 formatter_class=CustomFormatter)
parser.add_argument('-a', '--argument', help='not too long')
parser.add_argument('-l', '--less-ugly', choices=range(20), help='less messy')

输出:

usage: test.py [-h] [-a ARGUMENT]
               [-l {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}]

Argparse custom formatter.

optional arguments:
  -h, --help            show this help message and exit
  -a ARGUMENT, --argument ARGUMENT
                        not too long
  -l {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19},
  --less-ugly {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}
                        less messy

4
为什么不使用parser.add_argument_group为服务器选项创建一个组,并为其提供描述参数,以显示可能的选择列表?然后将argparse.SUPPRESS传递到每个单独选项的帮助中。我相信这将给你想要的结果。

我尝试了一下,看起来还不错。但是我还没有完全掌握这种方法。我必须通过让用户了解“-o”和“-s”选项来帮助他。 - Deleted

4
为了得到预期的输出,您需要子类化argparse.HelpFormatter并实现您需要的格式。特别是,您需要实现自己的_metavar_formatter方法,该方法负责将所有选项连接成由逗号分隔的单个字符串。

1

http://bugs.python.org/issue16468 讨论了有关选项格式的错误问题,argparse 仅支持可迭代的选项列表,这个列表可能出现在三个位置:使用行、帮助行和错误消息。

解析器只关心进行 in__contains__)测试。但是,对于格式化而言,长列表、无限制的“列表”(例如整数>100)和其他不可迭代的对象会导致问题。当前用户可以通过 metavar 来解决大多数格式化问题(它可能无法帮助处理错误消息)。请查看此问题以获取如何更改您自己版本的 argparse 的想法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接