CLI wrapper

Click Extra’s wrap subcommand applies its help colorization to any installed Click CLI, without modifying the target’s source code. This is useful for previewing how a third-party CLI would look with Click Extra’s keyword highlighting and themed styling.

Usage

The wrap subcommand is the default when no known subcommand is given, so both forms work:

$ click-extra wrap flask --help
$ click-extra flask --help
$ wrap --help
Usage: wrap [OPTIONS] SCRIPT [ARGS]...
Aliases: run

  Apply Click Extra help colorization to any Click CLI.

  Wraps SCRIPT with keyword highlighting and themed styling for help screens.
  The target CLI is not modified.

  Resolution order for SCRIPT: installed console_scripts entry point,
  module:function notation, Python file path, or Python module name.

Options:
  --theme [dark|light]  Color theme preset.
  -h, --help            Show this message and exit.

Tip

run is an alias for wrap, so you can also use:

$ click-extra run flask --help

Wrapping a Click CLI

Pass the target CLI name (or path) as the first argument. Everything after it is forwarded to the target:

$ click-extra flask --help
$ click-extra black --help
$ click-extra ./my_script.py --help
$ click-extra my_package.cli:main --help

Tip

An optional -- separator is available which you can use for visually separating Click Extra from the target CLI:

$ click-extra --no-color -- flask --help

Execution timing

The group-level --time flag measures the total execution time of the wrapped CLI, including import and patching overhead:

$ click-extra --time flask routes
Execution time: 0.342 seconds.

Color control

--color / --no-color (--ansi / --no-ansi) controls whether ANSI codes are emitted. The flag also respects environment variables like NO_COLOR, CLICOLOR, and FORCE_COLOR:

$ click-extra --no-color flask --help
$ NO_COLOR=1 click-extra flask --help

The --theme option on wrap selects a color preset:

$ click-extra wrap --theme light flask --help

Configuration

Click Extra’s configuration file support works alongside the wrapper. Group-level options like verbosity can be set in pyproject.toml:

[tool.click-extra]
verbosity = "DEBUG"
$ click-extra flask --help
debug: Set <Logger click_extra (DEBUG)> to DEBUG.
...

Defaults for the wrapped CLI

The [tool.click-extra.wrap.<script>] section sets persistent defaults for a specific target CLI. All keys are converted to CLI arguments and prepended to the target’s invocation:

[tool.click-extra.wrap.flask]
app = "myapp:create_app"
debug = true
$ click-extra flask routes
# Equivalent to: flask --app myapp:create_app --debug routes

The section name must match the script name you pass on the command line. Multiple targets can each have their own section:

[tool.click-extra.wrap.flask]
app = "myapp:create_app"

[tool.click-extra.wrap.quart]
app = "otherapp:create_app"

Explicit CLI arguments always override config values:

$ click-extra flask --app otherapp routes
# CLI --app wins over config

Invalid option names are caught by the target CLI itself with standard Click error messages, so typos are surfaced immediately.

Script resolution

The SCRIPT argument is resolved in this order:

  1. Console scripts entry point: any package installed with pip install or uv add that registers a console_scripts entry point. This covers most CLI tools.

  2. module:function notation: explicit import path like my_app.cli:main.

  3. .py file path: a local Python script.

  4. Python module name: a bare module or package name invocable via python -m.

Ephemeral wrapping with uvx

The wrapper is particularly useful with uvx for one-shot colorization of any Click CLI without permanently installing Click Extra:

$ uvx click-extra -- flask --help
$ uvx click-extra -- black --help

How it works

The wrapper monkey-patches Click at two levels before importing the target module:

  1. Decorator defaults: @click.command() and @click.group() produce colorized commands when no explicit cls= is given.

  2. Method patching: click.Command.get_help and click.Command.format_help are patched to inject the colorized formatter and keyword collection on all commands, including those with custom classes (like Flask’s FlaskGroup).

CLIs already built with Click Extra or Cloup are unaffected by the patching (they already have their own help formatting) but still run correctly through the wrapper.

Introspecting external CLIs

The show-params subcommand inspects the parameters of any Click CLI without running it. It displays a table with each parameter’s ID, spec, class, type, hidden status, environment variables, and default value.

$ click-extra show-params --help
Usage: show-params [OPTIONS] SCRIPT [SUBCOMMAND]...

  Show parameters of an external Click CLI.

  Resolves SCRIPT as a console_scripts entry point, module:function notation,
  .py file path, or Python module name. Loads the Click command and prints its
  parameter table.

  Extra arguments after SCRIPT navigate into nested command groups.

Options:
  --table-format [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]
              Rendering style of tables.  [default: ROUNDED_OUTLINE]
  -h, --help  Show this message and exit.

Here is an example introspecting Flask’s run subcommand with the vertical table format:

$ click-extra show-params --table-format vertical flask run
***************************[ 1. row ]***************************
ID          | run.cert
Spec.       | --cert PATH
Class       | click.core.Option
Param type  | flask.cli.CertParamType
Python type | str
Hidden      | 
Env. vars.  | 
Default     | Sentinel.UNSET
***************************[ 2. row ]***************************
ID          | run.debug
Spec.       | --debug / --no-debug
Class       | click.core.Option
Param type  | click.types.BoolParamType
Python type | bool
Hidden      | 
Env. vars.  | 
Default     | False
***************************[ 3. row ]***************************
ID          | run.debugger
Spec.       | --debugger / --no-debugger
Class       | click.core.Option
Param type  | click.types.BoolParamType
Python type | bool
Hidden      | 
Env. vars.  | 
Default     | None
***************************[ 4. row ]***************************
ID          | run.exclude_patterns
Spec.       | --exclude-patterns PATH
Class       | click.core.Option
Param type  | flask.cli.SeparatedPathType
Python type | str
Hidden      | 
Env. vars.  | 
Default     | None
***************************[ 5. row ]***************************
ID          | run.extra_files
Spec.       | --extra-files PATH
Class       | click.core.Option
Param type  | flask.cli.SeparatedPathType
Python type | str
Hidden      | 
Env. vars.  | 
Default     | None
***************************[ 6. row ]***************************
ID          | run.help
Spec.       | --help
Class       | click.core.Option
Param type  | click.types.BoolParamType
Python type | bool
Hidden      | 
Env. vars.  | 
Default     | False
***************************[ 7. row ]***************************
ID          | run.host
Spec.       | -h, --host TEXT
Class       | click.core.Option
Param type  | click.types.StringParamType
Python type | str
Hidden      | 
Env. vars.  | 
Default     | '127.0.0.1'
***************************[ 8. row ]***************************
ID          | run.key
Spec.       | --key FILE
Class       | click.core.Option
Param type  | click.types.Path
Python type | str
Hidden      | 
Env. vars.  | 
Default     | Sentinel.UNSET
***************************[ 9. row ]***************************
ID          | run.port
Spec.       | -p, --port INTEGER
Class       | click.core.Option
Param type  | click.types.IntParamType
Python type | int
Hidden      | 
Env. vars.  | 
Default     | 5000
***************************[ 10. row ]***************************
ID          | run.reload
Spec.       | --reload / --no-reload
Class       | click.core.Option
Param type  | click.types.BoolParamType
Python type | bool
Hidden      | 
Env. vars.  | 
Default     | None
***************************[ 11. row ]***************************
ID          | run.with_threads
Spec.       | --with-threads / --without-threads
Class       | click.core.Option
Param type  | click.types.BoolParamType
Python type | bool
Hidden      | 
Env. vars.  | 
Default     | True

All --table-format renderings are supported. JSON output is useful for programmatic consumption:

$ click-extra show-params --table-format json flask run
[
  {
    "ID": "run.cert",
    "Spec.": "--cert PATH",
    "Class": "click.core.Option",
    "Param type": "flask.cli.CertParamType",
    "Python type": "str",
    "Hidden": false,
    "Env. vars.": [],
    "Default": "Sentinel.UNSET"
  },
  {
    "ID": "run.debug",
    "Spec.": "--debug / --no-debug",
    "Class": "click.core.Option",
    "Param type": "click.types.BoolParamType",
    "Python type": "bool",
    "Hidden": false,
    "Env. vars.": [],
    "Default": false
  },
  {
    "ID": "run.debugger",
    "Spec.": "--debugger / --no-debugger",
    "Class": "click.core.Option",
    "Param type": "click.types.BoolParamType",
    "Python type": "bool",
    "Hidden": false,
    "Env. vars.": [],
    "Default": null
  },
  {
    "ID": "run.exclude_patterns",
    "Spec.": "--exclude-patterns PATH",
    "Class": "click.core.Option",
    "Param type": "flask.cli.SeparatedPathType",
    "Python type": "str",
    "Hidden": false,
    "Env. vars.": [],
    "Default": null
  },
  {
    "ID": "run.extra_files",
    "Spec.": "--extra-files PATH",
    "Class": "click.core.Option",
    "Param type": "flask.cli.SeparatedPathType",
    "Python type": "str",
    "Hidden": false,
    "Env. vars.": [],
    "Default": null
  },
  {
    "ID": "run.help",
    "Spec.": "--help",
    "Class": "click.core.Option",
    "Param type": "click.types.BoolParamType",
    "Python type": "bool",
    "Hidden": false,
    "Env. vars.": [],
    "Default": false
  },
  {
    "ID": "run.host",
    "Spec.": "-h, --host TEXT",
    "Class": "click.core.Option",
    "Param type": "click.types.StringParamType",
    "Python type": "str",
    "Hidden": false,
    "Env. vars.": [],
    "Default": "127.0.0.1"
  },
  {
    "ID": "run.key",
    "Spec.": "--key FILE",
    "Class": "click.core.Option",
    "Param type": "click.types.Path",
    "Python type": "str",
    "Hidden": false,
    "Env. vars.": [],
    "Default": "Sentinel.UNSET"
  },
  {
    "ID": "run.port",
    "Spec.": "-p, --port INTEGER",
    "Class": "click.core.Option",
    "Param type": "click.types.IntParamType",
    "Python type": "int",
    "Hidden": false,
    "Env. vars.": [],
    "Default": 5000
  },
  {
    "ID": "run.reload",
    "Spec.": "--reload / --no-reload",
    "Class": "click.core.Option",
    "Param type": "click.types.BoolParamType",
    "Python type": "bool",
    "Hidden": false,
    "Env. vars.": [],
    "Default": null
  },
  {
    "ID": "run.with_threads",
    "Spec.": "--with-threads / --without-threads",
    "Class": "click.core.Option",
    "Param type": "click.types.BoolParamType",
    "Python type": "bool",
    "Hidden": false,
    "Env. vars.": [],
    "Default": true
  }
]

Subcommand drilling

Extra arguments after SCRIPT navigate into nested command groups:

$ click-extra show-params -- flask run
$ click-extra show-params -- flask routes

Target resolution

Target resolution follows the same order as wrap: console_scripts entry points, module:function notation, .py file paths, and Python module names.

When the resolved entry point is a wrapper function (not a Click command), the module is scanned for Click command instances. If a single command group is found, it is used automatically. If multiple candidates exist, the error message lists them so you can use explicit module:name notation:

$ click-extra show-params -- flask.cli:cli

click_extra.wrap API

        classDiagram
  Command <|-- _WrapCommand
  ExtraGroup <|-- WrapperGroup
  ExtraHelpColorsMixin <|-- _WrapCommand
    

Wrap any Click CLI with Click Extra’s help colorization.

Monkey-patches Click’s decorator functions before importing the target module so @click.command() and @click.group() produce colorized variants with keyword highlighting and themed styling.

class click_extra.wrap.WrapperGroup(*args, help_command=True, **kwargs)[source]

Bases: ExtraGroup

ExtraGroup that falls back to the wrap subcommand for unknown names.

Known subcommands and their aliases are dispatched normally. Anything else is treated as a target script and forwarded to wrap.

Like ExtraCommand.__init__, but auto-injects a help subcommand.

Parameters:

help_command (bool) – when True (the default), a help subcommand is automatically registered. Set to False to suppress it, or register your own help subcommand to override it.

resolve_command(ctx, args)[source]
Return type:

tuple[str | None, Command | None, list[str]]

click_extra.wrap.patch_click(theme=None, color=True)[source]

Replace Click’s decorator functions with colorized variants.

Must be called before importing the target CLI module so that @click.command() and @click.group() decorators produce colorized commands.

Note

Only the decorator functions are replaced, not the class names (click.Command, click.Group). Replacing class names would break isinstance and issubclass checks in Click internals (_param_memo) and Cloup’s decorator validators.

Parameters:
  • theme (HelpExtraTheme | None) – Color theme to use. None keeps the current default.

  • color (bool) – When False, the patched context disables ANSI output.

Return type:

None

click_extra.wrap.unpatch_click()[source]

Restore Click’s original decorator functions and methods.

Reverses the changes made by patch_click(). Useful in tests to avoid leaking global state between test cases.

Return type:

None

click_extra.wrap.resolve_target(script)[source]

Resolve a script name to a module path and function name.

Resolution order:

  1. console_scripts entry points from installed packages.

  2. Explicit module:function notation.

  3. .py file path.

  4. Bare Python module or package name.

Return type:

tuple[str, str]

Returns:

(module_path, function_name) tuple. function_name is empty when the target should be invoked as a module or script file.

Raises:

click.ClickException – If the script cannot be resolved.

click_extra.wrap.invoke_target(script, module_path, function_name, args)[source]

Import and call the target CLI.

Reconstructs sys.argv so Click’s argument parsing sees the target’s program name and arguments.

Parameters:
  • script (str) – Original script name (used as sys.argv[0]).

  • module_path (str) – Dotted module path or .py file path.

  • function_name (str) – Function to call, or empty for module execution.

  • args (tuple[str, ...]) – Arguments to pass to the target CLI.

Return type:

None