Package: rich-click
Richly rendered command line interfaces in click.rich-clickis a shim aroundclickthat renders help output nicely usingRich.Click is a"Python package for creating beautiful command line interfaces".Rich is a"Python library for rich text and beautiful formatting in the terminal".The intention ofrich-clickis to provide attractive help output from
click, formatted with rich, with minimal customisation required.Features🌈 Rich command-line formatting of click help and error messages💫 Nice styles be default, usage is simplyimport rich_click as click💻 CLI tool to run onother people'stools (prefix the command withrich-click)🎁 Group commands and options into named panels❌ Well formatted error messages🔢 Easily give custom sort order for options and commands🎨 Extensive customisation of styling and behaviour possibleExamplesSimple ExampleTo use rich-click in your code, replaceimport clickwithimport rich_click as clickin your existing click CLI:importrich_clickasclick@click.command()@click.option("--count",default=1,help="Number of greetings.")@click.option("--name",prompt="Your name",help="The person to greet.")defhello(count,name):"""Simple program that greets NAME for a total of COUNT times."""for_inrange(count):click.echo(f"Hello,{name}!")if__name__=='__main__':hello()Screenshot fromexamples/11_hello.pyMore complex exampleScreenshot fromexamples/03_groups_sorting.pyInstallationYou can installrich-clickfrom thePython Package Index (PyPI)withpipor equivalent.python-mpipinstallrich-clickConda users can findrich-clickonconda forge.
Just set up conda to use conda-forge (seedocs) then run:condainstallrich-clickUsers on macOS can installrich-clickviaMacPorts.sudoportinstallpy-rich-clickNote that rich-click requiresclick>=7but formatted subcommands (groups) only work withclick>=8. With v7 the output simply reverts to default click output.UsageImport as clickTo userich-click, switch out your normalclickimport withrich-click, using the same namespace:importrich_clickasclickThat's it! ✨ Then continue to useclickas you would normally.Seeexamples/01_simple.pyfor an example.The intention is to maintain most / all of the normal click functionality and arguments.
If you spot something that breaks or is missing once you start using the plugin, please create an issue about it.DeclarativeIf you prefer, you canRichGrouporRichCommandwith theclsargument in your click usage instead.
This means that you can continue to use the unmodifiedclickpackage in parallel.Seeexamples/02_declarative.pyfor an example.Command-line usagerich-clickcomes with a CLI tool that allows you to format the click help output fromanypackage.
As long as that tool is using click and isn't already passing customclsobjects, it should work.
However, please consider it an experimental feature at this point.To use, simply prefix to your normal command.
For example, to get richified click help text from a package calledawesometool, you could run:$rich-clickawesometool--helpUsage: awesometool [OPTIONS]..more richified output below..PatchingIn some situations, you might be registering a command from another Click CLI that does not use Rich-Click:importrich_clickasclickfromsome_libraryimportanother_cli@click.group("my-cli")defcli():pass# `another_cli` will NOT have Rich-Click markup. :(cli.add_command(another_cli)In this situation,another_cliretains its original behavior. In order to makeanother_cliwork with Rich-Click, you need to patchclickbefore you importanother_cli. You can patch Click withrich_click.cli.patchlike this:importrich_clickasclickfromrich_click.cliimportpatchpatch()fromsome_libraryimportanother_cli# noqa: E402@click.group("my-cli")defcli():pass# `another_cli` will have Rich-Click markup. :)cli.add_command(another_cli)CustomisationThere are a large number of customisation options in rich-click.
These can be modified by changing variables in theclick.rich_clicknamespace.Note that most normal click options should still work, such asshow_default=True,required=Trueandhidden=True.Note: All images below are auto-generated using another side-project of mine:rich-codex. Pretty cool!Using Rich markupIn order to be as widely compatible as possible with a simple import, rich-click doesnotparse rich formatting markup (eg.[red]) by default. You need to opt-in to this behaviour.To use rich markup in your help texts, add the following:click.rich_click.USE_RICH_MARKUP=TrueOr alternatively, with therich_configandRichHelpConfiguration:@click.command()@click.rich_config(help_config=click.RichHelpConfiguration(use_rich_markup=True))defcli():...Remember that you'll need to escape any regular square brackets using a back slash in your help texts,
for example:[dim]\[my-default: foo][\]Seeexamples/04_rich_markup.pyfor an example.Using MarkdownIf you prefer, you can use Markdown text.
You must choose either Markdown or rich markup. If you specify both, Markdown takes preference.click.rich_click.USE_MARKDOWN=TrueOr alternatively, with theRichHelpConfiguration:@click.command()@click.rich_config(help_config=click.RichHelpConfiguration(use_markdown=True))defcli():...Seeexamples/05_markdown.pyfor an example.Positional argumentsThe default click behaviour is to only show positional arguments in the top usage string,
and not in the list below with the options.If you prefer, you can tell rich-click to show arguments withSHOW_ARGUMENTS.
By default, they will get their own panel but you can tell rich-click to bundle them together withGROUP_ARGUMENTS_OPTIONS:click.rich_click.SHOW_ARGUMENTS=Trueclick.rich_click.GROUP_ARGUMENTS_OPTIONS=TrueOr alternatively, with theRichHelpConfiguration:help_config=click.RichHelpConfiguration(show_arguments=True,group_arguments_options=True)@click.command()@click.rich_config(help_config=help_config)defcli():...Seeexamples/06_arguments.pyfor an example.Metavars and option choicesMetavars are click's way of showing expected input types.
For example, if you have an option that must be an integer, the metavar isINTEGER.
If you have a choice, the metavar is a list of the possible values.By default, rich-click shows metavars in their own column.
However, if you have a long list of choices, this column can be quite wide and result in a lot of white space:It may look better to show metavars appended to the help text, instead of in their own column.
For this, use the following:click.rich_click.SHOW_METAVARS_COLUMN=Falseclick.rich_click.APPEND_METAVARS_HELP=Truehelp_config=click.RichHelpConfiguration(show_metavars_column=False,append_metavars_help=True)@click.command()@click.rich_config(help_config=help_config)defcli():...Seeexamples/08_metavars.pyfor an example.Error messagesBy default, rich-click gives some nice formatting to error messages:You can customise theTry 'command --help' for help.message withERRORS_SUGGESTIONusing rich-click though, and add some text after the error withERRORS_EPILOGUE.For example, fromexamples/07_custom_errors.py:click.rich_click.STYLE_ERRORS_SUGGESTION="magenta italic"click.rich_click.ERRORS_SUGGESTION="Try running the '--help' flag for more information."click.rich_click.ERRORS_EPILOGUE="To find out more, visit [link=https://mytool.com]https://mytool.com[/link]"Seeexamples/07_custom_errors.pyfor an example.Help widthThe default behaviour of rich-click is to use the full width of the terminal for output.
However, if you've carefully crafted your help texts for the default narrow click output, you may find that you now have a lot of whitespace at the side of the panels.To limit the maximum width of the help output, regardless of the terminal size, setWIDTHin characters as follows:click.rich_click.WIDTH=128To still use the full width of the terminal up to a certain limit, setMAX_WIDTHin characters as follows:click.rich_click.MAX_WIDTH=96SettingMAX_WIDTHoverrides the effect ofWIDTHStylingMost aspects of rich-click formatting can be customised, from colours to alignment.For example, to print the option flags in a different colour, you can use:click.rich_click.STYLE_OPTION="magenta"To add a blank line between rows of options, you can use:click.rich_click.STYLE_OPTIONS_TABLE_LEADING=1click.rich_click.STYLE_OPTIONS_TABLE_BOX="SIMPLE"You can make some reallyhorriblecolourfulsolutions using these styles if you wish:Seeexamples/10_table_styles.pyfor an example.See theConfiguration optionssection below for the full list of available options.Groups and sortingrich-clickgives functionality to list options and subcommands in groups, printed as separate panels.
It accepts a list of options / commands which means you can also choose a custom sorting order.For options (flags), setclick.rich_click.OPTION_GROUPSFor subcommands (groups), setclick.rich_click.COMMAND_GROUPSWhen grouping subcommands into more than one group (in above example: 'Main usage' and 'Configuration') you may find that the automatically calculated widths of different groups do not line up, due to varying option name lengths.You can avoid this by enforcing the alignment of the help text across groups by settingclick.rich_click.STYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO = (1, 2). This results in a fixed ratio of 1:2 for the width of command name and help text column.Seeexamples/03_groups_sorting.pyfor a full example.OptionsTo group option flags into two sections with custom names, see the following example:click.rich_click.OPTION_GROUPS={"mytool":[{"name":"Simple options","options":["--name","--description","--version","--help"],},{"name":"Advanced options","options":["--force","--yes","--delete"],},]}If you omitnameit will useCommands(can be configured withOPTIONS_PANEL_TITLE).CommandsHere we create two groups of commands for the base command ofmytool.
Any subcommands not listed will automatically be printed in a panel at the end labelled "Commands" as usual.click.rich_click.COMMAND_GROUPS={"mytool":[{"name":"Commands for uploading","commands":["sync","upload"],},{"name":"Download data","commands":["get","fetch","download"],},]}If you omitnameit will useCommands(can be configured withCOMMANDS_PANEL_TITLE).Multiple commandsIf you use multiple nested subcommands, you can specify their commands using the top-level dictionary keys:click.rich_click.COMMAND_GROUPS={"mytool":[{"commands":["sync","auth"]}],"mytool sync":[{"name":"Commands for uploading","commands":["sync","upload"],},{"name":"Download data","commands":["get","fetch","download"],},],"mytool auth":[{"commands":["login","logout"]}],}Table stylingTypically you would style the option / command tables using the global config options.
However, if you wish you may style tables on a per-group basis using thetable_styleskey:click.rich_click.COMMAND_GROUPS={"mytool":[{"commands":["sync","auth"],"table_styles":{"show_lines":True,"row_styles":["magenta","yellow","cyan","green"],"border_style":"red","box":"DOUBLE",},},],}The available keys are:show_lines,leading,box,border_style,row_styles,pad_edge,padding.Configuration optionsHere is the full list of config options:# Default stylesSTYLE_OPTION="bold cyan"STYLE_ARGUMENT="bold cyan"STYLE_COMMAND="bold cyan"STYLE_SWITCH="bold green"STYLE_METAVAR="bold yellow"STYLE_METAVAR_APPEND="dim yellow"STYLE_METAVAR_SEPARATOR="dim"STYLE_HEADER_TEXT=""STYLE_EPILOG_TEXT=""STYLE_FOOTER_TEXT=""STYLE_USAGE="yellow"STYLE_USAGE_COMMAND="bold"STYLE_DEPRECATED="red"STYLE_HELPTEXT_FIRST_LINE=""STYLE_HELPTEXT="dim"STYLE_OPTION_HELP=""STYLE_OPTION_DEFAULT="dim"STYLE_OPTION_ENVVAR="dim yellow"STYLE_REQUIRED_SHORT="red"STYLE_REQUIRED_LONG="dim red"STYLE_OPTIONS_PANEL_BORDER="dim"ALIGN_OPTIONS_PANEL="left"STYLE_OPTIONS_TABLE_SHOW_LINES=FalseSTYLE_OPTIONS_TABLE_LEADING=0STYLE_OPTIONS_TABLE_PAD_EDGE=FalseSTYLE_OPTIONS_TABLE_PADDING=(0,1)STYLE_OPTIONS_TABLE_BOX=""STYLE_OPTIONS_TABLE_ROW_STYLES=NoneSTYLE_OPTIONS_TABLE_BORDER_STYLE=NoneSTYLE_COMMANDS_PANEL_BORDER="dim"ALIGN_COMMANDS_PANEL="left"STYLE_COMMANDS_TABLE_SHOW_LINES=FalseSTYLE_COMMANDS_TABLE_LEADING=0STYLE_COMMANDS_TABLE_PAD_EDGE=FalseSTYLE_COMMANDS_TABLE_PADDING=(0,1)STYLE_COMMANDS_TABLE_BOX=""STYLE_COMMANDS_TABLE_ROW_STYLES=NoneSTYLE_COMMANDS_TABLE_BORDER_STYLE=NoneSTYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO=(None,None)STYLE_ERRORS_PANEL_BORDER="red"ALIGN_ERRORS_PANEL="left"STYLE_ERRORS_SUGGESTION="dim"STYLE_ERRORS_SUGGESTION_COMMAND="blue"STYLE_ABORTED="red"WIDTH=int(getenv("TERMINAL_WIDTH"))ifgetenv("TERMINAL_WIDTH")elseNoneMAX_WIDTH=int(getenv("TERMINAL_WIDTH"))ifgetenv("TERMINAL_WIDTH")elseWIDTHCOLOR_SYSTEM="auto"# Set to None to disable colorsFORCE_TERMINAL=Trueifgetenv("GITHUB_ACTIONS")orgetenv("FORCE_COLOR")orgetenv("PY_COLORS")elseNone# Fixed stringsHEADER_TEXT=NoneFOOTER_TEXT=NoneDEPRECATED_STRING="(Deprecated) "DEFAULT_STRING="[default:{}]"ENVVAR_STRING="[env var:{}]"REQUIRED_SHORT_STRING="*"REQUIRED_LONG_STRING="[required]"RANGE_STRING=" [{}]"APPEND_METAVARS_HELP_STRING="({})"ARGUMENTS_PANEL_TITLE="Arguments"OPTIONS_PANEL_TITLE="Options"COMMANDS_PANEL_TITLE="Commands"ERRORS_PANEL_TITLE="Error"ERRORS_SUGGESTION=None# Default: Try 'cmd -h' for help. Set to False to disable.ERRORS_EPILOGUE=NoneABORTED_TEXT="Aborted."# BehavioursSHOW_ARGUMENTS=False# Show positional argumentsSHOW_METAVARS_COLUMN=True# Show a column with the option metavar (eg. INTEGER)APPEND_METAVARS_HELP=False# Append metavar (eg. [TEXT]) after the help textGROUP_ARGUMENTS_OPTIONS=False# Show arguments with options instead of in own panelOPTION_ENVVAR_FIRST=False# Show env vars before option help text instead of avertUSE_MARKDOWN=False# Parse help strings as markdownUSE_MARKDOWN_EMOJI=True# Parse emoji codes in markdown :smile:USE_RICH_MARKUP=False# Parse help strings for rich markup (eg. [red]my text[/])COMMAND_GROUPS={}# Define sorted groups of panels to display subcommandsOPTION_GROUPS={}# Define sorted groups of panels to display options and argumentsUSE_CLICK_SHORT_HELP=False# Use click's default function to truncate help textFull type annotations of these config options are availble insrc/rich_click/rich_click.py.100% of these options are supported in theRichHelpConfigurationclass, as well.ContributingContributions and suggestions for new features are welcome, as are bug reports!
Please create a newissueor better still, dive right in with a pull-request.Local setupCreate a new venv with a python3.7+ interpreter usingpython3 -m venv venvActivate the venv withsource venv/bin/activateInstall our the package as an editable including all dev dependencies withpip3 install -e ."[dev]"Install pre-commit withpre-commit installPre-commitOur pre-commit hooks contain the following hooks:Prettier: formats our markdown and yaml files nicely.no relative imports: prevents you from using relative imports.iSort: will automatically sort the imports alphabetically.black: will automatically format your code to be according to standardized python format.flake8: will do linting checks to make sure all your code is correctly styled and used.mypy: static type checker which verifies you are not using objects incorrectly.As mentioned, some of these tools automatically fix your code while other only highlight potential issues.
Sometimes it will be enough to try to commit a second time and it will pass, while other times it may require
manual changes to your code.In rare cases it may be difficult or undesirable to change to code to pass the linting rules.
If this happens, it's ok to add a flake8# noqaor mypy# type: ignorecomment to skip that line.
For details of how to do this, please see theflake8 docsandmypy docs.CreditsThis package was written by Phil Ewels (@ewels),
based on initial code by Will McGugan (@willmcgugan).rich-click is co-maintained by@dwreeves.Furthermore, these contributors helped make the package what it is today:@BrutalSimplicity@harens@fridex@pawamoy@jorrickSee the full list of contributorshere.