Skip to content

Commit

Permalink
Merge pull request #10 from ewels/import-as
Browse files Browse the repository at this point in the history
Import as
  • Loading branch information
ewels authored Feb 17, 2022
2 parents 2561050 + 1cc1a1b commit d0eefad
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 132 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Version 0.4.0.dev0

Major change: New usage, so that we can avoid having to do monkey patching [#10](https://github.com/ewels/rich-click/pull/10).

- Add ability to create groups of options with separate panels
- Show positional arguments in their own panel by default
- Add config `GROUP_ARGUMENTS_OPTIONS` option to group with options
Expand Down
90 changes: 20 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@
The intention of `rich-click` is to provide attractive help output from
click, formatted with rich, with minimal customisation required.

## Features

- Rich formatting of click help and error messages
- Nice styles be default, with simple import
- Ability to give custom sort order for options and commands
- Group commands and options into named panels
- Extensive styling and behaviour customisation available

## Screenshots

<table>
<tr>
<th>Native click help</th>
<th>With <code>rich-click</code></th>
</tr>
<tr>
<td><img src="docs/images/example_with_just_click.png"></td>
<td><img src="docs/images/example_with_rich-click.png"></td>
</tr>
</table>
![rich-click](docs/images/example_with_rich-click.png)

## Installation

Expand All @@ -31,65 +30,16 @@ python -m pip install rich-click

## Usage

There are two main ways to set up `rich-click` formatting for your tool:
monkey patching or declarative.
Which you choose will depend on your use-case and your personal disposition!

Note that the intention is to maintain most / all of the normal click functionality and arguments.
If you spot something that is missing once you start using the plugin, please
create an issue about it.

### The path of least typing

Monkey patching is [probably bad](https://en.wikipedia.org/wiki/Monkey_patch#Pitfalls)
and you should only use this method if you are a Responsible Developer.
It's also good if you're lazy, as it requires very little typing.

Assuming that you're already using click, you only need to add a few lines:

```python
import rich_click
click.Command.format_help = rich_click.rich_format_help
click.Group.format_help = rich_click.rich_format_help
click.ClickException.show = rich_click.rich_format_error
click.UsageError.show = rich_click.rich_format_error
```

This _overwrites_ the default `click` methods with those from the `rich-click` package.
As such, no other changes are needed - just continue to use `click` as you would
normally and it will use these custom methods to print your help output.

### The good and proper way

If using monkey-patching in your project makes your palms sweaty and your pulse race,
then you'll be pleased to know that you can also use `rich-click` in a nicely
declarative and verbose manner:
To use `rich-click`, import it instead of `click` but under the same namespace:

```python
import click
import rich_click

class RichClickGroup(click.Group):
def format_help(self, ctx, formatter):
rich_click.rich_format_help(self, ctx, formatter)
class RichClickCommand(click.Command):
def format_help(self, ctx, formatter):
rich_click.rich_format_help(self, ctx, formatter)

@click.group(cls=RichClickGroup)
@click.option('--debug/--no-debug', default=False)
def cli(debug):
click.echo(f"Debug mode is {'on' if debug else 'off'}")

@cli.command(cls=RichClickCommand)
def sync():
click.echo('Syncing')
import rich_click as click
```

_(example based on the [click docs](https://click.palletsprojects.com/en/8.0.x/commands/))_
That's it. Then continue to use `click` as you would normally.

Here we are making new `Group` and `Command` child classes that are based on click.
We define our custom `format_help()` functions and then tell click to to use these classes with the `cls` argument.
The intention is to maintain most / all of the normal click functionality and arguments.
If you spot something that is missing once you start using the plugin, please create an issue about it.

## Groups and sorting

Expand All @@ -99,8 +49,8 @@ It accepts a list of options / commands which means you can also choose a custom
For example, you can produce something that looks like this:
![command groups](docs/images/command_groups.png)

- To do this with options (flags), set `rich_click.core.OPTION_GROUPS`.
- To do this with subcommands (groups), set `rich_click.core.COMMAND_GROUPS`.
- To do this with options (flags), set `click.rich_click.OPTION_GROUPS`.
- To do this with subcommands (groups), set `click.rich_click.COMMAND_GROUPS`.

### Options

Expand Down Expand Up @@ -129,7 +79,7 @@ Here we create two groups of commands for the base command of `mytool`.
Any subcommands not listed will automatically be printed in a panel at the end labelled "Commands" as usual.

```python
rich_click.core.COMMAND_GROUPS = {
click.rich_click.COMMAND_GROUPS = {
"mytool": [
{
"name": "Commands for uploading",
Expand All @@ -150,7 +100,7 @@ If you omit `name` it will use `Commands` (can be configured with `COMMANDS_PANE
If you use multiple nested subcommands, you can specify their commands using the top-level dictionary keys:

```python
rich_click.core.COMMAND_GROUPS = {
click.rich_click.COMMAND_GROUPS = {
"mytool": ["commands": ["sync", "auth"]],
"mytool sync": [
{
Expand All @@ -173,13 +123,13 @@ You can customise most things that are related to formatting.
For example, to limit the maximum width of the help output to 100 characters:

```python
rich_click.core.MAX_WIDTH = 100
click.rich_click.MAX_WIDTH = 100
```

To print the option flags in a different colour, use:

```python
rich_click.core.STYLE_OPTION = "magenta"
click.rich_click.STYLE_OPTION = "magenta"
```

<details><summary>Full list of config options</summary>
Expand Down
43 changes: 43 additions & 0 deletions examples/01_simple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import rich_click


@rich_click.group()
@rich_click.option(
"--debug/--no-debug", "-d/-n", default=False, help="Enable debug mode"
)
def cli(debug):
"""
My amazing tool does all the things.
This is a minimal example based on documentation
from the 'click' package.
You can try using --help at the top level and also for
specific group subcommands.
"""
print(f"Debug mode is {'on' if debug else 'off'}")


@cli.command()
@rich_click.option(
"--type",
required=True,
default="files",
show_default=True,
help="Type of file to sync",
)
@rich_click.option("--all", is_flag=True, help="Sync all the things?")
def sync(type, all):
"""Synchronise all your files between two places"""
print("Syncing")


@cli.command()
@rich_click.option("--all", is_flag=True, help="Get everything")
def download(all):
"""Pretend to download some files from somewhere"""
print("Downloading")


if __name__ == "__main__":
cli()
40 changes: 0 additions & 40 deletions examples/01_simple_monkeypatch.py

This file was deleted.

16 changes: 3 additions & 13 deletions examples/02_simple_declarative.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
import click
import rich_click
from rich_click import RichGroup, RichCommand


class RichClickGroup(click.Group):
def format_help(self, ctx, formatter):
rich_click.rich_format_help(self, ctx, formatter)


class RichClickCommand(click.Command):
def format_help(self, ctx, formatter):
rich_click.rich_format_help(self, ctx, formatter)


@click.group(cls=RichClickGroup)
@click.group(cls=RichGroup)
@click.option("--debug/--no-debug", default=False)
def cli(debug):
"""
Expand All @@ -27,7 +17,7 @@ def cli(debug):
click.echo(f"Debug mode is {'on' if debug else 'off'}")


@cli.command(cls=RichClickCommand)
@cli.command(cls=RichCommand)
def sync():
"""Synchronise all your files between two places"""
click.echo("Syncing")
Expand Down
50 changes: 50 additions & 0 deletions examples/03_simple_importas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import click
import rich_click

####
## Rich-click cli stuff
####
@rich_click.group()
@rich_click.option(
"--debug/--no-debug", "-d/-n", default=False, help="Enable debug mode"
)
def cli(debug):
"""
My amazing tool does all the things.
This is a minimal example based on documentation
from the 'click' package.
You can try using --help at the top level and also for
specific group subcommands.
"""
print(f"Debug mode is {'on' if debug else 'off'}")


@cli.command()
@rich_click.option("--all", is_flag=True, help="Sync all the things?")
def sync(all):
"""Synchronise all your files between two places"""
print("Syncing")


@cli.command()
@rich_click.option("--all", is_flag=True, help="Get everything")
def download(all):
"""Pretend to download some files from somewhere"""
print("Downloading")


####
## Vanilla click import cli stuff
####
@click.command()
@click.option("--all", is_flag=True, help="Sync all the things?")
def original(all):
"""Synchronise all your files between two places"""
print("Syncing")


if __name__ == "__main__":
cli() # Use rich-click, should be fancy output
# original() # Use vanilla click, should be simple output
17 changes: 15 additions & 2 deletions src/rich_click/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,18 @@

__version__ = "0.4.0.dev0"

from .core import rich_format_help
from .core import rich_format_error
from click import *
from .rich_click import RichGroup
from .rich_click import RichCommand


def group(*args, cls=RichGroup, **kwargs):
from click import group as click_group

return click_group(*args, cls=cls, **kwargs)


def command(*args, cls=RichCommand, **kwargs):
from click import command as click_command

return click_command(*args, cls=cls, **kwargs)
Loading

0 comments on commit d0eefad

Please sign in to comment.