Search code examples
pythoncommand-line-interfacepython-click

How to handle sub-group help message


I am trying to understand some implementation details about Click. I have the following example code:

#cli.py

import click


@click.group()
def cli():
    pass


@cli.group()
def show():
    """ Define the environment of the product """
    pass


@show.command()
def name():
    click.echo("run show name command")


@show.command()
def height():
    click.echo("run show height command")


if __name__ == "__main__":
    cli()

With this code, name and height are sub-commands of the show group. However, semantically, that doesn't really make sense. They are more so arguments of a 'show' command.

I know I can have a command with an "attribute" argument from which I can call a different function based on the string value of "attribute." However, I feel that would get tedious to maintain once there are several possibilities for "attribute."

I believe I could still use the above structure if I were able to edit the help message. When I run cli.py show --help, I get the default help message for a command group:

Usage: cli.py show [OPTIONS] COMMAND [ARGS]...

  Define the environment of the product

Options:
  --help  Show this message and exit.

Commands:
  height
  name

Is there a way to edit the help message to change "Commands" to "Arguments"? I know how to change the usage statement in the click.group() decorator, but I'm not sure how to modify the help message itself.

The help message I would like to achieve is as follows:

Usage: cli.py show [OPTIONS] ARG

  Define the environment of the product

Options:
  --help  Show this message and exit.

Arguments:
  height
  name

Is something like this possible?

I'm using python3 and Click 6.7


Solution

  • You can change the message given by the sub commands help, by using a custom class for the group. The custom class can be made by inheriting from click.Group and changing the format_commands() method like:

    Custom Class:

    class HelpAsArgs(click.Group):
        # change the section head of sub commands to "Arguments"
    
        def format_commands(self, ctx, formatter):
            rows = []
            for subcommand in self.list_commands(ctx):
                cmd = self.get_command(ctx, subcommand)
                if cmd is None:
                    continue
    
                help = cmd.short_help or ''
                rows.append((subcommand, help))
    
            if rows:
                with formatter.section('Arguments'):
                    formatter.write_dl(rows)
    

    Test Code:

    import click
    
    @click.group()
    def cli():
        pass
    
    @cli.group(cls=HelpAsArgs)
    def show():
        """ Define the environment of the product """
        pass
    
    @show.command()
    def name():
        click.echo("run show name command")
    
    @show.command()
    def height():
        click.echo("run show height command")
    
    if __name__ == "__main__":
        commands = (
            'show',
            'show --help',
            '--help',
        )
    
        import sys, time
    
        time.sleep(1)
        print('Click Version: {}'.format(click.__version__))
        print('Python Version: {}'.format(sys.version))
        for command in commands:
            try:
                time.sleep(0.1)
                print('-----------')
                print('> ' + command)
                time.sleep(0.1)
                cli(command.split())
    
            except BaseException as exc:
                if str(exc) != '0' and \
                        not isinstance(exc, (click.ClickException, SystemExit)):
                    raise
    

    Results:

    Click Version: 6.7
    Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
    -----------
    > show
    Usage: test.py show [OPTIONS] COMMAND [ARGS]...
    
      Define the environment of the product
    
    Options:
      --help  Show this message and exit.
    
    Arguments:
      height
      name
    -----------
    > show --help
    Usage: test.py show [OPTIONS] COMMAND [ARGS]...
    
      Define the environment of the product
    
    Options:
      --help  Show this message and exit.
    
    Arguments:
      height
      name
    -----------
    > --help
    Usage: test.py [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --help  Show this message and exit.
    
    Commands:
      show  Define the environment of the product