Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Typer #26

Merged
merged 18 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ repos:
args: ["--config=.flake8"]
language: python
types: [python]
exclude: ^examples/
require_serial: true
additional_dependencies:
- flake8
Expand All @@ -53,6 +54,7 @@ repos:
entry: mypy
language: python
types: [python]
exclude: ^examples/
require_serial: true
additional_dependencies:
- mypy
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Version 1.2.2.dev0

- Added initial support for [Typer](https://typer.tiangolo.com/) [[#26](https://github.com/ewels/rich-click/pull/26)]
- Mark PEP 561 Compatibility [[#41](https://github.com/ewels/rich-click/pull/41)]
- Distribution now available via MacPorts [[#42](https://github.com/ewels/rich-click/pull/42)]
- Add typing information [[#39](https://github.com/ewels/rich-click/pull/39)]
Expand Down
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ click, formatted with rich, with minimal customisation required.

![rich-click](https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/command_groups.png)

_Screenshot from [`examples/03_groups_sorting.py`](examples/03_groups_sorting.py)_
_Screenshot from [`examples/click/03_groups_sorting.py`](examples/click/03_groups_sorting.py)_

## Installation

Expand Down Expand Up @@ -55,7 +55,7 @@ import rich_click as click

That's it ✨ Then continue to use `click` as you would normally.

> See [`examples/01_simple.py`](examples/01_simple.py) for an example.
> See [`examples/click/01_simple.py`](examples/click/01_simple.py) for 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.
Expand All @@ -65,7 +65,28 @@ If you spot something that breaks or is missing once you start using the plugin,
If you prefer, you can `RichGroup` or `RichCommand` with the `cls` argument in your click usage instead.
This means that you can continue to use the unmodified `click` package in parallel.

> See [`examples/02_declarative.py`](examples/02_declarative.py) for an example.
> See [`examples/click/02_declarative.py`](examples/click/02_declarative.py) for an example.

## Typer support

[`Typer`](https://github.com/tiangolo/typer) is also supported.
You need to use rich-click with the `typer` [extra](https://packaging.python.org/en/latest/tutorials/installing-packages/#installing-setuptools-extras) in your package requirements: `rich-click[typer]`

For example, to install locally:

```bash
python -m pip install rich-click[typer]
```

Then just replace your usual `typer` import by:

```python
import rich_click.typer as typer
```

That's it ✨ All the usual `typer` API should be available.

> See [`examples/typer/`](examples/typer/) for some example scripts.

### Command-line usage

Expand Down Expand Up @@ -105,7 +126,7 @@ for example: `[dim]\[my-default: foo][\]`

![Rich markup example](https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/rich_markup.png)

> See [`examples/04_rich_markup.py`](examples/04_rich_markup.py) fo
> See [`examples/click/04_rich_markup.py`](examples/click/04_rich_markup.py) fo

### Using Markdown

Expand All @@ -118,7 +139,7 @@ click.rich_click.USE_MARKDOWN = True

![Markdown example](https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/markdown.png)

> See [`examples/05_markdown.py`](examples/05_markdown.py) fo
> See [`examples/click/05_markdown.py`](examples/click/05_markdown.py) fo

### Positional arguments

Expand All @@ -135,7 +156,7 @@ click.rich_click.GROUP_ARGUMENTS_OPTIONS = True

![Positional arguments example](https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/arguments.png)

> See [`examples/06_arguments.py`](examples/06_arguments.py) for an example.
> See [`examples/click/06_arguments.py`](examples/click/06_arguments.py) for an example.

### Metavars and option choices

Expand All @@ -158,7 +179,7 @@ click.rich_click.APPEND_METAVARS_HELP = True

![Appended metavar](https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/metavars_appended.png)

> See [`examples/08_metavars.py`](examples/08_metavars.py) for an example.
> See [`examples/click/08_metavars.py`](examples/click/08_metavars.py) for an example.

### Error messages

Expand All @@ -169,7 +190,7 @@ By default, rich-click gives some nice formatting to error messages:
You can customise the _Try 'command --help' for help._ message with `ERRORS_SUGGESTION`
using rich-click though, and add some text after the error with `ERRORS_EPILOGUE`.

For example, from [`examples/07_custom_errors.py`](examples/07_custom_errors.py):
For example, from [`examples/click/07_custom_errors.py`](examples/click/07_custom_errors.py):

```python
click.rich_click.STYLE_ERRORS_SUGGESTION = "blue italic"
Expand Down Expand Up @@ -212,7 +233,7 @@ It accepts a list of options / commands which means you can also choose a custom

![rich-click](https://raw.githubusercontent.com/ewels/rich-click/main/docs/images/command_groups.png)

See [`examples/03_groups_sorting.py`](examples/03_groups_sorting.py) for a full example.
See [`examples/click/03_groups_sorting.py`](examples/click/03_groups_sorting.py) for a full example.

### Options

Expand Down
2 changes: 1 addition & 1 deletion examples/01_simple.py → examples/click/01_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def download(all):
\f
Also if you want to write function help text that won't
be rendered to the terminal.
""" # noqa: D301, D400
"""
print("Downloading")


Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@click.option(
"--input",
type=click.Path(),
help="Input [magenta bold]file[/]. [dim]\[default: a custom default][/]", # noqa: W605
help="Input [magenta bold]file[/]. [dim]\[default: a custom default][/]",
)
@click.option(
"--type",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions examples/typer/01_simple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import rich_click.typer as typer


def main():
"""Launch a CLI that says hello."""
typer.echo("Hello World")


if __name__ == "__main__":
typer.run(main)
56 changes: 56 additions & 0 deletions examples/typer/02_subcommands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import rich_click.typer as typer

app = typer.Typer()


@app.callback()
def cli(debug: bool = typer.Option(False, help="Enable debug mode.")):
"""
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'}")


@app.command()
def sync(
type: str = typer.Option("files", help="Type of file to sync"),
all: bool = typer.Option(False, help="Sync all the things?"),
):
"""Synchronise all your files between two places."""
print("Syncing")


@app.command()
def download(all: bool = typer.Option(False, help="Get everything")):
r"""
Pretend to download some files from
somewhere. Multi-line help strings are unwrapped
until you use a double newline.

Only the first paragraph is used in group help texts.
Don't forget you can opt-in to rich and markdown formatting!

\b
Click escape markers should still work.
* So you
* Can keep
* Your newlines

And this is a paragraph
that will be rewrapped again.

\f
Also if you want to write function help text that won't
be rendered to the terminal.
"""
print("Downloading")


if __name__ == "__main__":
app()
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"importlib-metadata; python_version < '3.8'",
],
extras_require={
"dev": ["pre-commit"],
"typer": "typer>=0.4",
"dev": "pre-commit",
},
package_data={"rich-click": ["py.typed"]},
)
35 changes: 35 additions & 0 deletions src/rich_click/typer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Any, Callable

from typer import * # noqa
from typer import Typer as BaseTyper
from typer.models import CommandFunctionType

from rich_click import RichCommand, RichGroup


class Typer(BaseTyper):
"""A custom subclassed version of typer.Typer to allow rich help."""

def __init__(
self,
*args,
cls=RichGroup,
**kwargs,
) -> None:
"""Initialise with a RichGroup class as the default."""
super().__init__(*args, cls=cls, **kwargs)

def command(
self,
*args,
cls=RichCommand,
**kwargs,
) -> Callable[[CommandFunctionType], CommandFunctionType]:
return super().command(*args, cls=cls, **kwargs)


def run(function: Callable[..., Any]) -> Any:
"""Redefine typer.run() to use our custom Typer class.""" # noqa D402
app = Typer()
app.command()(function)
app()