Commands & groups¶
Drop-in replacement¶
Click Extra aims to be a drop-in replacement for Click. The vast majority of Click Extra’s decorators, functions and classes are direct proxies of their Click counterparts. This means that you can replace, in your code, imports of the click
namespace by click_extra
and it will work as expected.
Here is for instance the canonical click
example with all original imports replaced with click_extra
:
from click_extra import command, echo, option
@command
@option("--count", default=1, help="Number of greetings.")
@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):
echo(f"Hello, {name}!")
As you can see the result does not deviates from the original Click-based output:
$ 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.
Note
Click and Cloup inheritance
At the module level, click_extra
imports all elements from click.*
, then all elements from the cloup.*
namespace.
Which means all elements not redefined by Click Extra fallback to Cloup. And if Cloup itself does not redefine them, they fallback to Click.
For example:
click_extra.echo
is a direct proxy toclick.echo
because Cloup does not re-implement anecho
helper.On the other hand,
@click_extra.option
is a proxy of@cloup.option
, because Cloup adds the possibility for options to be grouped.@click_extra.timer
is not a proxy of anything, because it is a new decorator implemented by Click Extra.As for
@click_extra.extra_version_option
, it is a re-implementation of@click.version_option
. Because it adds new features and breaks the original API, it was prefixed withextra_
to become its own thing. And@click_extra.version_option
still proxy the original from Click.
Here are few other examples on how Click Extra proxies the main elements from Click and Cloup:
Click Extra element |
Target |
|
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Not implemented |
|
|
|
|
|
|
|
Itself |
|
|
Itself |
|
|
Itself |
Not implemented |
… |
… |
… |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Not implemented |
|
|
|
|
Itself |
Not implemented |
|
|
Not implemented |
|
|
|
|
|
|
… |
… |
… |
You can inspect the implementation details by looking at:
Extra variants¶
Now if you want to benefit from all the wonderful features of Click Extra, you have to use the extra
-prefixed variants:
Extra variant |
|
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
You can see how to use some of these extra
variants in the tutorial.
Default options¶
The @extra_command
and @extra_group
decorators are pre-configured with a set of default options.
Remove default options¶
You can remove all default options by resetting the params
argument to None
:
from click_extra import extra_command
@extra_command(params=None)
def bare_cli():
pass
Which results in:
$ bare-cli --help
Usage: bare-cli [OPTIONS]
Options:
-h, --help Show this message and exit.
As you can see, all options are stripped out, but the colouring and formatting of the help message is preserved.
Change default options¶
To override the default options, you can provide the params=
argument to the command. But note how we use classes instead of option decorators:
from click_extra import extra_command, ConfigOption, VerbosityOption
@extra_command(
params=[
ConfigOption(default="ex.yml"),
VerbosityOption(default="DEBUG"),
]
)
def cli():
pass
And now you get:
$ cli --help
debug: Set <Logger click_extra (DEBUG)> to DEBUG.
debug: Set <RootLogger root (DEBUG)> to DEBUG.
Usage: cli [OPTIONS]
Options:
-C, --config CONFIG_PATH Location of the configuration file. Supports glob
pattern of local path and remote URL. [default:
ex.yml]
-v, --verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
[default: DEBUG]
-h, --help Show this message and exit.
debug: Reset <RootLogger root (DEBUG)> to WARNING.
debug: Reset <Logger click_extra (DEBUG)> to WARNING.
This let you replace the preset options by your own set, tweak their order and fine-tune their defaults.
Caution
Duplicate options
If you try to add option decorators to a command which already have them by default, you will end up with duplicate entries ([as seen in issue #232](https://github.com/kdeldycke/click-extra/issues/232)):
from click_extra import extra_command, extra_version_option
@extra_command
@extra_version_option(version="0.1")
def cli():
pass
See how the --version
option gets duplicated at the end:
$ cli --help
Usage: cli [OPTIONS]
Options:
--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]
-C, --config CONFIG_PATH Location of the configuration file. Supports glob
pattern of local path and remote URL. [default:
~/.config/cli/*.{toml,yaml,yml,json,ini,xml}]
--show-params Show all CLI parameters, their provenance, defaults
and value, then exit.
-v, --verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
[default: WARNING]
--version Show the version and exit.
-h, --help Show this message and exit.
--version Show the version and exit.
This is by design: decorators are cumulative, to allow you to add your own options to the preset of @extra_command and @extra_group.
Option order¶
Notice how the options above are ordered in the help message.
The default behavior of @extra_command
(and its derivates decorators) is to order options in the way they are provided to the params=
argument of the decorator. Then adds to that list the additional option decorators positioned after the @extra_command
decorator.
After that, there is a final sorting step applied to options. This is done by the extra_option_at_end
option, which is True
by default.
Option’s defaults¶
Because Click Extra commands and groups inherits from Click, you can override the defaults the way Click allows you to. Here is a reminder on how to do it.
For example, the --verbosity
option defaults to the WARNING
level. Now we’d like to change this default to INFO
.
If you manage your own --verbosity
option, you can pass the default
argument to its decorator like we did above:
from click_extra import command, verbosity_option
@command
@verbosity_option(default="INFO")
def cli():
pass
This also works in its class form:
from click_extra import command, VerbosityOption
@command(params=[VerbosityOption(default="INFO")])
def cli():
pass
But you also have the alternative to pass a default_map
via the context_settings
:
from click_extra import extra_command
@extra_command(context_settings={"default_map": {"verbosity": "INFO"}})
def cli():
pass
Which results in [default: INFO]
being featured in the help message:
$ cli --help
Usage: cli [OPTIONS]
Options:
--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]
-C, --config CONFIG_PATH Location of the configuration file. Supports glob
pattern of local path and remote URL. [default:
~/.config/cli/*.{toml,yaml,yml,json,ini,xml}]
--show-params Show all CLI parameters, their provenance, defaults
and value, then exit.
-v, --verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
[default: INFO]
--version Show the version and exit.
-h, --help Show this message and exit.
Tip
The advantage of the context_settings
method we demonstrated last, is that it let you change the default of the --verbosity
option provided by Click Extra, without having to re-list the whole set of default options.
Third-party commands composition¶
Click Extra is capable of composing with existing Click CLI in various situation.
Wrap other commands¶
Click allows you to build up a hierarchy of command and subcommands. Click Extra inherits this behavior, which means we are free to assemble multiple third-party subcommands into a top-level one.
For this example, let’s imagine you are working for an operation team that is relying daily on a couple of CLIs. Like dbt
to manage your data workflows, and aws-sam-cli
to deploy them in the cloud.
For some practical reasons, you’d like to wrap all these commands into a big one. This is how to do it.
Note
Here is how I initialized this example on my machine:
$ git clone https://github.com/kdeldycke/click-extra
(...)
$ cd click-extra
(...)
$ python -m pip install uv
(...)
$ uv venv
(...)
$ source .venv/bin/activate
(...)
$ uv sync --all-extras
(...)
$ uv pip install dbt-core
(...)
$ uv pip install aws-sam-cli
(...)
That way I had the latest Click Extra, dbt
and aws-sam-cli
installed in the same virtual environment:
$ uv run -- dbt --version
Core:
- installed: 1.6.1
- latest: 1.6.2 - Update available!
Your version of dbt-core is out of date!
You can find instructions for upgrading here:
https://docs.getdbt.com/docs/installation
Plugins:
$ uv run -- sam --version
SAM CLI, version 1.97.0
Once you identified the entry points of each commands, you can easily wrap them into a top-level Click Extra CLI. Here is for instance the content of a wrap.py
script:
from click_extra import extra_group
from samcli.cli.main import cli as sam_cli
from dbt.cli.main import cli as dbt_cli
@extra_group
def main():
pass
main.add_command(cmd=sam_cli, name="aws_sam")
main.add_command(cmd=dbt_cli, name="dbt")
if __name__ == "__main__":
main()
And this simple script gets rendered into:
$ uv run -- python ./wrap.py
Usage: wrap.py [OPTIONS] COMMAND [ARGS]...
Options:
--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]
-C, --config CONFIG_PATH Location of the configuration file. Supports glob
pattern of local path and remote URL. [default:
~/Library/Application
Support/wrap.py/*.{toml,yaml,yml,json,ini,xml}]
--show-params Show all CLI parameters, their provenance, defaults
and value, then exit.
-v, --verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
[default: WARNING]
--version Show the version and exit.
-h, --help Show this message and exit.
Commands:
aws_sam AWS Serverless Application Model (SAM) CLI
dbt An ELT tool for managing your SQL transformations and data models.
Here you can see that the top-level CLI gets all the default options and behavior (including coloring) of @extra_group
. But it also made available the standalone aws_sam
and dbt
CLI as standard subcommands.
And they are perfectly functional as-is.
You can compare the output of the aws_sam
subcommand with its original one:
$ uv run -- python ./wrap.py aws_sam --help
Usage: wrap.py aws_sam [OPTIONS] COMMAND [ARGS]...
AWS Serverless Application Model (SAM) CLI
The AWS Serverless Application Model Command Line Interface (AWS SAM CLI) is
a command line tool that you can use with AWS SAM templates and supported
third-party integrations to build and run your serverless applications.
Learn more: https://docs.aws.amazon.com/serverless-application-model/
Commands:
Learn:
docs NEW! Launch the AWS SAM CLI documentation in a browser.
Create an App:
init Initialize an AWS SAM application.
Develop your App:
build Build your AWS serverless function code.
local Run your AWS serverless function locally.
validate Validate an AWS SAM template.
sync NEW! Sync an AWS SAM project to AWS.
remote NEW! Invoke or send an event to cloud resources in your AWS
Cloudformation stack.
Deploy your App:
package Package an AWS SAM application.
deploy Deploy an AWS SAM application.
Monitor your App:
logs Fetch AWS Cloudwatch logs for AWS Lambda Functions or
Cloudwatch Log groups.
traces Fetch AWS X-Ray traces.
And More:
list NEW! Fetch the state of your AWS serverless application.
delete Delete an AWS SAM application and the artifacts created
by sam deploy.
pipeline Manage the continuous delivery of your AWS serverless
application.
publish Publish a packaged AWS SAM template to AWS Serverless
Application Repository for easy sharing.
Options:
--beta-features / --no-beta-features
Enable/Disable beta features.
--debug Turn on debug logging to print debug message
generated by AWS SAM CLI and display
timestamps.
--version Show the version and exit.
--info Show system and dependencies information.
-h, --help Show this message and exit.
Examples:
Get Started: $wrap.py aws_sam init
$ uv run -- sam --help
Usage: sam [OPTIONS] COMMAND [ARGS]...
AWS Serverless Application Model (SAM) CLI
The AWS Serverless Application Model Command Line Interface (AWS SAM CLI) is
a command line tool that you can use with AWS SAM templates and supported
third-party integrations to build and run your serverless applications.
Learn more: https://docs.aws.amazon.com/serverless-application-model/
Commands:
Learn:
docs NEW! Launch the AWS SAM CLI documentation in a browser.
Create an App:
init Initialize an AWS SAM application.
Develop your App:
build Build your AWS serverless function code.
local Run your AWS serverless function locally.
validate Validate an AWS SAM template.
sync NEW! Sync an AWS SAM project to AWS.
remote NEW! Invoke or send an event to cloud resources in your AWS
Cloudformation stack.
Deploy your App:
package Package an AWS SAM application.
deploy Deploy an AWS SAM application.
Monitor your App:
logs Fetch AWS Cloudwatch logs for AWS Lambda Functions or
Cloudwatch Log groups.
traces Fetch AWS X-Ray traces.
And More:
list NEW! Fetch the state of your AWS serverless application.
delete Delete an AWS SAM application and the artifacts created
by sam deploy.
pipeline Manage the continuous delivery of your AWS serverless
application.
publish Publish a packaged AWS SAM template to AWS Serverless
Application Repository for easy sharing.
Options:
--beta-features / --no-beta-features
Enable/Disable beta features.
--debug Turn on debug logging to print debug message
generated by AWS SAM CLI and display
timestamps.
--version Show the version and exit.
--info Show system and dependencies information.
-h, --help Show this message and exit.
Examples:
Get Started: $sam init
Here is the highlighted differences to make them even more obvious:
@@ -1,5 +1,5 @@
-$ uv run -- python ./wrap.py aws_sam --help
-Usage: wrap.py aws_sam [OPTIONS] COMMAND [ARGS]...
+$ uv run -- sam --help
+Usage: sam [OPTIONS] COMMAND [ARGS]...
AWS Serverless Application Model (SAM) CLI
@@ -56,4 +56,4 @@
Examples:
- Get Started: $wrap.py aws_sam init
+ Get Started: $sam init
Now that all commands are under the same umbrella, there is no limit to your imagination!
Important
This might looks janky, but this franken-CLI might be a great way to solve practical problems in your situation.
You can augment them with your custom glue code. Or maybe mashing them up will simplify the re-distribution of these CLIs on your production machines. Or control their common dependencies. Or freeze their versions. Or hard-code some parameters. Or apply monkey-patches. Or chain these commands to create new kind of automation…
There is a miriad of possibilities. If you have some other examples in the same vein, please share them in an issue or even directly via a PR. I’d love to complement this documentation with creative use-cases.
click_extra.commands
API¶
classDiagram Command <|-- ExtraCommand Context <|-- ExtraContext ExtraCommand <|-- ExtraGroup ExtraHelpColorsMixin <|-- ExtraCommand Group <|-- ExtraGroup
Wraps vanilla Click and Cloup commands with extra features.
Our flavor of commands, groups and context are all subclasses of their vanilla counterparts, but are pre-configured with good and common defaults. You can still leverage the mixins in here to build up your own custom variants.
- click_extra.commands.patched_exit(self, code=0)[source]¶
Exits the application with a given exit code.
Forces the context to close before exiting, so callbacks attached to parameters will be called to clean up their state. This is not important in normal CLI execution as the Python process will just be destroyed. But it will lead to leaky states in unitttests. :rtype:
NoReturn
See also
This fix has been proposed upstream to Click.
- class click_extra.commands.ExtraContext(*args, meta=None, **kwargs)[source]¶
Bases:
Context
Like
cloup._context.Context
, but with the ability to populate the context’smeta
property at instantiation.Also inherits
color
property from parent context. And sets it to True for parentless contexts at instantiatiom, so we can always have colorized output.Todo
Propose addition of
meta
keyword upstream to Click.Like parent’s context but with an extra
meta
keyword-argument.Also force
color
property default to True if not provided by user and this context has no parent.- formatter_class¶
Use our own formatter to colorize the help screen.
alias of
HelpExtraFormatter
- click_extra.commands.default_extra_params()[source]¶
Default additional options added to
extra_command
andextra_group
. :rtype:list
[Option
]Caution
The order of options has been carefully crafted to handle subtle edge-cases and avoid leaky states in unittests.
You can still override this hard-coded order for easthetic reasons and it should be fine. Your end-users are unlikely to be affected by these sneaky bugs, as the CLI context is going to be naturraly reset after each invocation (which is not the case in unitests).
--time
/--no-time
Hint
--time
is placed at the top so all other options can be timed.
-C
,--config CONFIG_PATH
Attention
--config
is at the top so it can have a direct influence on the default behavior and value of the other options.
--color
,--ansi
/--no-color
,--no-ansi
--show-params
-v
,--verbosity LEVEL
--version
-h
,--help
Todo
For bullet-proof handling of edge-cases, we should probably add an indirection layer to have the processing order of options (the one below) different from the presentation order of options in the help screen.
This is probably something that has been requested in issue #544.
Important
Sensitivity to order still remains to be proven. With the code of Click Extra and its dependencies moving fast, there is a non-zero chance that all the options are now sound enough to be re-ordered in a more natural way.
- class click_extra.commands.ExtraCommand(*args, version=None, extra_option_at_end=True, populate_auto_envvars=True, **kwargs)[source]¶
Bases:
ExtraHelpColorsMixin
,Command
Like
cloup.command
, with sane defaults and extra help screen colorization.List of extra parameters:
- Parameters:
version (
str
|None
) – allows a version string to be set directly on the command. Will be passed to the first instance ofExtraVersionOption
parameter attached to the command.extra_option_at_end (
bool
) – reorders all parameters attached to the command, by moving all instances ofExtraOption
at the end of the parameter list. The original order of the options is preserved among themselves.populate_auto_envvars (
bool
) – forces all parameters to have their auto-generated environment variables registered. This address the shortcoming ofclick
which only evaluates them dynamiccaly. By forcing their registration, the auto-generated environment variables gets displayed in the help screen, fixing click#2483 issue.
By default, these Click context settings are applied:
auto_envvar_prefix = self.name
(Click feature)Auto-generate environment variables for all options, using the command ID as prefix. The prefix is normalized to be uppercased and all non-alphanumerics replaced by underscores.
help_option_names = ("--help", "-h")
(Click feature)Allow help screen to be invoked with either –help or -h options.
show_default = True
(Click feature)Show all default values in help screen.
Additionally, these Cloup context settings are set:
align_option_groups = False
(Cloup feature)show_constraints = True
(Cloup feature)show_subcommand_aliases = True
(Cloup feature)
Click Extra also adds its own
context_settings
:show_choices = None
(Click Extra feature)If set to
True
orFalse
, will force that value on all options, so we can globally show or hide choices when prompting a user for input. Only makes sense for options whoseprompt
property is set.Defaults to
None
, which will leave all options untouched, and let them decide of their ownshow_choices
setting.show_envvar = None
(Click Extra feature)If set to
True
orFalse
, will force that value on all options, so we can globally enable or disable the display of environment variables in help screen.Defaults to
None
, which will leave all options untouched, and let them decide of their ownshow_envvar
setting. The rationale being that discoverability of environment variables is enabled by the--show-params
option, which is active by default on extra commands. So there is no need to surcharge the help screen.This addresses the click#2313 issue.
To override these defaults, you can pass your own settings with the
context_settings
parameter:@extra_command( context_settings={ "show_default": False, ... } )
- context_class¶
alias of
ExtraContext
- main(*args, **kwargs)[source]¶
Pre-invocation step that is instantiating the context, then call
invoke()
within it.During context instantiation, each option’s callbacks are called. Beware that these might break the execution flow (like
--help
or--version
).
- make_context(info_name, args, parent=None, **extra)[source]¶
Intercept the call to the original
click.core.BaseCommand.make_context
so we can keep a copy of the raw, pre-parsed arguments provided to the CLI.The result are passed to our own
ExtraContext
constructor which is able to initialize the context’smeta
property under our ownclick_extra.raw_args
entry. This will be used inShowParamsOption.print_params()
to print the table of parameters fed to the CLI. :rtype:Any
See also
This workaround is being discussed upstream in click#1279.
- class click_extra.commands.ExtraGroup(*args, version=None, extra_option_at_end=True, populate_auto_envvars=True, **kwargs)[source]¶
Bases:
ExtraCommand
,Group
Like``cloup.Group``, with sane defaults and extra help screen colorization.
List of extra parameters:
- Parameters:
version (
str
|None
) – allows a version string to be set directly on the command. Will be passed to the first instance ofExtraVersionOption
parameter attached to the command.extra_option_at_end (
bool
) –reorders all parameters attached to the command, by moving all instances of
ExtraOption
at the end of the parameter list. The original order of the options is preserved among themselves.populate_auto_envvars (
bool
) –forces all parameters to have their auto-generated environment variables registered. This address the shortcoming of
click
which only evaluates them dynamiccaly. By forcing their registration, the auto-generated environment variables gets displayed in the help screen, fixing click#2483 issue.
By default, these Click context settings are applied:
auto_envvar_prefix = self.name
(Click feature)Auto-generate environment variables for all options, using the command ID as prefix. The prefix is normalized to be uppercased and all non-alphanumerics replaced by underscores.
help_option_names = ("--help", "-h")
(Click feature)Allow help screen to be invoked with either –help or -h options.
show_default = True
(Click feature)Show all default values in help screen.
Additionally, these Cloup context settings are set:
align_option_groups = False
(Cloup feature)show_constraints = True
(Cloup feature)show_subcommand_aliases = True
(Cloup feature)
Click Extra also adds its own
context_settings
:show_choices = None
(Click Extra feature)If set to
True
orFalse
, will force that value on all options, so we can globally show or hide choices when prompting a user for input. Only makes sense for options whoseprompt
property is set.Defaults to
None
, which will leave all options untouched, and let them decide of their ownshow_choices
setting.show_envvar = None
(Click Extra feature)If set to
True
orFalse
, will force that value on all options, so we can globally enable or disable the display of environment variables in help screen.Defaults to
None
, which will leave all options untouched, and let them decide of their ownshow_envvar
setting. The rationale being that discoverability of environment variables is enabled by the--show-params
option, which is active by default on extra commands. So there is no need to surcharge the help screen.This addresses the click#2313 issue.
To override these defaults, you can pass your own settings with the
context_settings
parameter:@extra_command( context_settings={ "show_default": False, ... } )
- command_class¶
Makes commands of an
ExtraGroup
be instances ofExtraCommand
.That way all subcommands created from an
ExtraGroup
benefits from the same defaults and extra help screen colorization.See: https://click.palletsprojects.com/en/stable/api/#click.Group.command_class
alias of
ExtraCommand
- group_class¶
Let
ExtraGroup
produce sub-groups that are also ofExtraGroup
type.See: https://click.palletsprojects.com/en/stable/api/#click.Group.group_class
alias of
type