Command tree

The --tree flag prints the whole hierarchy of nested subcommands at once, with their one-line descriptions. It is a constant-cost overview of a CLI’s shape: without it, mapping an unfamiliar command set means walking --help screens one level at a time, or generating a full man page or completion spec dump.

It is also the hierarchical companion of --show-params: where the parameter table flattens the command tree into dotted IDs (cli.subcommand.param), --tree renders the skeleton those IDs hang off, without the parameter noise.

--tree flag

The flag is part of the default options, so every CLI built with @command or @group exposes it:

from click_extra import argument, group

@group
def observatory():
    """Weather observatory control center."""

@observatory.group(aliases=["st"])
def station():
    """Manage remote weather stations."""

@station.command()
def calibrate():
    """Recalibrate the sensors."""

@station.command(deprecated=True)
def reboot():
    """Power-cycle the station."""

@observatory.command()
@argument("city")
def report(city):
    """Print the forecast for a city."""

@observatory.command()
def status():
    """Report the current conditions."""

@observatory.command(hidden=True)
def diagnose():
    """Internal diagnostics."""
$ observatory --tree
observatory                     Weather observatory control center.
├── help [COMMAND_PATH]...      Show help for a command.
├── report CITY                 Print the forecast for a city.
├── station (st)                Manage remote weather stations.
│   ├── calibrate               Recalibrate the sensors.
│   ├── help [COMMAND_PATH]...  Show help for a command.
│   └── reboot                  (Deprecated) Power-cycle the station.
└── status                      Report the current conditions.

The rendering mirrors help screens: subcommands are discovered dynamically (so lazily-registered commands are included), listed in the same order, hidden ones are skipped, aliases are parenthesized, operand metavars follow each name like on the usage line (report CITY), and deprecated commands carry their (Deprecated) marker. Descriptions are column-aligned and wrap at the terminal width, with the tree rail running through the wrapped lines. Everything is styled with the same theme slots as help screens (invoked_command for the root, subcommand and alias below, metavar for operands), so the tree follows --theme and --color like every other output.

Options are deliberately absent from the tree: their exhaustive inventory is --show-params’ job.

Like the completion spec, the tree is a point-in-time snapshot: a group that computes its subcommands from external state (a loaded application, installed plugins, a scanned directory) renders exactly what its list_commands returns at that moment.

Accessibility

Box-drawing characters are hostile to screen readers, which either skip them or spell out their Unicode names. Under accessibility mode, the rail degrades to the pure-ASCII set tree(1) uses with --charset=ascii:

$ observatory --accessible --tree
observatory                     Weather observatory control center.
|-- help [COMMAND_PATH]...      Show help for a command.
|-- report CITY                 Print the forecast for a city.
|-- station (st)                Manage remote weather stations.
|   |-- calibrate               Recalibrate the sensors.
|   |-- help [COMMAND_PATH]...  Show help for a command.
|   `-- reboot                  (Deprecated) Power-cycle the station.
`-- status                      Report the current conditions.

Foreign CLIs

click-extra wrap --tree -- SCRIPT resolves a target, loads its Click command, and prints its tree without running it. SCRIPT is resolved the same way as for the other introspection modes, so with uvx nothing needs to be installed up front:

$ uvx --from click-extra --with flask click-extra wrap --tree -- flask
$ wrap --tree -- flask
Error: Could not locate a Flask application. Use the 'flask --app' option, 'FLASK_APP' environment variable, or a 'wsgi.py' or 'app.py' file in the current directory.

flask       A general utility script for Flask applications.
├── routes  Show the routes for the app.
├── run     Run a development server.
└── shell   Run a shell in the app context.

The error line above is Flask itself: its group computes part of its command list from the application, and without one only the static commands render. That is the point-in-time snapshot caveat made visible.

Extra arguments after SCRIPT navigate into nested command groups and re-root the tree at the resolved node, exactly like subcommand drilling for --show-params:

$ click-extra wrap --tree -- flask run

Structure vs. inventory

--tree and --show-params are two projections of the same walk, and answer different questions:

  • --tree is command-level, hierarchical, static: which subcommands exist and how they nest. No values, no options.

  • --show-params is parameter-level, flat, runtime: one row per parameter with its type, environment variable, default, current value and provenance.

The tree is a human rendering only. Machine-readable hierarchies are already covered: --show-params --table-format json serializes every parameter with its dotted path, and --carapace serializes the whole command-and-flag tree as YAML.

Standalone option

Use the @tree_option decorator to add the flag to a plain Click CLI, outside of Click Extra’s @command and @group:

import click

from click_extra import tree_option

@click.group
@tree_option
def produce():
    """Manage a produce stand."""

@produce.command()
def restock():
    """Restock the shelves."""
$ produce --tree
produce      Manage a produce stand.
└── restock  Restock the shelves.

click_extra.tree API

        classDiagram
  ExtraOption <|-- TreeOption
    

Render Click command hierarchies as trees.

Provides the --tree flag: a constant-cost overview of a CLI’s nested subcommands, where the only alternative is walking --help screens one level at a time or generating a full man page or completion spec dump.

This is the command-level, hierarchical companion of --show-params: where the parameter table flattens the hierarchy into dotted id paths (cli.subcommand.param), the tree renders the skeleton those paths hang off. Both share the same dynamic discovery (click.Group.list_commands() / click.Group.get_command() via iter_subcommands()) and the same one-line descriptions (full_short_help()).

The tree is a human rendering only: machine-readable hierarchies are already served by --show-params --table-format json (parameters with dotted paths) and the Carapace exporter (click_extra.carapace, a YAML command-and-flag tree).

Subcommands are discovered through a live-context walk, so lazily-registered commands are included, and the view hides behind a dedicated eager flag, so the plain help screen stays untouched. See Upstream for how this compares to the tree views of neighboring packages.

class click_extra.tree.TreeGlyphs(branch, last, pipe, space)[source]

Bases: object

The four drawing strings composing a tree’s left-hand rail.

branch: str

Connector of a subcommand with siblings below it.

last: str

Connector of the last subcommand of its level.

pipe: str

Rail continuation under a branch connector.

space: str

Rail continuation under a last connector.

click_extra.tree.BOX_GLYPHS = TreeGlyphs(branch='├── ', last='└── ', pipe='│   ', space='    ')

Unicode box-drawing rail, the same set drawn by tree(1).

The default table format (rounded-outline) already emits box-drawing characters, so the tree matches the rest of Click Extra’s terminal output.

click_extra.tree.ASCII_GLYPHS = TreeGlyphs(branch='|-- ', last='`-- ', pipe='|   ', space='    ')

Pure-ASCII rail, the same set drawn by tree(1) under --charset=ascii.

Selected when accessibility mode is active (ACCESSIBLE): box-drawing characters are hostile to screen readers, which either skip them or spell out their Unicode names.

click_extra.tree.COLUMN_GAP = 2

Blank cells between the tree rail column and the description column.

click_extra.tree.MIN_DESCRIPTION_WIDTH = 20

Floor for the description column’s wrap width.

A deep tree with long labels can push the description column close to (or past) the terminal’s right edge. Below this floor, wrapping would shred sentences into confetti, so the column keeps this width and overflows the terminal instead.

click_extra.tree.render_command_tree(command, prog_name=None, ctx=None, width=None)[source]

Render the hierarchy rooted at command as a tree with descriptions.

Reuses ctx when given (like the live invocation context), otherwise builds a throwaway one with resilient_parsing=True. The root line is labeled with prog_name when given, else the context’s command path.

Each node carries the command name, its aliases, its operand metavars (mirroring the usage line) and a column-aligned description from full_short_help(), so deprecated commands carry their (Deprecated) marker. Everything is styled with the same theme slots as help screens (invoked_command for the root, subcommand and alias for children, metavar for operands), so the tree follows --theme and --color. The rail switches from box-drawing to ASCII when accessibility mode is active on the context (see ACCESSIBLE).

Descriptions wrap at width, resolved like help screens when not given (ctx.make_formatter(), honoring the terminal_width and max_content_width context settings). Wrapped lines keep the tree rail running through the description column. A label wider than the column (typically a long script path as the root, under wrap --tree) keeps its line to itself and hangs its description underneath, at the column.

Note

The tree is a point-in-time snapshot: groups computing their subcommands from external state (plugins, scanned directories) render exactly what click.Group.list_commands() returns at that moment, in listing order. Cloup section groupings are not rendered.

Caution

A non-group command renders as a single root line: valid, just not very interesting. Like a man page, the output stays meaningful for every command type.

Return type:

str

class click_extra.tree.TreeOption(param_decls=None, is_flag=True, expose_value=False, is_eager=True, help='Show the tree of nested subcommands and exit.', **kwargs)[source]

Bases: ExtraOption

A pre-configured --tree flag that prints the hierarchy of nested subcommands and exits.

Eager and value-less, like ManOption. Part of the default option set injected by default_params(), so every @command and @group exposes it. Use @tree_option to add it to a plain Click CLI.

Note

The flag is named --tree, not --show-tree or --commands.

Rendering a hierarchy as a tree is conventionally named tree across ecosystems: eza --tree, lsblk --tree, poetry show --tree, cargo tree, pstree(1) and tree(1) itself (ps --forest being the lone dissenter). --commands would suggest the flat listing --help already provides.

The bare noun also lines up with the neighbouring --man, --version and --help informational flags; --show-params is the historical outlier, not the pattern. A flag was preferred over a registered tree subcommand, which could collide with the user’s own command namespace.

print_tree(ctx, param, value)[source]

Render and print the invoked command’s subcommand tree, then exit.