Command groups and subcommands

Click · Practical · 11 min

Most real tools have several actions (init, run, deploy); groups organise them under one entry point.

A group is a command that dispatches to subcommands:

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

@cli.command()
def init(): ...

Now cli init runs init, and cli --help lists the subcommands with their short help. Commands can also be defined separately and attached with cli.add_command(cmd), which is how large CLIs split commands across modules.

Options declared on the group belong before the subcommand: cli --verbose init, not cli init --verbose. The group's function runs first (useful for setup such as configuring logging), then the subcommand's.

Groups nest: a subcommand can itself be a group, giving cli db migrate.

Useful settings:

  • invoke_without_command=True lets the group run when called alone; check ctx.invoked_subcommand to know whether a subcommand follows.
  • chain=True allows several subcommands in one invocation, like cli build test deploy.
  • @cli.command("custom-name") sets a name different from the function's; Click also converts underscores to dashes automatically.
  • no_args_is_help=True shows help instead of an error when no subcommand is given (the default for groups).

Commands are listed alphabetically in help by default. Sharing state between the group and its subcommands is done with the context object, covered next.

Example

import click

@click.group()
@click.option("--verbose", is_flag=True, help="Print extra details.")
def cli(verbose):
    """Manage the project."""
    if verbose:
        click.echo("[verbose mode]")

@cli.command()
@click.argument("name")
def init(name):
    click.echo(f"Initialised {name}")

@cli.group()
def db():
    """Database commands."""

@db.command("migrate")
@click.option("--to", default="head")
def db_migrate(to):
    click.echo(f"Migrating to {to}")

cli.main(["--verbose", "init", "demo"], standalone_mode=False)  # [verbose mode] / Initialised demo
cli.main(["db", "migrate", "--to", "042"], standalone_mode=False)  # Migrating to 042

Remember

  • @click.group() plus @group.command() builds subcommands
  • Group options go before the subcommand name
  • Groups nest; add_command assembles commands from other modules

Watch out for

  • Passing a group option after the subcommand (cli init --verbose), which the subcommand rejects.

Check yourself

  1. Given a group cli with option --verbose and subcommand init, which invocation is valid?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: click.palletsprojects.com/en/stable/commands-and-groups/