rich-click CLI tool¶
Overview¶
rich-click comes with a CLI tool that allows you to format the Click help output for any CLI that uses Click.
To use, simply prefix rich-click to the command. Here are a few real world examples:
If the CLI is not installed as a script, you can also pass the location with: <module_name>:<click_command_name>.
For example, if you have a file located at path/to/my/cli.py, and the Click Command object is named main, then you can run: rich-click path.to.my.cli:main.
Warning
If you are experiencing any unexpected issues with the rich-click CLI, first make sure you are not calling
your command on load of the module.
For example, the following could cause a strange No such option: --output error when attempting to run rich-click --output html my_cli:cli:
import rich_click as click
@click.command("my-cli")
@click.argument("x")
def cli(x):
...
cli()
To make it so rich-click --output html works on the above code, add a if __name__ == "__main__":
import rich_click as click
@click.command("my-cli")
@click.argument("x")
def cli(x):
...
if __name__ == "__main__":
cli()
Render help text as HTML or SVG¶
You can also use rich-click --output=html [command] to render rich HTML for help text, or rich-click --output=svg [command] to generate an SVG.
This works for RichCommands as well as normal click Commands.
SVG example:
HTML example:
SVG and HTML generated from docs/code_snippets/rich_click_cli/app.py
Notes on how the rich-click CLI works¶
Under the hood, the rich-click CLI is patching the click module, and replacing the Click decorators and click.Command, click.Group, etc. objects with their equivalent rich-click versions.
Sometimes, a subclassed click.Command will overwrite one of these methods:
click.Command.format_usageclick.Command.format_help_textclick.Command.format_optionsclick.MultiCommand.format_commandsclick.Command.format_epilog
Patching Click internals can mess with method resolution order,
since by the time the downstream library subclasses the click.Command, it will be a RichCommand, and the subclass's method will take precedence over the RichCommand's methods.
The problem is that rich-click's methods can be incompatible or at least stylistically incongruous with the base Click help text rendering.
To solve this, rich-click checks whether a method comes from a "true" RichCommand subclass or if it just looks that way due to patching.
If RichCommand is "properly" subclassed, the override is allowed.
If the subclass is only a result of the patching operation, we ignore the aforementioned methods and use the rich-click implementation.
Long story short, the rich-click CLI is safe to subclassing when it is the user's intent to subclass a rich-click object. (This is so that you can use other nifty features of the CLI such as the --output option on your own rich-click CLIs)
That said, custom, non-rich-click implementations are ignored.
Using patch() as an end user¶
The functionality that rich-click uses to patch Click internals is available for use by rich-click end users,
and it occasionally comes in handy outside of the rich-click CLI.
In some situations, you might be registering a command from another Click CLI that does not use rich-click:
import rich_click as click
from some_library import another_cli
@click.group("my-cli")
def cli():
pass
# `another_cli` will NOT have rich-click markup. :(
cli.add_command(another_cli)
In this situation, another_cli retains its original help text behavior.
In order to make another_cli work with rich-click, you need to patch click before you import another_cli.
You can patch Click with rich_click.patch.patch like this:
import rich_click as click
from rich_click.patch import patch
patch()
from some_library import another_cli # noqa: E402
@click.group("my-cli")
def cli():
pass
# `another_cli` will have rich-click markup. :)
cli.add_command(another_cli)