Machine-readable help¶
CLIs are increasingly read by something other than a person: a script wiring two tools together, a package manager, a language model deciding which flag to pass. All of them start from --help, and --help is the worst possible source. It is written for a human at a terminal: it wraps to a width, carries ANSI styling, and expresses structure as layout, so a reader has to recover the option list from column alignment and guess where a description ends.
None of that parsing is necessary. The structure was there before it was formatted, and Click Extra hands it over directly.
click_extra.command_doc extracts a command once, into a CommandDoc, and renders that model several ways. Every command gets a --help-format FORMAT option reaching them, and click-extra wrap does the same for a CLI whose author never heard of Click Extra.
A program wants to know |
Ask for |
Documented in |
|---|---|---|
What does this command do, and what does it accept? |
|
this page |
What are the parameters, their types, and where did each value come from? |
||
What is the resolved configuration, as a file I can edit and replay? |
||
What subcommands exist? |
|
|
How do I complete this in a shell? |
|
from click_extra import Choice, argument, command, echo, option
@command(context_settings={"show_envvar": True})
@argument("city", help="Name of the city to report on.")
@option(
"--units",
type=Choice(["celsius", "fahrenheit"]),
default="celsius",
help="Temperature scale to display.",
)
def weather(city, units):
"""Report the current temperature for a city."""
echo(f"{city}: 21 degrees {units}.")
The --help-format option¶
One option carrying a format, rather than a flag per format. A CLI’s option list is the most expensive real estate in its help screen, and every reader pays for it whether or not they will ever export anything: a family of --help-json, --help-markdown and --help-carapace flags would widen the label column of every screen, forever. Here a new format costs a registry entry and nothing on screen.
JSON¶
$ weather --help-format json
{
"name": "weather",
"short_help": "Report the current temperature for a city.",
"version": null,
"synopsis": "weather [OPTIONS] CITY",
"description": "Report the current temperature for a city.",
"arguments": [
{
"metavar": "CITY",
"help": "Name of the city to report on."
}
],
"option_groups": [
{
"title": "Options",
"help": null,
"options": [
{
"names": [
"--units"
],
"spec": "--units [celsius|fahrenheit]",
"metavar": "[celsius|fahrenheit]",
"choices": [
"celsius",
"fahrenheit"
],
"help": "Temperature scale to display.",
"required": false,
"optional_value": false
},
{
"names": [
"--help",
"-h"
],
"spec": "--help / -h",
"metavar": null,
"choices": null,
"help": "Show this message and exit.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Configuration options",
"help": null,
"options": [
{
"names": [
"--config"
],
"spec": "--config LOCATION",
"metavar": "LOCATION",
"choices": null,
"help": "Location of the configuration file. Supports local path with glob patterns or remote URL.",
"required": false,
"optional_value": false
},
{
"names": [
"--no-config"
],
"spec": "--no-config",
"metavar": null,
"choices": null,
"help": "Ignore all configuration files and only use command line parameters and environment variables.",
"required": false,
"optional_value": false
},
{
"names": [
"--validate-config"
],
"spec": "--validate-config LOCATION",
"metavar": "LOCATION",
"choices": null,
"help": "Validate the configuration file and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--export-config"
],
"spec": "--export-config FORMAT",
"metavar": "FORMAT",
"choices": [
"toml",
"yaml",
"json",
"json5",
"jsonc",
"hjson",
"xml",
"plist"
],
"help": "Export the configuration in the selected format to <stdout>, then exit.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Output options",
"help": null,
"options": [
{
"names": [
"--accessible"
],
"spec": "--accessible",
"metavar": null,
"choices": null,
"help": "Accessibility mode: disable colors and render tables in a borderless, screen-reader-friendly format.",
"required": false,
"optional_value": false
},
{
"names": [
"--color"
],
"spec": "--color[=auto|always|never]",
"metavar": "[auto|always|never]",
"choices": [
"auto",
"always",
"never"
],
"help": "Colorize the output. A bare --color is the same as --color=always.",
"required": false,
"optional_value": true
},
{
"names": [
"--no-color"
],
"spec": "--no-color",
"metavar": null,
"choices": null,
"help": "Disable colorization (alias of --color=never).",
"required": false,
"optional_value": false
},
{
"names": [
"--progress",
"--no-progress"
],
"spec": "--progress / --no-progress",
"metavar": null,
"choices": null,
"help": "Show progress indicators during long operations. Disabled for non-interactive output (pipes, dumb terminals, CI) and by --accessible.",
"required": false,
"optional_value": false
},
{
"names": [
"--theme"
],
"spec": "--theme [auto|dark|dracula|light|manpage|monokai|nord|solarized-dark]",
"metavar": "[auto|dark|dracula|light|manpage|monokai|nord|solarized-dark]",
"choices": [
"auto",
"dark",
"dracula",
"light",
"manpage",
"monokai",
"nord",
"solarized-dark"
],
"help": "Color theme used for help screens.",
"required": false,
"optional_value": false
},
{
"names": [
"--table-format"
],
"spec": "--table-format FORMAT",
"metavar": "FORMAT",
"choices": [
"aligned",
"asciidoc",
"colon-grid",
"csv",
"csv-excel",
"csv-excel-tab",
"csv-unix",
"double-grid",
"double-outline",
"fancy-grid",
"fancy-outline",
"github",
"grid",
"heavy-grid",
"heavy-outline",
"hjson",
"html",
"jira",
"json",
"json5",
"jsonc",
"latex",
"latex-booktabs",
"latex-longtable",
"latex-raw",
"mediawiki",
"mixed-grid",
"mixed-outline",
"moinmoin",
"orgtbl",
"outline",
"pipe",
"plain",
"presto",
"pretty",
"psql",
"rounded-grid",
"rounded-outline",
"rst",
"simple",
"simple-grid",
"simple-outline",
"textile",
"toml",
"tsv",
"unsafehtml",
"vertical",
"xml",
"yaml",
"youtrack"
],
"help": "Rendering style of tables.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Logging options",
"help": null,
"options": [
{
"names": [
"--verbosity"
],
"spec": "--verbosity LEVEL",
"metavar": "LEVEL",
"choices": [
"CRITICAL",
"ERROR",
"WARNING",
"INFO",
"DEBUG"
],
"help": "Either CRITICAL, ERROR, WARNING, INFO, DEBUG.",
"required": false,
"optional_value": false
},
{
"names": [
"--verbose",
"-v"
],
"spec": "--verbose / -v",
"metavar": null,
"choices": null,
"help": "Increase the default WARNING verbosity by one level for each additional repetition of the option.",
"required": false,
"optional_value": false
},
{
"names": [
"--quiet",
"-q"
],
"spec": "--quiet / -q",
"metavar": null,
"choices": null,
"help": "Decrease the default WARNING verbosity by one level for each additional repetition of the option.",
"required": false,
"optional_value": false
},
{
"names": [
"--debug"
],
"spec": "--debug",
"metavar": null,
"choices": null,
"help": "Shorthand for --verbosity DEBUG.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Introspection options",
"help": null,
"options": [
{
"names": [
"--time",
"--no-time"
],
"spec": "--time / --no-time",
"metavar": null,
"choices": null,
"help": "Measure and print elapsed execution time.",
"required": false,
"optional_value": false
},
{
"names": [
"--params"
],
"spec": "--params",
"metavar": null,
"choices": null,
"help": "Show all CLI parameters, their provenance, defaults and value, then exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--tree"
],
"spec": "--tree",
"metavar": null,
"choices": null,
"help": "Show the tree of nested subcommands and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--man"
],
"spec": "--man",
"metavar": null,
"choices": null,
"help": "Read the command's manual page and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--help-format"
],
"spec": "--help-format [carapace|json|json-full|man|markdown|markdown-full]",
"metavar": "[carapace|json|json-full|man|markdown|markdown-full]",
"choices": [
"carapace",
"json",
"json-full",
"man",
"markdown",
"markdown-full"
],
"help": "Render the command in the given format and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--version"
],
"spec": "--version",
"metavar": null,
"choices": null,
"help": "Show the version and exit.",
"required": false,
"optional_value": false
}
]
}
],
"subcommands": [],
"examples": [],
"environment": [
{
"variable": "WEATHER_UNITS",
"help": "Temperature scale to display."
},
{
"variable": "WEATHER__CLICK_DEFAULT_HELP",
"help": "Show this message and exit."
},
{
"variable": "WEATHER_CONFIG",
"help": "Location of the configuration file. Supports local path with glob patterns or remote URL."
},
{
"variable": "WEATHER_VALIDATE_CONFIG",
"help": "Validate the configuration file and exit."
},
{
"variable": "WEATHER_EXPORT_CONFIG",
"help": "Export the configuration in the selected format to <stdout>, then exit."
},
{
"variable": "WEATHER_ACCESSIBLE",
"help": "Accessibility mode: disable colors and render tables in a borderless, screen-reader-friendly format."
},
{
"variable": "WEATHER_COLOR",
"help": "Colorize the output. A bare --color is the same as --color=always."
},
{
"variable": "WEATHER_NO_COLOR",
"help": "Disable colorization (alias of --color=never)."
},
{
"variable": "WEATHER_PROGRESS",
"help": "Show progress indicators during long operations. Disabled for non-interactive output (pipes, dumb terminals, CI) and by --accessible."
},
{
"variable": "WEATHER_THEME",
"help": "Color theme used for help screens."
},
{
"variable": "WEATHER_TABLE_FORMAT",
"help": "Rendering style of tables."
},
{
"variable": "WEATHER_VERBOSITY",
"help": "Either CRITICAL, ERROR, WARNING, INFO, DEBUG."
},
{
"variable": "WEATHER_VERBOSE",
"help": "Increase the default WARNING verbosity by one level for each additional repetition of the option."
},
{
"variable": "WEATHER_QUIET",
"help": "Decrease the default WARNING verbosity by one level for each additional repetition of the option."
},
{
"variable": "WEATHER_DEBUG",
"help": "Shorthand for --verbosity DEBUG."
},
{
"variable": "WEATHER_TIME",
"help": "Measure and print elapsed execution time."
},
{
"variable": "WEATHER_PARAMS",
"help": "Show all CLI parameters, their provenance, defaults and value, then exit."
},
{
"variable": "WEATHER_TREE",
"help": "Show the tree of nested subcommands and exit."
},
{
"variable": "WEATHER_MAN",
"help": "Read the command's manual page and exit."
},
{
"variable": "WEATHER_HELP_FORMAT",
"help": "Render the command in the given format and exit."
},
{
"variable": "WEATHER_VERSION",
"help": "Show the version and exit."
}
],
"files": [
"~/.config/weather/{*.toml,*.yaml,*.yml,*.json,*.json5,*.jwcc,*.jsonc,*.hjson,*.ini,*.xml,*.plist,*.sqlite,*.sqlite3,*.conf,pyproject.toml}"
],
"exit_status": [
{
"code": "0",
"meaning": "Success."
},
{
"code": "1",
"meaning": "A runtime error, an aborted prompt (Ctrl-D), or a declined confirmation."
},
{
"code": "2",
"meaning": "A usage error: unknown option, invalid value, missing operand, or an unparsable configuration file."
},
{
"code": "130",
"meaning": "Interrupted with Ctrl-C: the process ends by SIGINT, reported as 130."
}
]
}
An option whose type enumerates its values carries them in choices, as a list. Read them from there rather than parsing the metavar: an option is free to replace the [celsius|fahrenheit] rendering with a short placeholder to keep its help screen readable, and --table-format FORMAT does exactly that for its fifty values. choices is null for every other type.
An option whose value is optional (usable bare, with a value attached only when the caller supplies one) reports optional_value: true, and its spec folds in the bracketed [=METAVAR] form instead of a space-separated one. DocOptionItem.attached_value is the property behind that rendering: it strips the metavar’s own brackets first, so a Choice value doesn’t double up.
A group also lists what it dispatches to. Each entry names the subcommand, the other spellings it answers to, and its one-line description:
from click_extra import group
@group(name="observatory")
def observatory():
"""Watch the sky."""
@observatory.command(aliases=["st"])
def station():
"""Manage remote weather stations."""
$ observatory --help-format json
{
"name": "observatory",
"short_help": "Watch the sky.",
"version": null,
"synopsis": "observatory [OPTIONS] COMMAND [ARGS]...",
"description": "Watch the sky.",
"arguments": [],
"option_groups": [
{
"title": "Options",
"help": null,
"options": [
{
"names": [
"--help",
"-h"
],
"spec": "--help / -h",
"metavar": null,
"choices": null,
"help": "Show this message and exit.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Configuration options",
"help": null,
"options": [
{
"names": [
"--config"
],
"spec": "--config LOCATION",
"metavar": "LOCATION",
"choices": null,
"help": "Location of the configuration file. Supports local path with glob patterns or remote URL.",
"required": false,
"optional_value": false
},
{
"names": [
"--no-config"
],
"spec": "--no-config",
"metavar": null,
"choices": null,
"help": "Ignore all configuration files and only use command line parameters and environment variables.",
"required": false,
"optional_value": false
},
{
"names": [
"--validate-config"
],
"spec": "--validate-config LOCATION",
"metavar": "LOCATION",
"choices": null,
"help": "Validate the configuration file and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--export-config"
],
"spec": "--export-config FORMAT",
"metavar": "FORMAT",
"choices": [
"toml",
"yaml",
"json",
"json5",
"jsonc",
"hjson",
"xml",
"plist"
],
"help": "Export the configuration in the selected format to <stdout>, then exit.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Output options",
"help": null,
"options": [
{
"names": [
"--accessible"
],
"spec": "--accessible",
"metavar": null,
"choices": null,
"help": "Accessibility mode: disable colors and render tables in a borderless, screen-reader-friendly format.",
"required": false,
"optional_value": false
},
{
"names": [
"--color"
],
"spec": "--color[=auto|always|never]",
"metavar": "[auto|always|never]",
"choices": [
"auto",
"always",
"never"
],
"help": "Colorize the output. A bare --color is the same as --color=always.",
"required": false,
"optional_value": true
},
{
"names": [
"--no-color"
],
"spec": "--no-color",
"metavar": null,
"choices": null,
"help": "Disable colorization (alias of --color=never).",
"required": false,
"optional_value": false
},
{
"names": [
"--progress",
"--no-progress"
],
"spec": "--progress / --no-progress",
"metavar": null,
"choices": null,
"help": "Show progress indicators during long operations. Disabled for non-interactive output (pipes, dumb terminals, CI) and by --accessible.",
"required": false,
"optional_value": false
},
{
"names": [
"--theme"
],
"spec": "--theme [auto|dark|dracula|light|manpage|monokai|nord|solarized-dark]",
"metavar": "[auto|dark|dracula|light|manpage|monokai|nord|solarized-dark]",
"choices": [
"auto",
"dark",
"dracula",
"light",
"manpage",
"monokai",
"nord",
"solarized-dark"
],
"help": "Color theme used for help screens.",
"required": false,
"optional_value": false
},
{
"names": [
"--table-format"
],
"spec": "--table-format FORMAT",
"metavar": "FORMAT",
"choices": [
"aligned",
"asciidoc",
"colon-grid",
"csv",
"csv-excel",
"csv-excel-tab",
"csv-unix",
"double-grid",
"double-outline",
"fancy-grid",
"fancy-outline",
"github",
"grid",
"heavy-grid",
"heavy-outline",
"hjson",
"html",
"jira",
"json",
"json5",
"jsonc",
"latex",
"latex-booktabs",
"latex-longtable",
"latex-raw",
"mediawiki",
"mixed-grid",
"mixed-outline",
"moinmoin",
"orgtbl",
"outline",
"pipe",
"plain",
"presto",
"pretty",
"psql",
"rounded-grid",
"rounded-outline",
"rst",
"simple",
"simple-grid",
"simple-outline",
"textile",
"toml",
"tsv",
"unsafehtml",
"vertical",
"xml",
"yaml",
"youtrack"
],
"help": "Rendering style of tables.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Logging options",
"help": null,
"options": [
{
"names": [
"--verbosity"
],
"spec": "--verbosity LEVEL",
"metavar": "LEVEL",
"choices": [
"CRITICAL",
"ERROR",
"WARNING",
"INFO",
"DEBUG"
],
"help": "Either CRITICAL, ERROR, WARNING, INFO, DEBUG.",
"required": false,
"optional_value": false
},
{
"names": [
"--verbose",
"-v"
],
"spec": "--verbose / -v",
"metavar": null,
"choices": null,
"help": "Increase the default WARNING verbosity by one level for each additional repetition of the option.",
"required": false,
"optional_value": false
},
{
"names": [
"--quiet",
"-q"
],
"spec": "--quiet / -q",
"metavar": null,
"choices": null,
"help": "Decrease the default WARNING verbosity by one level for each additional repetition of the option.",
"required": false,
"optional_value": false
},
{
"names": [
"--debug"
],
"spec": "--debug",
"metavar": null,
"choices": null,
"help": "Shorthand for --verbosity DEBUG.",
"required": false,
"optional_value": false
}
]
},
{
"title": "Introspection options",
"help": null,
"options": [
{
"names": [
"--time",
"--no-time"
],
"spec": "--time / --no-time",
"metavar": null,
"choices": null,
"help": "Measure and print elapsed execution time.",
"required": false,
"optional_value": false
},
{
"names": [
"--params"
],
"spec": "--params",
"metavar": null,
"choices": null,
"help": "Show all CLI parameters, their provenance, defaults and value, then exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--tree"
],
"spec": "--tree",
"metavar": null,
"choices": null,
"help": "Show the tree of nested subcommands and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--man"
],
"spec": "--man",
"metavar": null,
"choices": null,
"help": "Read the command's manual page and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--help-format"
],
"spec": "--help-format [carapace|json|json-full|man|markdown|markdown-full]",
"metavar": "[carapace|json|json-full|man|markdown|markdown-full]",
"choices": [
"carapace",
"json",
"json-full",
"man",
"markdown",
"markdown-full"
],
"help": "Render the command in the given format and exit.",
"required": false,
"optional_value": false
},
{
"names": [
"--version"
],
"spec": "--version",
"metavar": null,
"choices": null,
"help": "Show the version and exit.",
"required": false,
"optional_value": false
}
]
}
],
"subcommands": [
{
"name": "help",
"aliases": [],
"short_help": "Show help for a command."
},
{
"name": "station",
"aliases": [
"st"
],
"short_help": "Manage remote weather stations."
}
],
"examples": [],
"environment": [
{
"variable": "OBSERVATORY__CLICK_DEFAULT_HELP",
"help": "Show this message and exit."
},
{
"variable": "OBSERVATORY_CONFIG",
"help": "Location of the configuration file. Supports local path with glob patterns or remote URL."
},
{
"variable": "OBSERVATORY_VALIDATE_CONFIG",
"help": "Validate the configuration file and exit."
},
{
"variable": "OBSERVATORY_EXPORT_CONFIG",
"help": "Export the configuration in the selected format to <stdout>, then exit."
},
{
"variable": "OBSERVATORY_ACCESSIBLE",
"help": "Accessibility mode: disable colors and render tables in a borderless, screen-reader-friendly format."
},
{
"variable": "OBSERVATORY_COLOR",
"help": "Colorize the output. A bare --color is the same as --color=always."
},
{
"variable": "OBSERVATORY_NO_COLOR",
"help": "Disable colorization (alias of --color=never)."
},
{
"variable": "OBSERVATORY_PROGRESS",
"help": "Show progress indicators during long operations. Disabled for non-interactive output (pipes, dumb terminals, CI) and by --accessible."
},
{
"variable": "OBSERVATORY_THEME",
"help": "Color theme used for help screens."
},
{
"variable": "OBSERVATORY_TABLE_FORMAT",
"help": "Rendering style of tables."
},
{
"variable": "OBSERVATORY_VERBOSITY",
"help": "Either CRITICAL, ERROR, WARNING, INFO, DEBUG."
},
{
"variable": "OBSERVATORY_VERBOSE",
"help": "Increase the default WARNING verbosity by one level for each additional repetition of the option."
},
{
"variable": "OBSERVATORY_QUIET",
"help": "Decrease the default WARNING verbosity by one level for each additional repetition of the option."
},
{
"variable": "OBSERVATORY_DEBUG",
"help": "Shorthand for --verbosity DEBUG."
},
{
"variable": "OBSERVATORY_TIME",
"help": "Measure and print elapsed execution time."
},
{
"variable": "OBSERVATORY_PARAMS",
"help": "Show all CLI parameters, their provenance, defaults and value, then exit."
},
{
"variable": "OBSERVATORY_TREE",
"help": "Show the tree of nested subcommands and exit."
},
{
"variable": "OBSERVATORY_MAN",
"help": "Read the command's manual page and exit."
},
{
"variable": "OBSERVATORY_HELP_FORMAT",
"help": "Render the command in the given format and exit."
},
{
"variable": "OBSERVATORY_VERSION",
"help": "Show the version and exit."
}
],
"files": [
"~/.config/observatory/{*.toml,*.yaml,*.yml,*.json,*.json5,*.jwcc,*.jsonc,*.hjson,*.ini,*.xml,*.plist,*.sqlite,*.sqlite3,*.conf,pyproject.toml}"
],
"exit_status": [
{
"code": "0",
"meaning": "Success."
},
{
"code": "1",
"meaning": "A runtime error, an aborted prompt (Ctrl-D), or a declined confirmation."
},
{
"code": "2",
"meaning": "A usage error: unknown option, invalid value, missing operand, or an unparsable configuration file."
},
{
"code": "130",
"meaning": "Interrupted with Ctrl-C: the process ends by SIGINT, reported as 130."
}
]
}
An alias is invocable, so it is reported wherever the command is: aliases is an empty list for a subcommand that declares none.
Markdown¶
The same command, in what a language model reads most comfortably:
$ weather --help-format markdown
# weather
Report the current temperature for a city.
## Synopsis
```shell-session
$ weather [OPTIONS] CITY
```
## Description
Report the current temperature for a city.
## Arguments
- `CITY`: Name of the city to report on.
## Options
### Options
- `--units [celsius|fahrenheit]`: Temperature scale to display.
- `--help / -h`: Show this message and exit.
### Configuration options
- `--config LOCATION`: Location of the configuration file. Supports local path with glob patterns or remote URL.
- `--no-config`: Ignore all configuration files and only use command line parameters and environment variables.
- `--validate-config LOCATION`: Validate the configuration file and exit.
- `--export-config FORMAT`: Export the configuration in the selected format to <stdout>, then exit. Values: `toml`, `yaml`, `json`, `json5`, `jsonc`, `hjson`, `xml`, `plist`.
### Output options
- `--accessible`: Accessibility mode: disable colors and render tables in a borderless, screen-reader-friendly format.
- `--color[=auto|always|never]`: Colorize the output. A bare --color is the same as --color=always.
- `--no-color`: Disable colorization (alias of --color=never).
- `--progress / --no-progress`: Show progress indicators during long operations. Disabled for non-interactive output (pipes, dumb terminals, CI) and by --accessible.
- `--theme [auto|dark|dracula|light|manpage|monokai|nord|solarized-dark]`: Color theme used for help screens.
- `--table-format FORMAT`: Rendering style of tables. Values: `aligned`, `asciidoc`, `colon-grid`, `csv`, `csv-excel`, `csv-excel-tab`, `csv-unix`, `double-grid`, `double-outline`, `fancy-grid`, `fancy-outline`, `github`, `grid`, `heavy-grid`, `heavy-outline`, `hjson`, `html`, `jira`, `json`, `json5`, `jsonc`, `latex`, `latex-booktabs`, `latex-longtable`, `latex-raw`, `mediawiki`, `mixed-grid`, `mixed-outline`, `moinmoin`, `orgtbl`, `outline`, `pipe`, `plain`, `presto`, `pretty`, `psql`, `rounded-grid`, `rounded-outline`, `rst`, `simple`, `simple-grid`, `simple-outline`, `textile`, `toml`, `tsv`, `unsafehtml`, `vertical`, `xml`, `yaml`, `youtrack`.
### Logging options
- `--verbosity LEVEL`: Either CRITICAL, ERROR, WARNING, INFO, DEBUG. Values: `CRITICAL`, `ERROR`, `WARNING`, `INFO`, `DEBUG`.
- `--verbose / -v`: Increase the default WARNING verbosity by one level for each additional repetition of the option.
- `--quiet / -q`: Decrease the default WARNING verbosity by one level for each additional repetition of the option.
- `--debug`: Shorthand for --verbosity DEBUG.
### Introspection options
- `--time / --no-time`: Measure and print elapsed execution time.
- `--params`: Show all CLI parameters, their provenance, defaults and value, then exit.
- `--tree`: Show the tree of nested subcommands and exit.
- `--man`: Read the command's manual page and exit.
- `--help-format [carapace|json|json-full|man|markdown|markdown-full]`: Render the command in the given format and exit.
- `--version`: Show the version and exit.
## Environment variables
- `WEATHER_UNITS`: Temperature scale to display.
- `WEATHER__CLICK_DEFAULT_HELP`: Show this message and exit.
- `WEATHER_CONFIG`: Location of the configuration file. Supports local path with glob patterns or remote URL.
- `WEATHER_VALIDATE_CONFIG`: Validate the configuration file and exit.
- `WEATHER_EXPORT_CONFIG`: Export the configuration in the selected format to <stdout>, then exit.
- `WEATHER_ACCESSIBLE`: Accessibility mode: disable colors and render tables in a borderless, screen-reader-friendly format.
- `WEATHER_COLOR`: Colorize the output. A bare --color is the same as --color=always.
- `WEATHER_NO_COLOR`: Disable colorization (alias of --color=never).
- `WEATHER_PROGRESS`: Show progress indicators during long operations. Disabled for non-interactive output (pipes, dumb terminals, CI) and by --accessible.
- `WEATHER_THEME`: Color theme used for help screens.
- `WEATHER_TABLE_FORMAT`: Rendering style of tables.
- `WEATHER_VERBOSITY`: Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
- `WEATHER_VERBOSE`: Increase the default WARNING verbosity by one level for each additional repetition of the option.
- `WEATHER_QUIET`: Decrease the default WARNING verbosity by one level for each additional repetition of the option.
- `WEATHER_DEBUG`: Shorthand for --verbosity DEBUG.
- `WEATHER_TIME`: Measure and print elapsed execution time.
- `WEATHER_PARAMS`: Show all CLI parameters, their provenance, defaults and value, then exit.
- `WEATHER_TREE`: Show the tree of nested subcommands and exit.
- `WEATHER_MAN`: Read the command's manual page and exit.
- `WEATHER_HELP_FORMAT`: Render the command in the given format and exit.
- `WEATHER_VERSION`: Show the version and exit.
## Files
- `~/.config/weather/{*.toml,*.yaml,*.yml,*.json,*.json5,*.jwcc,*.jsonc,*.hjson,*.ini,*.xml,*.plist,*.sqlite,*.sqlite3,*.conf,pyproject.toml}`
## Exit status
- `0`: Success.
- `1`: A runtime error, an aborted prompt (Ctrl-D), or a declined confirmation.
- `2`: A usage error: unknown option, invalid value, missing operand, or an unparsable configuration file.
- `130`: Interrupted with Ctrl-C: the process ends by SIGINT, reported as 130.
The format list¶
Format |
What it renders |
|---|---|
|
A Carapace completion spec (YAML), which doubles as a command-and-flag tree. |
|
This command as a JSON object, its direct subcommands listed by name and aliases. |
|
Every command of the tree, under a |
|
This command as a man page: the roff source |
|
This command as a Markdown document, one section per topic. |
|
Every command of the tree as one Markdown document. |
Each row is a HelpFormat member, and --help-format takes its choices from that enum. HELP_FORMATS maps each member to a one-line description. render_help() renders a command in a given format from Python. The option calls that same function at runtime.
Note
The plain and -full variants differ in how much they hand over at once. A plain render describes one command and names its children, so a reader descends one level at a time rather than pulling a whole tree into a context window to answer a question about one leaf. The -full variants are for the opposite job: generating documentation, or diffing a CLI’s whole surface between two releases.
Whatever --color says, these renderings carry no ANSI codes: they are meant to be piped into a parser, which has no use for escape sequences. --help remains the colorized human view.
Installing the artifacts¶
Two of these renderings are installed rather than read: a man page under a man directory, a Carapace spec under Carapace’s. For those, the wrapper takes a destination instead of printing to stdout:
$ click-extra wrap --help-format man --install -- flask
/home/me/.local/share/man/man1/flask.1
/home/me/.local/share/man/man1/flask-run.1
$ click-extra wrap --help-format carapace --install -- flask
/home/me/.config/carapace/specs/flask.yaml
--install means the same thing for both: put this where its consumer looks for it, honoring XDG_DATA_HOME and XDG_CONFIG_HOME. --output-dir DIR writes it somewhere else instead. Both are refused for the other formats, which are documents nothing goes looking for: a shell redirection is the whole story there.
Any Click CLI¶
A CLI that has never heard of Click Extra gets the same treatment through the wrapper: it is loaded and walked from the outside, so an agent is not limited to the tools whose authors thought about it.
$ click-extra wrap --help-format json -- flask run
$ click-extra wrap --help-format markdown -- flask
The parameter inventory comes out the same way, in any structured format, values keeping their native types:
$ click-extra wrap --params --table-format json -- flask run
[
{
"ID": "run._click_default_help",
"Spec.": "--help",
"Class": "click.core.Option",
"Param type": "click.types.BoolParamType",
"Python type": "bool",
"Hidden": false,
"Exposed": false,
"Env. vars.": [
"FLASK_RUN__CLICK_DEFAULT_HELP"
],
"Default": false,
"Is flag": true,
"Flag value": true,
"Is bool flag": true,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.cert",
"Spec.": "--cert PATH",
"Class": "click.core.Option",
"Param type": "flask.cli.CertParamType",
"Python type": "str",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_CERT"
],
"Default": null,
"Is flag": false,
"Flag value": null,
"Is bool flag": false,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.debug",
"Spec.": "--debug / --no-debug",
"Class": "click.core.Option",
"Param type": "click.types.BoolParamType",
"Python type": "bool",
"Hidden": false,
"Exposed": false,
"Env. vars.": [
"FLASK_RUN_DEBUG"
],
"Default": false,
"Is flag": true,
"Flag value": true,
"Is bool flag": true,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.debugger",
"Spec.": "--debugger / --no-debugger",
"Class": "click.core.Option",
"Param type": "click.types.BoolParamType",
"Python type": "bool",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_DEBUGGER"
],
"Default": null,
"Is flag": true,
"Flag value": true,
"Is bool flag": true,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.exclude_patterns",
"Spec.": "--exclude-patterns PATH",
"Class": "click.core.Option",
"Param type": "flask.cli.SeparatedPathType",
"Python type": "str",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_EXCLUDE_PATTERNS"
],
"Default": null,
"Is flag": false,
"Flag value": null,
"Is bool flag": false,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.extra_files",
"Spec.": "--extra-files PATH",
"Class": "click.core.Option",
"Param type": "flask.cli.SeparatedPathType",
"Python type": "str",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_EXTRA_FILES"
],
"Default": null,
"Is flag": false,
"Flag value": null,
"Is bool flag": false,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.host",
"Spec.": "-h, --host TEXT",
"Class": "click.core.Option",
"Param type": "click.types.StringParamType",
"Python type": "str",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_HOST"
],
"Default": "127.0.0.1",
"Is flag": false,
"Flag value": null,
"Is bool flag": false,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.key",
"Spec.": "--key FILE",
"Class": "click.core.Option",
"Param type": "click.types.Path",
"Python type": "str",
"Hidden": false,
"Exposed": false,
"Env. vars.": [
"FLASK_RUN_KEY"
],
"Default": null,
"Is flag": false,
"Flag value": null,
"Is bool flag": false,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.port",
"Spec.": "-p, --port INTEGER",
"Class": "click.core.Option",
"Param type": "click.types.IntParamType",
"Python type": "int",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_PORT"
],
"Default": 5000,
"Is flag": false,
"Flag value": null,
"Is bool flag": false,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.reload",
"Spec.": "--reload / --no-reload",
"Class": "click.core.Option",
"Param type": "click.types.BoolParamType",
"Python type": "bool",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_RELOAD"
],
"Default": null,
"Is flag": true,
"Flag value": true,
"Is bool flag": true,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
},
{
"ID": "run.with_threads",
"Spec.": "--with-threads / --without-threads",
"Class": "click.core.Option",
"Param type": "click.types.BoolParamType",
"Python type": "bool",
"Hidden": false,
"Exposed": true,
"Env. vars.": [
"FLASK_RUN_WITH_THREADS"
],
"Default": true,
"Is flag": true,
"Flag value": true,
"Is bool flag": true,
"Multiple": false,
"Nargs": 1,
"Prompt": null,
"Confirmation prompt": false,
"Value": null,
"Source": null
}
]
Pair it with --columns to hand a consumer only the fields it reads:
$ click-extra wrap --params --table-format json --columns id,spec,envvars,default -- flask routes
[
{
"ID": "routes._click_default_help",
"Spec.": "--help",
"Env. vars.": [
"FLASK_ROUTES__CLICK_DEFAULT_HELP"
],
"Default": false
},
{
"ID": "routes.all_methods",
"Spec.": "--all-methods",
"Env. vars.": [
"FLASK_ROUTES_ALL_METHODS"
],
"Default": false
},
{
"ID": "routes.sort",
"Spec.": "-s, --sort [endpoint|methods|domain|rule|match]",
"Env. vars.": [
"FLASK_ROUTES_SORT"
],
"Default": "endpoint"
}
]
Where --params describes the parameters, --help-format describes the command itself: its usage line, description, option groups, subcommands and examples. The target cooperates with neither, and needs to know nothing about Click Extra:
$ click-extra wrap --help-format json -- flask run
{
"name": "flask run",
"short_help": "Run a development server.",
"version": null,
"synopsis": "flask run [OPTIONS]",
"description": "Run a local development server.\n\nThis server is for development purposes only. It does not provide\nthe stability, security, or performance of production WSGI servers.\n\nThe reloader and debugger are enabled by default with the '--debug'\noption.",
"arguments": [],
"option_groups": [
{
"title": null,
"help": null,
"options": [
{
"names": [
"--debug",
"--no-debug"
],
"spec": "--debug / --no-debug",
"metavar": null,
"choices": null,
"help": "Set debug mode.",
"required": false,
"optional_value": false
},
{
"names": [
"--host",
"-h"
],
"spec": "--host / -h TEXT",
"metavar": "TEXT",
"choices": null,
"help": "The interface to bind to.",
"required": false,
"optional_value": false
},
{
"names": [
"--port",
"-p"
],
"spec": "--port / -p INTEGER",
"metavar": "INTEGER",
"choices": null,
"help": "The port to bind to.",
"required": false,
"optional_value": false
},
{
"names": [
"--cert"
],
"spec": "--cert PATH",
"metavar": "PATH",
"choices": null,
"help": "Specify a certificate file to use HTTPS.",
"required": false,
"optional_value": false
},
{
"names": [
"--key"
],
"spec": "--key FILE",
"metavar": "FILE",
"choices": null,
"help": "The key file to use when specifying a certificate.",
"required": false,
"optional_value": false
},
{
"names": [
"--reload",
"--no-reload"
],
"spec": "--reload / --no-reload",
"metavar": null,
"choices": null,
"help": "Enable or disable the reloader. By default the reloader is active if debug is enabled.",
"required": false,
"optional_value": false
},
{
"names": [
"--debugger",
"--no-debugger"
],
"spec": "--debugger / --no-debugger",
"metavar": null,
"choices": null,
"help": "Enable or disable the debugger. By default the debugger is active if debug is enabled.",
"required": false,
"optional_value": false
},
{
"names": [
"--with-threads",
"--without-threads"
],
"spec": "--with-threads / --without-threads",
"metavar": null,
"choices": null,
"help": "Enable or disable multithreading.",
"required": false,
"optional_value": false
},
{
"names": [
"--extra-files"
],
"spec": "--extra-files PATH",
"metavar": "PATH",
"choices": null,
"help": "Extra files that trigger a reload on change. Multiple paths are separated by ':'.",
"required": false,
"optional_value": false
},
{
"names": [
"--exclude-patterns"
],
"spec": "--exclude-patterns PATH",
"metavar": "PATH",
"choices": null,
"help": "Files matching these fnmatch patterns will not trigger a reload on change. Multiple patterns are separated by ':'.",
"required": false,
"optional_value": false
},
{
"names": [
"--help"
],
"spec": "--help",
"metavar": null,
"choices": null,
"help": "Show this message and exit.",
"required": false,
"optional_value": false
}
]
}
],
"subcommands": [],
"examples": [],
"environment": [],
"files": [],
"exit_status": [
{
"code": "0",
"meaning": "Success."
},
{
"code": "1",
"meaning": "A runtime error, an aborted prompt (Ctrl-D), or a declined confirmation."
},
{
"code": "2",
"meaning": "A usage error: unknown option, invalid value, missing operand, or an unparsable configuration file."
},
{
"code": "130",
"meaning": "Interrupted with Ctrl-C: the process ends by SIGINT, reported as 130."
}
]
}
Feeding a CLI to an agent¶
Three things are worth knowing before wiring any of this into a tool or a model.
Hand a model Markdown, not JSON. Both carry the same content, and JSON is the right answer for code that indexes fields. But a model reads prose better than it reads a nested object, and pays fewer tokens for it: no quoting, no punctuation scaffolding, and headings it already knows how to skim.
Descend, do not dump. The plain formats name a command’s children without expanding them, so an agent answering a question about one leaf spends its context on that leaf rather than on a tree it will not read. Reach for -full when the whole surface is the point (generating documentation, diffing two releases), not by default. On a large CLI the difference is the whole budget.
Two lookalikes that answer different questions. --help-format json describes the interface: what the command is, what it accepts, what it documents. --params describes the state of one invocation: every parameter’s resolved value and where it came from, be that a flag, an environment variable, a configuration file or a default. An agent picking flags wants the first. An agent debugging why a run behaved a certain way wants the second.
Neither requires the target to cooperate: click-extra wrap extracts both from a CLI that knows nothing about Click Extra.
click_extra.command_doc API¶
classDiagram
ExtraOption <|-- HelpFormatOption
ExtraOption <|-- ManOption
StrEnum <|-- HelpFormat
Extract a Click command into a structured document and render it.
extract_command_doc() walks a command (and, through
iter_command_contexts(), its whole tree) into a CommandDoc: one
extraction carrying the man-pages(7) sections documented in Man-page
(NAME, SYNOPSIS, DESCRIPTION, OPTIONS, COMMANDS, ENVIRONMENT, FILES and EXIT
STATUS). The model then renders to any of the HelpFormat backends:
roff (CommandDoc.to_roff()), Markdown (CommandDoc.to_markdown())
and JSON (CommandDoc.to_dict() / CommandDoc.to_json()), with the
Carapace completion spec delegated to click_extra.carapace.
The roff backend is Click Extra’s answer to the unmaintained click-man package. It improves on it by:
working on a command object via
click.Command.make_context(), so it needs noconsole_scriptsentry point;discovering subcommands dynamically through
click.Group.list_commands()/click.Group.get_command()with a live context;honoring Click’s
\bno-rewrap marker (rendered as roff.nf/.fi);rendering boolean flags (
--foo/--no-foo) and skipping hidden commands and options;mirroring Cloup option groups as
.SSsubsections of OPTIONS (ungrouped options fall under anOther optionsheading), matching the help screen;emitting ENVIRONMENT (from auto-generated env vars), FILES (from the
--configsearch pattern) and EXIT STATUS sections that click-man never grew.
Font selection follows the man typographic convention encoded by
click_extra.theme.LITERAL_STYLES / REPLACEABLE_STYLES:
literal tokens (command and option names) render bold (\fB), replaceable
tokens (metavars, operands) render italic (\fI).
- click_extra.command_doc.MAN_SECTION = '1'
Default man page section. Section 1 is for executable programs and shell commands, which is what a Click CLI is.
- click_extra.command_doc.DEFAULT_EXIT_STATUS: tuple[tuple[str, str], ...] = (('0', 'Success.'), ('1', 'A runtime error, an aborted prompt (Ctrl-D), or a declined confirmation.'), ('2', 'A usage error: unknown option, invalid value, missing operand, or an unparsable configuration file.'), ('130', 'Interrupted with Ctrl-C: the process ends by SIGINT, reported as 130.'))
Conventional exit codes shared by every Click Extra CLI.
Mirrors the EXIT STATUS table in Man-page. Click returns
2for usage errors (UsageError),1for aborts, and0on success. A real Ctrl+C ends the process bySIGINTinstead, which a shell reports as130.
- click_extra.command_doc.normalize_examples(examples)[source]
Validate and freeze a command’s
examplesinto(description, command)pairs.Accepts any sequence of two-item sequences, so a list of tuples and a list of lists (what a configuration file or a JSON payload would produce) are both fine.
Noneand an empty sequence both yield an empty tuple.
- class click_extra.command_doc.DocOptionItem(names, metavar, help, required, optional_value=False, choices=())[source]
Bases:
objectA single OPTIONS entry, extracted from a Click option.
- names: tuple[str, ...]
All literal spellings: primary
optsfollowed bysecondary_opts(so--foo/--no-fooboolean flags render both).
- metavar: str | None
The rendered metavar, or
Nonewhen the option takes no value (boolean flags and counters).
- required: bool
Whether the option is mandatory.
- optional_value: bool = False
Whether the option’s value is optional (a bare flag is allowed). Rendered as the attached
[=METAVAR]form instead of a space-separated metavar.
- choices: tuple[str, ...] = ()
Every value the option accepts, for an option whose type enumerates them.
Recorded whatever the metavar shows, so a JSON consumer never has to parse
[a|b|c]back out of it. Seeunlisted_choicesfor the rendered subset.
- property unlisted_choices: tuple[str, ...]
The choices the metavar does not already spell out.
A
Choicerenders as[a|b|c]by default, putting every value on screen already, and repeating them below the help would say the same thing twice. An option overriding that with a short placeholder (--table-format FORMAT) hides them instead, and this is what the man page and the Markdown render add back.
- property attached_value: str
The
[=METAVAR]form of an optional value.An optional value renders attached and bracketed (
--color[=auto|always|never]), the man convention for a flag usable bare. The metavar’s own outer brackets, if any, are stripped so a Choice does not double up.
- property spec: str
The option’s spelling and value placeholder, as one plain string.
- to_markdown()[source]
Render this option as a Markdown list item.
A
\bno-rewrap region in the help becomes a fenced block indented under the item, rather than being folded into the sentence: the author aligned it on purpose, and a list item can carry a block as well as a paragraph can.
- class click_extra.command_doc.DocSubcommand(name, short_help, aliases=())[source]
Bases:
objectOne COMMANDS entry: a subcommand as its parent lists it.
- name: str
The name the subcommand is invoked under.
- short_help: str
Its one-line description, from
full_short_help().
- class click_extra.command_doc.DocOptionGroup(options, title=None, help=None)[source]
Bases:
objectA titled cluster of OPTIONS entries, mirroring a Cloup option group.
A plain Click command, or a Cloup command with no explicit
@option_group, yields a single group withtitle=None: it renders as a flat OPTIONS list with no.SSsubsection heading, identical to a man page that never grouped its options.- options: tuple[DocOptionItem, ...]
The option entries in this group.
- title: str | None = None
The subsection heading, rendered as a roff
.SS.Nonefor the implicit single group of an ungrouped command (no heading emitted).
- to_roff()[source]
Render an optional
.SSheading, group help, then the options.
- to_markdown(level=3)[source]
Render an optional heading, group help, then the options.
- class click_extra.command_doc.CommandDoc(name, short_help='', section='1', synopsis_pieces=(), description='', operands=(), option_groups=(), subcommands=(), environment=(), files=(), exit_status=(('0', 'Success.'), ('1', 'A runtime error, an aborted prompt (Ctrl-D), or a declined confirmation.'), ('2', 'A usage error: unknown option, invalid value, missing operand, or an unparsable configuration file.'), ('130', 'Interrupted with Ctrl-C: the process ends by SIGINT, reported as 130.')), examples=(), version=None, date='', manual=None, authors=None, copyright=None)[source]
Bases:
objectA whole man page in structured form, ready to render to roff.
One
CommandDocmaps to one command (or subcommand). Its fields are the man-pages(7) sections, in the order Man-page documents them. Build it withextract_command_doc()and serialize withto_roff().- name: str
Full command path, space-joined (like
weather forecast).
- short_help: str = ''
One-line description for the NAME section.
- section: str = '1'
Man section number.
- description: str = ''
The command’s full help text / docstring for the DESCRIPTION section.
- option_groups: tuple[DocOptionGroup, ...] = ()
The OPTIONS entries, partitioned into one or more groups. A command without explicit option groups carries a single untitled group.
- subcommands: tuple[DocSubcommand, ...] = ()
For groups: the COMMANDS section entries, in listing order.
- exit_status: tuple[tuple[str, str], ...] = (('0', 'Success.'), ('1', 'A runtime error, an aborted prompt (Ctrl-D), or a declined confirmation.'), ('2', 'A usage error: unknown option, invalid value, missing operand, or an unparsable configuration file.'), ('130', 'Interrupted with Ctrl-C: the process ends by SIGINT, reported as 130.'))
EXIT STATUS entries as
(code, meaning)pairs.
- examples: tuple[tuple[str, str], ...] = ()
EXAMPLES entries as
(description, command_line)pairs.Collected from the command’s own
examplesattribute (seeclick_extra.commands.Command.examples). Empty for a command that declares none, in which case every backend omits the section entirely.
- date: str = ''
Date for the
.THheader (YYYY-MM-DD).
- property title: str
The
.THpage title: the command path, hyphen-joined and upper-cased.
- to_markdown()[source]
Render the whole document as Markdown.
Same sections as
to_roff(), in the same order, minus the roff.THheader, whose date, section number and manual name describe a man page rather than the command. The version survives, as a line under the title.- Return type:
- to_dict()[source]
Render the whole document as a JSON-serializable mapping.
Subcommands are listed by name and one-line description only, never recursively: a consumer walking a deep tree asks for the child it cares about instead of paying for the whole tree at once.
render_help()exposes the recursive variant separately, for the consumers that do want everything.
- click_extra.command_doc.extract_command_doc(command, ctx, *, version=None, date=None, manual=None, authors=None, copyright=None)[source]
Build a
CommandDocfrom a Click command and its context.The context must have been created for
command(for example viaclick.Command.make_context()withresilient_parsing=True), so that auto-generated environment-variable prefixes resolve correctly.A
version,dateorauthorsleft atNoneis resolved from the distribution metadata and the build moment, see_fill_overrides.- Return type:
- click_extra.command_doc.iter_command_contexts(command, prog_name=None)[source]
Walk a command tree, yielding
(path, command, context)for each visible one.Subcommands are discovered dynamically, through
iter_subcommand_contexts(), so dynamically-registered commands are included, each under the name its parent registered it with rather than its ownname. Hidden commands are skipped. Each context is built withresilient_parsing=Trueto avoid triggering required-argument errors, prompts, or eager-option side effects.
- click_extra.command_doc.iter_command_docs(command, prog_name=None, **overrides)[source]
Extract every visible command of a tree, yielding
(path, doc)pairs.Walks the tree with
iter_command_contexts()and extracts each command into aCommandDoc. The header fields the keyword overrides leave unset (version,date,authors) are resolved once, from the root context, and shared by every page: the pages of one tree name the same program, so they look the same distribution up.
- click_extra.command_doc.render_manpage(command, prog_name=None, ctx=None, **overrides)[source]
Render a single command’s man page as a roff string.
Reuses
ctxwhen given (like the live invocation context), otherwise builds a throwaway one withresilient_parsing=True. Keyword overrides (version,date,manual,authors,copyright) are passed through toextract_command_doc().- Return type:
- click_extra.command_doc.render_manpages(command, prog_name=None, **overrides)[source]
Render the whole command tree, one man page per (sub)command.
Returns an ordered mapping of
{filename: roff}where each filename is the command path joined by hyphens plus the section suffix (likeweather-forecast.1).
- click_extra.command_doc.write_manpages(command, target_dir, prog_name=None, **overrides)[source]
Render the command tree and write each man page into
target_dir.Creates
target_dirif missing. Returns the list of written paths.
- click_extra.command_doc.install_manpages(command, prog_name=None, **overrides)[source]
Write the command tree’s man pages where
mancan find them.Targets
$XDG_DATA_HOME/man/man1when that variable is set, else~/.local/share/man/man1, expanded against theHOMEin force at the call. Returns the written paths.The environment is read here rather than at import time, through
xdg_dir(), so a caller that setsXDG_DATA_HOMEorHOMEfor one invocation (a test, a packaging script staging into a build root) is honored. This mirrorsinstall_carapace_spec(), whose spec directory resolves the same way.
- class click_extra.command_doc.HelpFormat(*values)[source]
Bases:
StrEnumThe formats
render_help()renders.The value is the spelling
--help-formattakes. Members are ordered alphabetically, which is also the order--help-formatadvertises them in. Adding a format is a member here, a description inHELP_FORMATSand a branch inrender_help(): no new flag, no wider help screen. See Man-page for what each one is good for.A member prints, compares, hashes and sorts as its value, so a plain string keeps working wherever a format is expected.
- CARAPACE = 'carapace'
- JSON = 'json'
- JSON_FULL = 'json-full'
- MAN = 'man'
- MARKDOWN = 'markdown'
- MARKDOWN_FULL = 'markdown-full'
- click_extra.command_doc.HELP_FORMATS: dict[HelpFormat, str] = {HelpFormat.CARAPACE: 'Carapace completion spec (YAML). Doubles as a command-and-flag tree, and is the shape `carapace` itself consumes. Needs the `carapace` extra.', HelpFormat.JSON: 'This command as a JSON object: usage, description, arguments, options grouped as the help screen groups them, environment variables, files, exit codes, and its direct subcommands by name.', HelpFormat.JSON_FULL: 'Every command of the tree as JSON, under a `commands` array, each entry in the `json` shape.', HelpFormat.MAN: 'This command as a man page: the roff source a packager installs, which `--man` typesets for reading.', HelpFormat.MARKDOWN: 'This command as a Markdown document, one section per topic.', HelpFormat.MARKDOWN_FULL: 'Every command of the tree as one Markdown document, in tree order.'}
Each
HelpFormat, mapped to its one-line description.Note
The distinction the plain and
-fullvariants draw is progressive disclosure. A plain render describes one command and names its children, so a reader (a tool or an agent, typically) descends one level at a time instead of pulling a whole tree into a context window to answer a question about one leaf. The-fullvariants exist for the opposite job: generating documentation, or diffing a CLI’s whole surface between two releases.
- click_extra.command_doc.INSTALLABLE_FORMATS: frozenset[HelpFormat] = frozenset({HelpFormat.CARAPACE, HelpFormat.MAN})
The formats with a canonical place on disk their consumer reads them from.
A man page under a
mandirectory, a Carapace spec under Carapace’s. These are the two renderings that are installed rather than read, which is what letsclick-extra wrapoffer them a destination (--output-dir,--install) and refuse one to the others. A JSON or Markdown document has no such place: nothing goes looking for it, so stdout and a shell redirection are the whole story.
- click_extra.command_doc.render_help(command, help_format, prog_name=None, ctx=None, **overrides)[source]
Render command in one of the
HelpFormatformats.Reuses
ctxwhen given (like the live invocation context), otherwise builds a throwaway one withresilient_parsing=True, exactly likerender_manpage(). Keyword overrides are passed through toextract_command_doc(), and ignored by thecarapaceformat, which carries no version or authorship of its own.- Raises:
ValueError – on an unknown format, listing the known ones.
- Return type:
- click_extra.command_doc.MAN_TYPESETTERS: dict[str, tuple[tuple[str, ...], ...]] = {'html': (('mandoc', '-Thtml'), ('groff', '-Thtml', '-mandoc')), 'terminal': (('groff', '-man', '-Tutf8', '-rLL={width}n', '-P-c'), ('mandoc', '-Tutf8', '-Owidth={width}'))}
The programs typesetting roff, keyed by what they render it to, best first.
Each entry is a command line: the program, then its arguments, where
:width:stands for the line length in columns. Both modes run the same two programs.groffis the GNU implementation found nearly everywhere a man page is;mandoccovers the BSDs and Alpine, which ship it instead.terminal: the textformat_manpage()sends to the pager for--man, read on stdin.html: the pageclick_extra.sphinx.manpagespublishes beside each man page, read from a file.mandoccomes first there: its HTML gives every section and option a semanticidanchor (#NAME,#SYNOPSIS,#config…), which prose can deep-link to.
Note
-P-chands-cdown togrotty, groff’s terminal driver, pinning the emphasis it produces to the character-backspace pairsstrip_overstrike()removes, andread_manpage()with it under--accessible. Left to its own default agrottyrecent enough writes SGR escape sequences instead, which that function cannot see: the manual then reaches a screen reader with its emphasis intact, and loses it altogether once the output is not a terminal and the codes are stripped as color.mandocneeds no counterpart: it overstrikes already.Note
The
manbinary is deliberately not in this list, even though it is the tool being imitated. Reading roff from stdin is where the implementations diverge: GNUmantakes-l -, while the BSD one wants a real file path. Driving the typesetter directly sidesteps a portability problem that buys nothing, since paging is handled here anyway.
- click_extra.command_doc.find_typesetters(mode, **fields)[source]
Yield the command line of each typesetter installed for
mode, best first.Probes
PATHfor each program ofMAN_TYPESETTERSand skips the absent ones. Each command line starts with the program’s resolved path, and its arguments are filled fromfields, likewidth.- Parameters:
mode (
str) – theMAN_TYPESETTERSkey naming the output,terminalorhtml.- Return type:
- click_extra.command_doc.run_typesetter(argv, roff=None, *, timeout=None)[source]
Run a typesetter and return what it wrote to
stdout.The output is read as UTF-8, the encoding
groffandmandocwrite in whatever the locale of the platform.- Parameters:
- Raises:
subprocess.SubprocessError – the typesetter exits with an error or outlives
timeout.OSError – the typesetter cannot start.
- Return type:
- click_extra.command_doc.format_manpage(roff, width=None)[source]
Typeset roff into readable terminal text, or
Noneif nothing can.Tries each
terminaltypesetter ofMAN_TYPESETTERSin turn and returns the output of the first that succeeds. ReturnsNonewhen none of them is installed, which the caller is expected to degrade on rather than fail: a CLI that cannot find a typesetter is a CLI running somewhere that never had man pages to begin with (Windows, a slim container), and that is no reason for--manto error.
- click_extra.command_doc.strip_overstrike(text)[source]
Remove the character-backspace pairs a roff typesetter emits for emphasis.
A bold
Nis writtenN\x08Nand an underlined one_\x08N, a convention inherited from line printers that a pager still renders as bold and underline today. Dropping the pair’s first half leaves the plain character.The terminal-text counterpart of
strip_ansi().- Return type:
- click_extra.command_doc.read_manpage(command, ctx=None)[source]
Typeset a command’s manual and send it to the pager.
The reading counterpart of
--help-format man, which emits the roff source a packager installs. Falls back to printing that source, with a warning naming what to install, when no typesetter is available: something on screen beats an error, and the source still carries every word of the manual.Under
--accessiblethe emphasis is stripped and the pager bypassed (echo_via_pager()streams instead). Both matter to the same reader: a pager is a cursor-driven takeover, and overstrike is worse than the ANSI codes accessible mode already removes, since a screen reader voicesN\x08NA\x08AM\x08ME\x08Erather than skipping it.- Return type:
- class click_extra.command_doc.ManOption(param_decls=None, *, is_flag=True, expose_value=False, is_eager=True, help="Read the command's manual page and exit.", **kwargs)[source]
Bases:
ExtraOptionA pre-configured
--manflag that typesets the command’s manual, pages it, and exits.Eager and value-less, like
ShowParamsOption. Part of the default option set injected bydefault_params(), so every@commandand@groupexposes it. Use@man_optionto add it to a plain Click CLI.Note
The flag is named
--man, not--show-manor--man-page.In the POSIX, GNU and BSD traditions a program does not emit its own man page through a flag: the page is a separate file read with
man <prog>, either hand-written (BSDmdoc) or generated at build time from--helpoutput (GNUhelp2man). Click Extra already covers that build-time path withwrite_manpages(), itshelp2manequivalent.The one ecosystem that exposes a runtime flag is Perl’s
Pod::Usage, whose convention is--helpfor the brief usage and bare--manfor the full manual.--manalso lines up with the neighbouring--helpand--versioninformational flags, which use bare nouns with noshow-prefix.--show-manand--man-pagehave no precedent outside Click Extra.Note
That Perl convention is about reading a manual, so the flag typesets the page and sends it to the pager, the way
manitself does. The roff source a build step ships is--help-format man, beside every other artifact this module renders. The two are one question apart: do you want to read the manual, or to ship it?
- class click_extra.command_doc.HelpFormatOption(param_decls=None, *, expose_value=False, is_eager=True, help='Render the command in the given format and exit.', **kwargs)[source]
Bases:
ExtraOptionA pre-configured
--help-formatoption printing the command in one of theHelpFormatformats and exiting.Eager and value-taking, unlike its
--manneighbour. That bare flag typesets the same page and sends it to the pager, where--help-format manprints its roff source. It stays a flag of its own because a runtime manual flag has its own tradition (seeManOption).Note
One option carrying a format, rather than one flag per format. A CLI’s option list is the most expensive real estate in its help screen, and every reader pays for it whether or not they will ever export anything: a family of
--help-json,--help-markdownand--help-carapaceflags would widen the label column of every screen, forever, one line per format anyone ever adds. Here a new format costs aHelpFormatmember and nothing on screen.Note
The rendered output is deliberately colorless whatever
--colorsays. Every format here is meant to be piped into something (a file, a parser, a model), and ANSI escapes in a JSON string or a Markdown fence are noise to all of them.--helpremains the colorized human view.