Tutorial¶
This tutorial details how we transformed the canonical click example:

Into this:

All bells and whistles¶
The canonical click example is implemented that way:
import click
@click.command
@click.option("--count", default=1, help="Number of greetings.")
@click.option("--name", prompt="Your name", help="The person to greet.")
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for _ in range(count):
click.echo(f"Hello, {name}!")
Whose help screen renders as:
$ hello --help
Usage: hello [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
To augment the example above with all the bells and whistles click-extra has in store, you just need to import the same decorators and functions from its namespace:
import click_extra
@click_extra.command
@click_extra.option("--count", default=1, help="Number of greetings.")
@click_extra.option("--name", prompt="Your name", help="The person to greet.")
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for _ in range(count):
click_extra.echo(f"Hello, {name}!")
And now you get:
$ hello --help
Usage: hello [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings. [default: 1]
--name TEXT The person to greet.
--time / --no-time Measure and print elapsed execution time. [default: no-
time]
--color, --ansi / --no-color, --no-ansi
Strip out all colors and all ANSI codes from output.
[default: color]
--config CONFIG_PATH Location of the configuration file. Supports local path
with glob patterns or remote URL. [default: ~/.config/h
ello/*.toml|*.yaml|*.yml|*.json|*.json5|*.jsonc|*.hjson|
*.ini|*.xml]
--no-config Ignore all configuration files and only use command line
parameters and environment variables.
--show-params Show all CLI parameters, their provenance, defaults and
value, then exit.
--table-format [asciidoc|csv|csv-excel|csv-excel-tab|csv-unix|double-grid|double-outline|fancy-grid|fancy-outline|github|grid|heavy-grid|heavy-outline|html|jira|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|tsv|unsafehtml|vertical|youtrack]
Rendering style of tables. [default: rounded-outline]
--verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG. [default:
WARNING]
-v, --verbose Increase the default WARNING verbosity by one level for
each additional repetition of the option. [default: 0]
--version Show the version and exit.
-h, --help Show this message and exit.
That’s it!
Tip
click_extra is proxy-ing the whole click and cloud namespace, so you can use it as a drop-in replacement.
Mix and match¶
If you do not like the opiniated way the @click_extra.command decorator is setup, with all its defaults options, you are still free to pick them up independently.
If, for example, you’re only interested in using the --config option, you’re free to to use it with a standard Click CLI. Just take the @config_option decorator from click_extra and add it to your command:
import click
from click_extra import config_option
@click.command
@click.option("--count", default=1, help="Number of greetings.")
@click.option("--name", prompt="Your name", help="The person to greet.")
@config_option
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for _ in range(count):
click.echo(f"Hello, {name}!")
Which now renders to:
$ hello --help
Usage: hello [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--config CONFIG_PATH Location of the configuration file. Supports local path
with glob patterns or remote URL. [default: ~/.config/h
ello/*.toml|*.yaml|*.yml|*.json|*.json5|*.jsonc|*.hjson|
*.ini|*.xml]
--help Show this message and exit.
This option behave like any Click option and can be customized easily:
import click
from click_extra import config_option
@click.command
@click.option("--count", default=1, help="Number of greetings.")
@click.option("--name", prompt="Your name", help="The person to greet.")
@config_option("--hello-conf", metavar="CONF_FILE", help="Loads CLI config.")
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for _ in range(count):
echo(f"Hello, {name}!")
$ hello --help
Usage: hello [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--hello-conf CONF_FILE Loads CLI config. [default: ~/.config/hello/*.toml|*.
yaml|*.yml|*.json|*.json5|*.jsonc|*.hjson|*.ini|*.xml]
--help Show this message and exit.
Cloup integration¶
All Click Extra primitives are sub-classes of Cloup’s and supports all its features.
Like option groups:
import click
import cloup
from click_extra import config_option
@cloup.command()
@click.option("--count", default=1, help="Number of greetings.")
@click.option("--name", prompt="Your name", help="The person to greet.")
@cloup.option_group(
"Cool options",
cloup.option("--foo", help="The option that starts it all."),
cloup.option("--bar", help="Another important option."),
config_option("--hello-conf", metavar="CONF_FILE", help="Loads CLI config."),
constraint=cloup.constraints.RequireAtLeast(1),
)
def hello(count, name, foo, bar, hello_conf):
"""Simple program that greets NAME for a total of COUNT times."""
for _ in range(count):
click.echo(f"Hello, {name}!")
See how the configuration option is grouped with others:
$ hello --help
Usage: hello [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Cool options: [at least 1 required]
--foo TEXT The option that starts it all.
--bar TEXT Another important option.
--hello-conf CONF_FILE Loads CLI config. [default: ~/.config/hello/*.toml|*.
yaml|*.yml|*.json|*.json5|*.jsonc|*.hjson|*.ini|*.xml]
Other options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
Caution
Notice in the example above how the @command() decorator from Cloup is used with parenthesis. Contrary to Click and Click Extra, Cloup requires parenthesis on its decorators.
Available options¶
Click Extra provides these additional, pre-configured options decorators you can use standalone. Some of them are included by default in the @extra_command and @extra_group decorators (see the last column):
Decorator |
Specification |
Default |
|---|---|---|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
✅ |
|
|
❌ |
Note
Because single-letter options are a scarce resource, Click Extra does not impose them on you. All the options above are specified with their long names only. You can always customize them to add a short name if you wish.
That’s a general rule, unless some short names follow a widely-accepted convention or an overwhelmingly-followed tradition. Which is the case for -v, --verbose and -h, --help.
Tip
If you find the click_extra namespace too long to type, you can always alias it to something shorter.
A popular choice is clickx:
import click
import click_extra as clickx
@click.command
@click.option("--foo")
@clickx.config_option
def first(foo): ...
Standalone script¶
You can create a full-featured CLI based on Click Extra, without any Python project boilerplate.
The trick is to rely on uv to run simple Python scripts.
Taking the canonical example again, you can create a file named hello.py with this content:
hello.py¶ 1#!/usr/bin/env -S uv run --script
2# /// script
3# dependencies = ["click-extra >= 7.0.0"]
4# ///
5
6from click_extra import command, echo, option
7
8
9@command
10@option("--count", default=1, help="Number of greetings.")
11@option("--name", prompt="Your name", help="The person to greet.")
12def hello(count, name):
13 """Simple program that greets NAME for a total of COUNT times."""
14 for _ in range(count):
15 echo(f"Hello, {name}!")
16
17
18if __name__ == "__main__":
19 hello()
See the first few commented lines? The first line is a shebang that tells the OS to run the script with uv. The other comments are script dependencies.
Now all you need to do is to make the script executable and run it:
$ chmod +x ./hello.py
$ ./hello.py --help
The magic happens because uv will read the script comments and install click-extra and its dependencies in an isolated environment before running the Python code.
And just like that, you have a self-contained, single-file CLI, with all the features of Click Extra, including multi-platform support.
Hint
You can target specific versions of Click Extra in your script dependencies:
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["click-extra"]
# ///
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["click-extra == 7.2.0"]
# ///
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["click-extra @ git+https://github.com/kdeldycke/click-extra"]
# ///
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["click-extra @ file:///Users/me/code/click-extra"]
# ///