Python argparse子parser单片帮助输出

Python argparse子parser单片帮助输出,python,argparse,Python,Argparse,我的argparse在顶层只有3个标志(store_true),其他一切都通过子parser处理。当我运行myprog.py--help时,输出显示所有子命令的列表,如normal、{sub1、sub2、sub3、sub4、}。因此,默认值非常有效 我通常记不起所需的确切子命令名及其所有选项。因此,我最终进行了两次帮助查找: myprog.py --help myprog.py sub1 --help 我经常这样做,我决定把它塞进一个步骤。我希望我的顶级帮助输出一个巨大的摘要,然后手动滚动列表

我的argparse在顶层只有3个标志(store_true),其他一切都通过子parser处理。当我运行
myprog.py--help
时,输出显示所有子命令的列表,如normal、
{sub1、sub2、sub3、sub4、}
。因此,默认值非常有效

我通常记不起所需的确切子命令名及其所有选项。因此,我最终进行了两次帮助查找:

myprog.py --help
myprog.py sub1 --help
我经常这样做,我决定把它塞进一个步骤。我希望我的顶级帮助输出一个巨大的摘要,然后手动滚动列表。我发现这要快得多(至少对我来说)

我使用的是RawDescriptionHelpFormatter,并手动输入长长的帮助输出。但是现在我有很多子命令,管理起来很麻烦

有没有办法只需一次程序调用就获得详细的帮助输出

如果没有,我如何迭代argparse实例的子parser,然后分别从每个子parser中检索帮助输出(稍后我将把它们粘在一起)


下面是我的argparse设置的简要概述。我对代码进行了一点清理/剥离,因此如果没有一点帮助,这可能无法运行

parser = argparse.ArgumentParser(
        prog='myprog.py',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=textwrap.dedent(""" You can manually type Help here """) )

parser.add_argument('--debuglog', action='store_true', help='Verbose logging for debug purposes.')
parser.add_argument('--ipyonexit', action='store_true', help='Drop into an embeded Ipython session instead of exiting command.')

subparser = parser.add_subparsers()

### --- Subparser B
parser_b = subparser.add_parser('pdfreport', description="Used to output reports in PDF format.")
parser_b.add_argument('type', type=str, choices=['flatlist', 'nested', 'custom'],
                        help="The type of PDF report to generate.")
parser_b.add_argument('--of', type=str, default='',
                        help="Override the path/name of the output file.")
parser_b.add_argument('--pagesize', type=str, choices=['letter', '3x5', '5x7'], default='letter',
                        help="Override page size in output PDF.")
parser_b.set_defaults(func=cmd_pdf_report)

### ---- Subparser C
parser_c = subparser.add_parser('dbtables', description="Used to perform direct DB import/export using XLS files.")
parser_c.add_argument('action', type=str, choices=['push', 'pull', 'append', 'update'],
                        help="The action to perform on the Database Tables.")
parser_c.add_argument('tablename', nargs="+",
                        help="The name(s) of the DB-Table to operate on.")
parser_c.set_defaults(func=cmd_db_tables)

args = parser.parse_args()
args.func(args)

这有点棘手,因为argparse不直接公开定义的子解析器列表。但这是可以做到的:

import argparse
#创建顶级解析器
parser=argparse.ArgumentParser(prog='prog')
parser.add_参数('--foo',action='store_true',help='foo help')
subparsers=parser.add_subparsers(help='sub-command help')
#为“a”命令创建解析器
parser\u a=子parser.add\u parser('a',help='a help')
解析器\u a.add\u参数('bar',type=int,help='bar help'))
#为“b”命令创建解析器
parser_b=子parser。添加_parser('b',help='b help')
解析器添加参数('--baz',choices='XYZ',help='baz-help')
#打印主要帮助
打印(parser.format\u help())
#从解析器中检索子parser
子Parser_动作=[
解析器中的操作对操作。\u操作
if isinstance(动作,argparse.\u子动作)]
#可能只会有一个次帕苏行动,
#但安全总比后悔好
对于子Parser_操作中的子Parser_操作:
#获取所有Subparser并打印帮助
对于选项,请在subparser_action.choices.items()中选择subparser:
打印(“Subparser'{}'.format(choice))
打印(subparser.format\u help())
这个例子应该适用于Python2.7和Python3。示例解析器来自


剩下要做的唯一一件事就是为完整帮助添加一个新参数,或者替换内置的
-h/--help

在Adaephon的示例中,迭代子parser的一种更简单的方法是

for subparser in [parser_a, parser_b]:
   subparser.format_help()
Python确实允许您访问隐藏属性,如
解析器。_actions
,但不鼓励这样做。在定义解析器时,构建自己的列表同样容易。用参数做特殊的事情也是如此
add_参数
add_子Parser
返回它们各自的
Action
Parser
对象,原因如下

如果我正在创建
ArgumentParser
的子类,我可以随意使用
\u actions
。但对于一次性应用程序,构建自己的列表会更清晰


例如:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('mainpos')
parser.add_argument('--mainopt')
sp = parser.add_subparsers()
splist = []   # list to collect subparsers
sp1 = sp.add_parser('cmd1')
splist.append(sp1)
sp1.add_argument('--sp1opt')
sp2 = sp.add_parser('cmd2')
splist.append(sp2)
sp2.add_argument('--sp2opt')

# collect and display for helps    
helps = []
helps.append(parser.format_help())
for p in splist:
   helps.append(p.format_help())
print('\n'.join(helps))

# or to show just the usage
helps = []
helps.append(parser.format_usage())
for p in splist:
   helps.append(p.format_usage())
print(''.join(helps))
组合“用法”显示为:

usage: stack32607706.py [-h] [--mainopt MAINOPT] mainpos {cmd1,cmd2} ...
usage: stack32607706.py mainpos cmd1 [-h] [--sp1opt SP1OPT]
usage: stack32607706.py mainpos cmd2 [-h] [--sp2opt SP2OPT]

组合帮助的显示既长又冗余。可以在格式化后或使用特殊帮助格式化程序以各种方式对其进行编辑。但是谁会做出这样的选择呢?

下面是自定义帮助处理程序的完整解决方案(几乎所有代码都来自@Adaephon answer):


我还能够使用
\u选项\u操作
打印命令的简短帮助

def print_help(parser):
  print(parser.description)
  print('\ncommands:\n')

  # retrieve subparsers from parser
  subparsers_actions = [
      action for action in parser._actions 
      if isinstance(action, argparse._SubParsersAction)]
  # there will probably only be one subparser_action,
  # but better save than sorry
  for subparsers_action in subparsers_actions:
      # get all subparsers and print help
      for choice in subparsers_action._choices_actions:
          print('    {:<19} {}'.format(choice.dest, choice.help))
def打印帮助(解析器):
打印(parser.description)
打印('\n命令:\n')
#从解析器中检索子parser
子Parser_动作=[
解析器中的操作对操作。\u操作
if isinstance(动作,argparse.\u子动作)]
#可能只会有一个次帕苏行动,
#但存钱总比后悔好
对于子Parser_操作中的子Parser_操作:
#获取所有Subparser并打印帮助
用于子帕斯行动中的选择。\选择\行动:

打印(“{:也许更简单的方法是使用
parser.epilog

def define_parser():
    import argparse
    parser = argparse.ArgumentParser(
        prog='main',
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    commands = parser.add_subparsers(
        title="required commands",
        help='Select one of:',
    )    
    command_list = commands.add_parser(
        'list',
        help='List included services',
    )
    command_ensure = commands.add_parser(
        'ensure',
        help='Provision included service',
    )
    command_ensure.add_argument(
        "service",
        help='Service name',
    )
    import textwrap
    parser.epilog = textwrap.dedent(
        f"""\
        commands usage:\n
        {command_list.format_usage()}
        {command_ensure.format_usage()}
        """
    )
    return parser

parser = define_parser()

parser.print_help()
这将导致以下输出:

usage: main [-h] {list,ensure} ...

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

required commands:
  {list,ensure}  Select one of:
    list         List included services
    ensure       Provision included service

commands usage:

usage: main list [-h]

usage: main ensure [-h] service
add_subparsers().add_parser()
不仅接受子命令帮助中显示的
说明
,还接受顶级解析器帮助中用作一行说明的
help=

在公式中隐藏了这一点

(但是,可以通过提供help=参数来添加_parser(),从而为每个subparser命令提供帮助消息,如上所述。)

甚至在这句话的示例代码中:

[……]


是的,这不是所有事情的全部帮助,但IMHO很好地涵盖了基本用例,不容易发现。

给我们展示一个小示例,其中包含一些代码,只有几个选项和几个子参数。很好的示例。这为我生成了很好的输出。我不确定如何在我的示例中重新定义-h/--help参数,作为可选参数do不喜欢使用我的子parser。虽然我可能只定义另一个子parser,名为“help”,作为最后一个,它可以检查之前添加的所有内容。我添加了另一个子parser,名为help。这个解决方案很好,因为我可以将它转换为一个接受“parser”而不接受任何其他内容的函数。为什么不使用
hasattr()
而不是列表理解和循环的
?最好使用
parser.add_参数('-h','-help',action=\u HelpAction,help='show this help message and exit')
匹配默认的
argparse
--help
选项。如何在没有提供参数的情况下打印帮助?@kanna,请查看以下答案:
usage: main [-h] {list,ensure} ...

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

required commands:
  {list,ensure}  Select one of:
    list         List included services
    ensure       Provision included service

commands usage:

usage: main list [-h]

usage: main ensure [-h] service
>>> # create the parser for the "b" command
>>> parser_b = subparsers.add_parser('b', help='b help')
>>> parser_b.add_argument('--baz', choices='XYZ', help='baz help')
usage: PROG [-h] [--foo] {a,b} ...

positional arguments:
  {a,b}   sub-command help
    a     a help
    b     b help