我的 argparse 在顶层只有 3 个标志 (store_true),其他所有内容都通过子解析器处理。当我运行
myprog.py --help
时,输出会像正常的 {sub1, sub2, sub3, sub4, ...}
一样显示所有子命令的列表。所以,默认设置效果很好...
我通常记不起我需要的确切子命令名称及其所有选项。所以我最终做了 2 次帮助查找:
myprog.py --help
myprog.py sub1 --help
我经常这样做,所以我决定将其塞进一个步骤中。我宁愿让我的顶级帮助输出一个巨大的摘要,然后我手动滚动列表。我发现它要快得多(至少对我来说)。
我正在使用 RawDescriptionHelpFormatter,并手动输入长帮助输出。但现在我有很多子命令,管理起来很痛苦。
有没有一种方法只需一次程序调用即可获得详细的帮助输出?
如果没有,我如何迭代 argparse 实例的子解析器,然后从每个子解析器中单独检索帮助输出(稍后我会将其粘合在一起)?
这是我的 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
# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the "a" command
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('bar', type=int, help='bar help')
# 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')
# print main help
print(parser.format_help())
# 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 safe than sorry
for subparsers_action in subparsers_actions:
# get all subparsers and print help
for choice, subparser in subparsers_action.choices.items():
print("Subparser '{}'".format(choice))
print(subparser.format_help())
此示例应适用于 python 2.7 和 python 3。示例解析器来自 有关 argparse 子命令的 Python 2.7 文档。
剩下要做的唯一一件事就是为完整的帮助添加新参数,或替换内置的
-h/--help
。
这是带有自定义帮助处理程序的完整解决方案(几乎所有代码都来自@Adaephon 答案):
import argparse
class _HelpAction(argparse._HelpAction):
def __call__(self, parser, namespace, values, option_string=None):
parser.print_help()
# 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, subparser in subparsers_action.choices.items():
print("Subparser '{}'".format(choice))
print(subparser.format_help())
parser.exit()
# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG', add_help=False) # here we turn off default help action
parser.add_argument('--help', action=_HelpAction, help='help for help if you need some help') # add custom help
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the "a" command
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('bar', type=int, help='bar help')
# 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')
parsed_args = parser.parse_args()
也许更简单的方法是使用
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
在 Adaephon 的示例中迭代子解析器的一种更简单的方法是
for subparser in [parser_a, parser_b]:
subparser.format_help()
Python 确实允许您访问隐藏属性,例如
parser._actions
,但不鼓励这样做。在定义解析器时构建您自己的列表同样容易。用参数做特殊的事情也是如此。 add_argument
和 add_subparser
返回各自的 Action
和 Parser
对象是有原因的。
如果我要创建
ArgumentParser
的子类,我会随意使用 _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]
组合帮助的显示又长又冗余。它可以通过多种方式进行编辑,可以在格式化后,也可以使用特殊的帮助格式化程序。但谁会做出这样的选择呢?
add_subparsers().add_parser()
不仅接受显示在子命令帮助中的 description
,还接受在顶级解析器帮助中用作单行描述的 help=
。
文档将其隐藏在公式中
(但是,可以通过向上面的 add_parser() 提供 help= 参数来给出每个子解析器命令的帮助消息。)
甚至在该句子周围的示例代码中:
>>> # 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
是的,这并不是所有事情的完整帮助,但恕我直言,它很好地涵盖了基本用例,并且不容易发现。
我还可以使用
_choices_actions
打印命令的简短帮助。
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))
if __name__ == '__main__':
parser = argparse.ArgumentParser("TOML FILE OVERWRITE SCRIPT")
subparsers = parser.add_subparsers()
parser_set = subparsers.add_parser('set', help='Set Toml')
parser_set.add_argument('set', help='TOMl file edit set action', action='store_true')
parser_set.add_argument('-n', '--name', type=str, help='Service Name', required=True)
parser_set.add_argument('-s', '--section', type=str, help='Toml Section Name', required=True)
parser_set.add_argument('-k', '--key', type=str, help='Toml Key of Section', required=True)
parser_set.add_argument('-v', '--value', help='New Value', required=True)
args = parser.parse_args()
if args.set:
setter = ConfigurationSetter(args.name, args.section, args.key, args.value)
setter.execute()
else:
print("Ops! Something is wrong, type --help or -h")
你可以查看我的代码,也许会对你有所启发!
这是一个递归整体帮助输出,看起来并不可怕:
def recursive_help(parser):
parser.print_help()
def remove_argument(parser, arg):
for action in parser._actions:
opts = action.option_strings
if (opts and opts[0] == arg) or action.dest == arg:
parser._remove_action(action)
break
for action in parser._action_groups:
for group_action in action._group_actions:
opts = group_action.option_strings
if (opts and opts[0] == arg) or group_action.dest == arg:
action._group_actions.remove(group_action)
return
print("\n")
for action in parser._actions:
if isinstance(action, argparse._SubParsersAction):
subparsers_actions = action.choices.values()
for subparser in subparsers_actions:
subparser: argparse.ArgumentParser
remove_argument(subparser, "help")
print(subparser.format_help().replace("usage: ", "# "))
完全基于@grundic 答案的变体。
这里,解析器处理程序在子类定义中被修改。因此,无需在代码中重新定义“-h”。唯一的区别是使用子类而不是普通的 ArgumentParser。
class VerboseArgumentParser(argparse.ArgumentParser):
def format_help(self):
help_messages = [super().format_help()]
subparsers_actions = [
action
for action in self._actions
if isinstance(action, argparse._SubParsersAction)
]
for action in subparsers_actions:
for choice, subparser in action.choices.items():
help_messages.append(subparser.format_help())
return '\n\n'.join(help_messages)
parser = VerboseArgumentParser(...)
# The rest of the code is intact.
您还可以使用重新定义子解析器帮助消息来进一步改进此解决方案
这是结果输出的示例:
usage: program.py [-h] {sub1,sub2} ...
positional arguments:
{sub1, sub2} Subcommand choice help.
sub1 Subcommand 1 help.
sub2 Subcommand 2 help.
optional arguments:
-h, --help show this help message and exit
usage: program.py sub1 [-h] [--opt1]
optional arguments:
-h, --help show this help message and exit
--opt1 OPT1 Option1 help message.
usage: program.py sub2 [-h] [--opt2 OPT2] [--opt3 OPT3]
optional arguments:
-h, --help show this help message and exit
--opt2 OPT2 Option2 help message.
--opt3 OPT3 Option3 help message.