This code:
#!/usr/bin env python3
import click
def f(*a, **kw):
print(a, kw)
commands = [click.Command("cmd1", callback=f), click.Command("cmd2", callback=f)]
cli = click.Group(commands={c.name: c for c in commands})
if __name__ == "__main__":
cli()
generates this help:
# Usage: cli.py [OPTIONS] COMMAND [ARGS]...
# Options:
# --help Show this message and exit.
# Commands:
# cmd1
# cmd2
I have a lot of subcommands, so I want to divide them into sections in the help like this:
# Usage: cli.py [OPTIONS] COMMAND [ARGS]...
# Options:
# --help Show this message and exit.
# Commands:
# cmd1
# cmd2
#
# Extra other commands:
# cmd3
# cmd4
How can I split the commands into sections in the help like that, without affecting the functionality?
If you define your own group class you can overide the help generation like:
class SectionedHelpGroup(click.Group):
"""Sections commands into help groups"""
def __init__(self, *args, **kwargs):
self.grouped_commands = kwargs.pop('grouped_commands', {})
commands = {}
for group, command_list in self.grouped_commands.items():
for cmd in command_list:
cmd.help_group = group
commands[cmd.name] = cmd
super(SectionedHelpGroup, self).__init__(
*args, commands=commands, **kwargs)
def command(self, *args, **kwargs):
help_group = kwargs.pop('help_group')
decorator = super(SectionedHelpGroup, self).command(*args, **kwargs)
def new_decorator(f):
cmd = decorator(f)
cmd.help_group = help_group
self.grouped_commands.setdefault(help_group, []).append(cmd)
return cmd
return new_decorator
def format_commands(self, ctx, formatter):
for group, cmds in self.grouped_commands.items():
rows = []
for subcommand in self.list_commands(ctx):
cmd = self.get_command(ctx, subcommand)
if cmd is None or cmd.help_group != group:
continue
rows.append((subcommand, cmd.short_help or ''))
if rows:
with formatter.section(group):
formatter.write_dl(rows)
Pass the Custom Class to click.group()
using cls
parameter like:
@click.group(cls=SectionedHelpGroup)
def cli():
""""""
when defining commands, pass the help group the command belongs to like:
@cli.command(help_group='my help group')
def a_command(*args, **kwargs):
....
This works because click is a well designed OO framework. The @click.group()
decorator usually instantiates a click.Group
object but allows this behavior to be over-ridden with the cls
parameter. So it is a relatively easy matter to inherit from click.Group
in our own class and over ride the desired methods.
In this case, we hook the command()
decorator to allow the help_group
to be identified. We also override the format_commands()
method to print the commands help into the groups.
import click
def f(*args, **kwargs):
click.echo(args, kwargs)
commands = {
'help group 1': [
click.Command("cmd1", callback=f),
click.Command("cmd2", callback=f)
],
'help group 2': [
click.Command("cmd3", callback=f),
click.Command("cmd4", callback=f)
]
}
cli = SectionedHelpGroup(grouped_commands=commands)
@cli.command(help_group='help group 3')
def a_command(*args, **kwargs):
"""My command"""
click.echo(args, kwargs)
if __name__ == "__main__":
cli(['--help'])
Usage: test.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
help group 1:
cmd1
cmd2
help group 2:
cmd3
cmd4
help group 3:
a_command My command