Version¶
Click Extra provides its own version option which, compared to Click’s built-in:
adds new variable to compose your version string
adds colors
adds complete environment information in JSON
works with standalone scripts
expose metadata in the context
Hint
To prevent any confusion, and to keep on the promise of drop-in replacement, Click Extra’s version option is prefixed with extra_
:
the vanilla Click’s
version_option
is aliased asclick_extra.version_option
Click Extra’s own
version_option
is available asclick_extra.extra_version_option
Click Extra adds a
@extra_version_option
decorator which is based onclick_extra.ExtraVersionOption
class
Defaults¶
Here is how the defaults looks like:
from click_extra import command, extra_version_option
@command
@extra_version_option(version="1.2.3")
def cli():
pass
$ cli --help
Usage: cli [OPTIONS]
Options:
--version Show the version and exit.
--help Show this message and exit.
The default version message is the same as Click’s default, but colored:
$ cli --version
cli, version 1.2.3
Hint
In this example I have hard-coded the version to 1.2.3
for the sake of demonstration. In most cases, you do not need to force it. By default, the version will be automattically fetched from the __version__
attribute of the module where the command is defined.
Variables¶
The message template is a format string, which defaults to:
"{prog_name}, version {version}"
Important
This is different from Click, which uses the %(prog)s, version %(version)s
template.
Click is based on old-school printf
-style formatting, which relies on variables of the %(variable)s
form.
Click Extra uses modern format string syntax, with variables of the {variable}
form, to provide a more flexible and powerful templating.
You can customize the message template with the following variables:
Variable |
Description |
---|---|
The module object in which the command is implemented. |
|
The |
|
The full path of the file in which the command is implemented. |
|
The string found in the local |
|
The name of the package in which the CLI is distributed. |
|
The version from the package metadata in which the CLI is distributed. |
|
User-friendly name of the executed CLI. Returns |
|
Version of the CLI. Returns |
|
The name of the program, from Click’s point of view. |
|
The environment information in JSON. |
Caution
Some Click’s built-in variables are not recognized:
%(package)s
should be replaced by{package_name}
%(prog)s
should be replaced by{prog_name}
All other
%(variable)s
should be replaced by their{variable}
counterpart
You can compose your own version string by passing the message
argument:
from click_extra import command, extra_version_option
@command
@extra_version_option(message="✨ {prog_name} v{version} - {package_name}")
def my_own_cli():
pass
$ my-own-cli --version
✨ my-own-cli v4.11.8 - click_extra
Note
Notice here how the {package_name}
string takes the click_extra
value. That’s because this snippet of code is dynamiccaly executed by Sphinx in the context of Click Extra itself. And as a result, the {version}
string takes the value of the current version of Click Extra (i.e. click_extra.__version__
).
You will not have this behavior once your get your CLI packaged: your CLI will properly inherits its metadata automatically from your package.
Standalone script¶
The --version
option works with standalone scripts.
Let’s put this code in a file named greet.py
:
from click_extra import extra_command
@extra_command
def greet():
print("Hello world")
if __name__ == "__main__":
greet()
Here is the result of the --version
option:
$ python ./greet.py --version
greet.py, version None
Because the script is not packaged, the {package_name}
is set to the script file name (greet.py
) and {version}
variable to None
.
But Click Extra recognize a __version__
variable. So you can force the version string in your script:
from click_extra import extra_command
__version__ = "0.9.3-alpha"
@extra_command
def greet():
print("Hello world")
if __name__ == "__main__":
greet()
$ python ./greet.py --version
greet.py, version 0.9.3-alpha
Caution
The __version__
variable is not an enforced Python standard and more like a tradition.
It is supported by Click Extra as a convenience for script developers.
Colors¶
Each variable listed in the section above can be rendered in its own style. They all have dedicated parameters you can pass to the extra_version_option
decorator:
Parameter |
Description |
---|---|
|
Default style of the message. |
|
Style of the |
|
Style of the |
|
Style of the |
|
Style of the |
|
Style of the |
|
Style of the |
|
Style of the |
|
Style of the |
|
Style of the |
|
Style of the |
Here is an example:
from click_extra import command, extra_version_option, Style
@command
@extra_version_option(
message="{prog_name} v{version} 🔥 {package_name} ( ͡❛ ͜ʖ ͡❛)",
message_style=Style(fg="cyan"),
prog_name_style=Style(fg="green", bold=True),
version_style=Style(fg="bright_yellow", bg="red"),
package_name_style=Style(fg="bright_blue", italic=True),
)
def cli():
pass
$ cli --version
cli v4.11.8 🔥 click_extra ( ͡❛ ͜ʖ ͡❛)
Hint
You can pass None
to any of the style parameters to disable styling for the corresponding variable:
from click_extra import command, extra_version_option
@command
@extra_version_option(
message_style=None,
version_style=None,
prog_name_style=None,
)
def cli():
pass
$ cli --version
cli, version 4.11.8
Environment information¶
The {env_info}
variable compiles all sorts of environment information.
Here is how it looks like:
from click_extra import command, extra_version_option
@command
@extra_version_option(message="{env_info}")
def env_info_cli():
pass
$ env-info-cli --version
{'username': '-', 'guid': '1c610cf7acccb593e5be1007b93fb8c', 'hostname': '-', 'hostfqdn': '-', 'uname': {'system': 'Linux', 'node': '-', 'release': '6.8.0-1017-azure', 'version': '#20-Ubuntu SMP Tue Oct 22 03:43:13 UTC 2024', 'machine': 'x86_64', 'processor': 'x86_64'}, 'linux_dist_name': '', 'linux_dist_version': '', 'cpu_count': 4, 'fs_encoding': 'utf-8', 'ulimit_soft': 65536, 'ulimit_hard': 65536, 'cwd': '-', 'umask': '0o2', 'python': {'argv': '-', 'bin': '-', 'version': '3.12.7 (main, Oct 1 2024, 15:18:31) [GCC 13.2.0]', 'compiler': 'GCC 13.2.0', 'build_date': 'Oct 1 2024 15:18:31', 'version_info': [3, 12, 7, 'final', 0], 'features': {'openssl': 'OpenSSL 3.0.13 30 Jan 2024', 'expat': 'expat_2.6.3', 'sqlite': '3.45.1', 'tkinter': '8.6', 'zlib': '1.3', 'unicode_wide': True, 'readline': True, '64bit': True, 'ipv6': True, 'threading': True, 'urandom': True}}, 'time_utc': '2024-12-08 13:36:13.769349', 'time_utc_offset': 0.0, '_eco_version': '1.1.0'}
It’s verbose but it’s helpful for debugging and reporting of issues from end users.
Important
The JSON output is scrubbed out of identifiable information by default: current working directory, hostname, Python executable path, command-line arguments and username are replaced with -
.
Another trick consist in picking into the content of {env_info}
to produce highly customized version strings. This can be done because {env_info}
is kept as a dict
:
from click_extra import command, extra_version_option
@command
@extra_version_option(
message="{prog_name} {version}, from {module_file} (Python {env_info[python][version]})"
)
def custom_env_info():
pass
$ custom-env-info --version
custom-env-info 4.11.8, from /home/runner/work/click-extra/click-extra/click_extra/testing.py (Python 3.12.7 (main, Oct 1 2024, 15:18:31) [GCC 13.2.0])
Debug logs¶
When the DEBUG
level is enabled, all available variables will be printed in the log:
from click_extra import command, verbosity_option, extra_version_option, echo
@command
@extra_version_option
@verbosity_option
def version_in_logs():
echo("Standard operation")
$ version-in-logs --verbosity DEBUG
debug: Set <Logger click_extra (DEBUG)> to DEBUG.
debug: Set <RootLogger root (DEBUG)> to DEBUG.
debug: Version string template variables:
debug: {module} : <module 'click_extra.testing' from '/home/runner/work/click-extra/click-extra/click_extra/testing.py'>
debug: {module_name} : click_extra.testing
debug: {module_file} : /home/runner/work/click-extra/click-extra/click_extra/testing.py
debug: {module_version} : None
debug: {package_name} : click_extra
debug: {package_version}: 4.11.8
debug: {exec_name} : click_extra.testing
debug: {version} : 4.11.8
debug: {prog_name} : version-in-logs
debug: {env_info} : {'username': '-', 'guid': '1c610cf7acccb593e5be1007b93fb8c', 'hostname': '-', 'hostfqdn': '-', 'uname': {'system': 'Linux', 'node': '-', 'release': '6.8.0-1017-azure', 'version': '#20-Ubuntu SMP Tue Oct 22 03:43:13 UTC 2024', 'machine': 'x86_64', 'processor': 'x86_64'}, 'linux_dist_name': '', 'linux_dist_version': '', 'cpu_count': 4, 'fs_encoding': 'utf-8', 'ulimit_soft': 65536, 'ulimit_hard': 65536, 'cwd': '-', 'umask': '0o2', 'python': {'argv': '-', 'bin': '-', 'version': '3.12.7 (main, Oct 1 2024, 15:18:31) [GCC 13.2.0]', 'compiler': 'GCC 13.2.0', 'build_date': 'Oct 1 2024 15:18:31', 'version_info': [3, 12, 7, 'final', 0], 'features': {'openssl': 'OpenSSL 3.0.13 30 Jan 2024', 'expat': 'expat_2.6.3', 'sqlite': '3.45.1', 'tkinter': '8.6', 'zlib': '1.3', 'unicode_wide': True, 'readline': True, '64bit': True, 'ipv6': True, 'threading': True, 'urandom': True}}, 'time_utc': '2024-12-08 13:36:13.769349', 'time_utc_offset': 0.0, '_eco_version': '1.1.0'}
Standard operation
debug: Reset <RootLogger root (DEBUG)> to WARNING.
debug: Reset <Logger click_extra (DEBUG)> to WARNING.
Get metadata values¶
You can get the uncolored, Python values used in the composition of the version message from the context:
from click_extra import command, echo, pass_context, extra_version_option
@command
@extra_version_option
@pass_context
def version_metadata(ctx):
version = ctx.meta["click_extra.version"]
package_name = ctx.meta["click_extra.package_name"]
prog_name = ctx.meta["click_extra.prog_name"]
env_info = ctx.meta["click_extra.env_info"]
echo(f"version = {version}")
echo(f"package_name = {package_name}")
echo(f"prog_name = {prog_name}")
echo(f"env_info = {env_info}")
$ version-metadata --version
version-metadata, version 4.11.8
$ version-metadata
version = 4.11.8
package_name = click_extra
prog_name = version-metadata
env_info = {'username': '-', 'guid': '1c610cf7acccb593e5be1007b93fb8c', 'hostname': '-', 'hostfqdn': '-', 'uname': {'system': 'Linux', 'node': '-', 'release': '6.8.0-1017-azure', 'version': '#20-Ubuntu SMP Tue Oct 22 03:43:13 UTC 2024', 'machine': 'x86_64', 'processor': 'x86_64'}, 'linux_dist_name': '', 'linux_dist_version': '', 'cpu_count': 4, 'fs_encoding': 'utf-8', 'ulimit_soft': 65536, 'ulimit_hard': 65536, 'cwd': '-', 'umask': '0o2', 'python': {'argv': '-', 'bin': '-', 'version': '3.12.7 (main, Oct 1 2024, 15:18:31) [GCC 13.2.0]', 'compiler': 'GCC 13.2.0', 'build_date': 'Oct 1 2024 15:18:31', 'version_info': [3, 12, 7, 'final', 0], 'features': {'openssl': 'OpenSSL 3.0.13 30 Jan 2024', 'expat': 'expat_2.6.3', 'sqlite': '3.45.1', 'tkinter': '8.6', 'zlib': '1.3', 'unicode_wide': True, 'readline': True, '64bit': True, 'ipv6': True, 'threading': True, 'urandom': True}}, 'time_utc': '2024-12-08 13:36:13.769349', 'time_utc_offset': 0.0, '_eco_version': '1.1.0'}
Hint
These variables are presented in their original Python type. If most of these variables are strings, others like env_info
retains their original dict
type.
Template rendering¶
You can render the version string manually by calling the option’s internal methods:
from click_extra import command, echo, pass_context, extra_version_option, ExtraVersionOption, search_params
@command
@extra_version_option
@pass_context
def template_rendering(ctx):
# Search for a ``--version`` parameter.
version_opt = search_params(ctx.command.params, ExtraVersionOption)
version_string = version_opt.render_message()
echo(f"Version string ~> {version_string}")
Hint
To fetch the --version
parameter defined on the command, we rely on the click_extra.search_params.
$ template-rendering --version
template-rendering, version 4.11.8
$ template-rendering
Version string ~> template-rendering, version 4.11.8
That way you can collect the rendered version_string
, as if it was printed to the terminal by a call to --version
, and use it in your own way.
Other internal methods to build-up and render the version string are available in the API below.
click_extra.version
API¶
classDiagram ExtraOption <|-- ExtraVersionOption
Gather CLI metadata and print them.
- class click_extra.version.ExtraVersionOption(param_decls=None, message=None, module=None, module_name=None, module_file=None, module_version=None, package_name=None, package_version=None, exec_name=None, version=None, prog_name=None, env_info=None, message_style=None, module_style=None, module_name_style=Style(fg='bright_white', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'bright_white', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), module_file_style=None, module_version_style=Style(fg='green', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'green', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), package_name_style=Style(fg='bright_white', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'bright_white', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), package_version_style=Style(fg='green', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'green', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), exec_name_style=Style(fg='bright_white', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'bright_white', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), version_style=Style(fg='green', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'green', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), prog_name_style=Style(fg='bright_white', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'bright_white', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), env_info_style=Style(fg='bright_black', bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None, _style_kwargs={'fg': 'bright_black', 'bg': None, 'bold': None, 'dim': None, 'underline': None, 'overline': None, 'italic': None, 'blink': None, 'reverse': None, 'strikethrough': None}), is_flag=True, expose_value=False, is_eager=True, help='Show the version and exit.', **kwargs)[source]¶
Bases:
ExtraOption
Gather CLI metadata and prints a colored version string.
Note
This started as a copy of the standard @click.version_option() decorator, but is no longer a drop-in replacement. Hence the
Extra
prefix.This address the following Click issues:
click#2324, to allow its use with the declarative
params=
argument.click#2331, by distinguishing the module from the package.
click#1756, by allowing path and Python version.
Preconfigured as a
--version
option flag.- Parameters:
message (
str
|None
) – the message template to print, in format string syntax. Defaults to{prog_name}, version {version}
.module_name (
str
|None
) – forces the value of{module_name}
.module_file (
str
|None
) – forces the value of{module_file}
.module_version (
str
|None
) – forces the value of{module_version}
.package_name (
str
|None
) – forces the value of{package_name}
.package_version (
str
|None
) – forces the value of{package_version}
.env_info (
dict
[str
,str
] |None
) – forces the value of{env_info}
.message_style (
Optional
[Callable
[[str
],str
]]) – default style of the message.module_style (
Optional
[Callable
[[str
],str
]]) – style of{module}
.module_name_style (
Optional
[Callable
[[str
],str
]]) – style of{module_name}
.module_file_style (
Optional
[Callable
[[str
],str
]]) – style of{module_file}
.module_version_style (
Optional
[Callable
[[str
],str
]]) – style of{module_version}
.package_name_style (
Optional
[Callable
[[str
],str
]]) – style of{package_name}
.package_version_style (
Optional
[Callable
[[str
],str
]]) – style of{package_version}
.exec_name_style (
Optional
[Callable
[[str
],str
]]) – style of{exec_name}
.version_style (
Optional
[Callable
[[str
],str
]]) – style of{version}
.prog_name_style (
Optional
[Callable
[[str
],str
]]) – style of{prog_name}
.env_info_style (
Optional
[Callable
[[str
],str
]]) – style of{env_info}
.
- template_fields: tuple[str, ...] = ('module', 'module_name', 'module_file', 'module_version', 'package_name', 'package_version', 'exec_name', 'version', 'prog_name', 'env_info')¶
List of field IDs recognized by the message template.
- message: str = '{prog_name}, version {version}'¶
Default message template used to render the version string.
- static cli_frame()[source]¶
Returns the frame in which the CLI is implemented.
Inspects the execution stack frames to find the package in which the user’s CLI is implemented.
Returns the frame name, the frame itself, and the frame chain for debugging.
- Return type:
FrameType
- property module: ModuleType¶
Returns the module in which the CLI resides.
- property exec_name: str¶
User-friendly name of the executed CLI.
Returns the module name. But if the later is
__main__
, returns the package name.If not packaged, the CLI is assumed to be a simple standalone script, and the returned name is the script’s file name (including its extension).
- property version: str | None¶
Return the version of the CLI.
Returns the module version if a
__version__
variable is set alongside the CLI in its module.Else returns the package version if the CLI is implemented in a package, using importlib.metadata.version().
- property env_info: dict[str, str]¶
Various environment info.
Returns the data produced by boltons.ecoutils.get_profile().
- colored_template(template=None)[source]¶
Insert ANSI styles to a message template.
Accepts a custom
template
as parameter, otherwise uses the default message defined on the Option instance.This step is necessary because we need to linearize the template to apply the ANSI codes on the string segments. This is a consequence of the nature of ANSI, directives which cannot be encapsulated within another (unlike markup tags like HTML).
- Return type:
- render_message(template=None)[source]¶
Render the version string from the provided template.
Accepts a custom
template
as parameter, otherwise uses the defaultself.colored_template()
produced by the instance.- Return type:
- default: t.Union[t.Any, t.Callable[[], t.Any]]¶
- type: types.ParamType¶
- is_flag: bool¶
- is_bool_flag: bool¶
- flag_value: t.Any¶
- name: t.Optional[str]¶
- opts: t.List[str]¶
- secondary_opts: t.List[str]¶