Skip to content

Commit

Permalink
actual final changes (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwreeves committed Apr 14, 2024
1 parent 3c7852c commit cfd41c6
Show file tree
Hide file tree
Showing 19 changed files with 224 additions and 44 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
## Version 1.8.0dev (2023-04-09)

- Add `--rich-config` option to the `rich-click` CLI.
- Better typing for option groups and command groups with `TypedDict` [[#156](https://github.com/ewels/rich-click/pull/156)]
- Lazy load Rich to reduce overhead when not rendering help text. [[#154](https://github.com/ewels/rich-click/pull/154)]
- Some internal refactors. These refactors are aimed at making the abstractions more maintainable over time, more consistent, and more adept for advanced used cases.
- `rich_click.py` is exclusively the global config; all formatting has been moved to `rich_help_rendering.py`.
Expand All @@ -17,10 +16,17 @@
- Moved exclusively to `pyproject.toml` and removed `setup.py` / `setup.cfg`; thank you [@Stealthii](https://github.com/Stealthii)!
- Moved to `text_markup: Literal["markdown", "rich", None]` instead of booleans.
- Fixed issue where error messages would not print to `stderr` by default.
- Some quality of life improvements for command and option groups:
- Support both `command_path` and `command.name`
- New configuration options: [[#178](https://github.com/ewels/rich-click/pull/178)]
- `STYLE_OPTIONS_PANEL_BOX`
- `STYLE_COMMANDS_PANEL_BOX`
- `STYLE_ERRORS_PANEL_BOX`
- Many quality of life improvements for command and option groups:
- Support both `command_path` and `command.name`.
- Added wildcard (`*`) option for command groups and option groups, with thanks to [@ITProKyle](https://github.com/ITProKyle)!
- Resolve duplicates.
- Better typing for option groups and command groups with `TypedDict` [[#156](https://github.com/ewels/rich-click/pull/156)]
- Added `panel_styles` support to groups. [[#178](https://github.com/ewels/rich-click/pull/178)]
- Allow `table_styles` and `panel_styles` to be defined for the positional arguments group.

## Version 1.7.4 (2024-03-12)

Expand Down
3 changes: 3 additions & 0 deletions docs/documentation/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ STYLE_OPTION_ENVVAR = "dim yellow"
STYLE_REQUIRED_SHORT = "red"
STYLE_REQUIRED_LONG = "dim red"
STYLE_OPTIONS_PANEL_BORDER = "dim"
STYLE_OPTIONS_PANEL_BOX = "ROUNDED"
ALIGN_OPTIONS_PANEL = "left"
STYLE_OPTIONS_TABLE_SHOW_LINES = False
STYLE_OPTIONS_TABLE_LEADING = 0
Expand All @@ -151,6 +152,7 @@ STYLE_OPTIONS_TABLE_BOX = ""
STYLE_OPTIONS_TABLE_ROW_STYLES = None
STYLE_OPTIONS_TABLE_BORDER_STYLE = None
STYLE_COMMANDS_PANEL_BORDER = "dim"
STYLE_COMMANDS_PANEL_BOX = "ROUNDED"
ALIGN_COMMANDS_PANEL = "left"
STYLE_COMMANDS_TABLE_SHOW_LINES = False
STYLE_COMMANDS_TABLE_LEADING = 0
Expand All @@ -161,6 +163,7 @@ STYLE_COMMANDS_TABLE_ROW_STYLES = None
STYLE_COMMANDS_TABLE_BORDER_STYLE = None
STYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO = (None, None)
STYLE_ERRORS_PANEL_BORDER = "red"
STYLE_ERRORS_PANEL_BOX = "ROUNDED"
ALIGN_ERRORS_PANEL = "left"
STYLE_ERRORS_SUGGESTION = "dim"
STYLE_ERRORS_SUGGESTION_COMMAND = "blue"
Expand Down
107 changes: 99 additions & 8 deletions docs/documentation/groups_and_sorting.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ Instead of defining the group based on the command path, you can use wildcards i
click.rich_click.COMMAND_GROUPS = {
"*": [
{
"name": "Commands for uploading",
"commands": ["sync", "upload"],
"name": "Commands for entity management",
"commands": ["user", "item"],
}
]
}
Expand All @@ -97,22 +97,43 @@ click.rich_click.OPTION_GROUPS = {
"*": [
{
"name": "Simple options",
"options": ["--name", "--description", "--version", "--help"],
"options": ["--name", "--description"],
},
]
],
# This will match to all subcommands of "user", e.g. "cli user create" and "cli user update".
"cli user *": [
{
"name": "Cloud",
"options": ["--region", "--env"],
},
],
# This will match to things like "cli user delete" and "cli item delete".
"cli * delete": [
{
"name": "Advanced",
"options": ["--force"],
},
],
}
```

This will apply the groups to every subcommand of the command group.
If a command or option specified in the wildcard does not exist, then it is ignored.
Using the above code as an example, imagine the command `cli user describe` does not have an option `--environment`.
It would still be safe in that case to map `--environment` to `cli user *`; it will just be ignored.

If an option is specified for both a wildcard and explicitly named command, then the wildcard is ignored;
explicit naming always takes precedence.

## Table styling
!!! warning
**rich-click** does its best to attempt to resolve duplicate options/command definitions, but other than
preferring exact matches to wildcards, the exact logic of how duplicates are resolved is subject to
revision in upcoming minor version releases. Don't get too clever with duplicating your options/commands!

Typically 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 the `table_styles` key:
## Styling

Typically, 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 the `table_styles` and `panel_styles` keys:

```python
click.rich_click.COMMAND_GROUPS = {
Expand All @@ -125,9 +146,79 @@ click.rich_click.COMMAND_GROUPS = {
"border_style": "red",
"box": "DOUBLE",
},
"panel_styles": {
"box": "ASCII",
}
},
],
}
```

The available keys are: `show_lines`, `leading`, `box`, `border_style`, `row_styles`, `pad_edge`, `padding`.
The `table_styles` dict is pass as kwargs into the inner `rich.table.Table()`, and `panel_styles` is passed into the outer `rich.panel.Panel()`.

You can view the respective docstrings of the `Table` and `Panel` objects for more information:

- [`rich/table.py`](https://github.com/Textualize/rich/blob/master/rich/table.py)
- [`rich/panel.py`](https://github.com/Textualize/rich/blob/master/rich/panel.py)

### Argument Styles

The panel for positional arguments is a little special.

If the following three things are true...:

1. `config.show_arguments` is `True`.
2. There is an option group with a `name` equal to the `config.arguments_panel_title` (default: `'Arguments'`)
3. The option group does not have any `options` (The list is empty, undefined, or `None`).

... Then this allows you to specify styling options for the `Arguments` panel separately of other panels.

For example, in the below code, the `Arguments` panel will have a box type of `ASCII`,
independent of the options panel.

```python
import rich_click as click

help_config = click.RichHelpConfiguration(
show_arguments=True,
option_groups={"my-command": [{"name": "Arguments", "panel_styles": {"box": "ASCII"}}]}
)

@click.command
@click.argument("foo")
@click.option("--bar")
@click.rich_config(help_config=help_config)
def cli(foo, bar):
...
```

## Usage notes

Be careful implementing command/option groups with `@click.rich_config(help_config=...)` in subcommands:
- The groups are _**not**_ merged to the parent help config.
- It is _**still required**_ to spell out the whole command as the key, or at least match with a wildcard.

For example, the following code works:

```python
@click.group("my-cli")
def my_cli():
...

# This example uses `"*my-subcommand"` to enable it to map to any arbitrarily named parent command.
@my_cli.command("my-subcommand")
@click.option("--version")
@click.rich_config(help_config={"option_groups": {"*my-subcommand": [{"name": "Version", "commands": ["--version"]}]}})
def my_subcommand():
...
```

However, if you were to only specify `"my-subcommand"`

!!! info
The reason why specifying option/command groups is like this has to do with **rich-click**'s history.
Basically, the `@click.rich_config()` decorator and `RichHelpConfiguration()` object were not introduced until version 1.7.0.
Prior to that, the only way to define config options was with the global configuration.

So, the way that `option_groups` and `command_groups` are defined is a relic of **rich-click**'s global configuration.
**rich-click**'s maintainers are aware of how awkward it feels, and we'll be introducing a more seamless and natural API in a future release. 😊
2 changes: 1 addition & 1 deletion src/rich_click/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
customisation required.
"""

__version__ = "1.8.0dev5"
__version__ = "1.8.0dev6"

# Import the entire click API here.
# We need to manually import these instead of `from click import *` to force
Expand Down
8 changes: 6 additions & 2 deletions src/rich_click/rich_click.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

if TYPE_CHECKING:
import rich.align
import rich.box
import rich.padding
import rich.style

Expand All @@ -34,25 +35,28 @@
STYLE_REQUIRED_SHORT: "rich.style.StyleType" = "red"
STYLE_REQUIRED_LONG: "rich.style.StyleType" = "dim red"
STYLE_OPTIONS_PANEL_BORDER: "rich.style.StyleType" = "dim"
STYLE_OPTIONS_PANEL_BOX: Optional[Union[str, "rich.box.Box"]] = "ROUNDED"
ALIGN_OPTIONS_PANEL: "rich.align.AlignMethod" = "left"
STYLE_OPTIONS_TABLE_SHOW_LINES: bool = False
STYLE_OPTIONS_TABLE_LEADING: int = 0
STYLE_OPTIONS_TABLE_PAD_EDGE: bool = False
STYLE_OPTIONS_TABLE_PADDING: "rich.padding.PaddingDimensions" = (0, 1)
STYLE_OPTIONS_TABLE_BOX: "rich.style.StyleType" = ""
STYLE_OPTIONS_TABLE_BOX: Optional[Union[str, "rich.box.Box"]] = ""
STYLE_OPTIONS_TABLE_ROW_STYLES: Optional[List["rich.style.StyleType"]] = None
STYLE_OPTIONS_TABLE_BORDER_STYLE: Optional["rich.style.StyleType"] = None
STYLE_COMMANDS_PANEL_BORDER: "rich.style.StyleType" = "dim"
STYLE_COMMANDS_PANEL_BOX: Optional[Union[str, "rich.box.Box"]] = "ROUNDED"
ALIGN_COMMANDS_PANEL: "rich.align.AlignMethod" = "left"
STYLE_COMMANDS_TABLE_SHOW_LINES: bool = False
STYLE_COMMANDS_TABLE_LEADING: int = 0
STYLE_COMMANDS_TABLE_PAD_EDGE: bool = False
STYLE_COMMANDS_TABLE_PADDING: "rich.padding.PaddingDimensions" = (0, 1)
STYLE_COMMANDS_TABLE_BOX: "rich.style.StyleType" = ""
STYLE_COMMANDS_TABLE_BOX: Optional[Union[str, "rich.box.Box"]] = ""
STYLE_COMMANDS_TABLE_ROW_STYLES: Optional[List["rich.style.StyleType"]] = None
STYLE_COMMANDS_TABLE_BORDER_STYLE: Optional["rich.style.StyleType"] = None
STYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO: Optional[Union[Tuple[None, None], Tuple[int, int]]] = (None, None)
STYLE_ERRORS_PANEL_BORDER: "rich.style.StyleType" = "red"
STYLE_ERRORS_PANEL_BOX: Optional[Union[str, "rich.box.Box"]] = "ROUNDED"
ALIGN_ERRORS_PANEL: "rich.align.AlignMethod" = "left"
STYLE_ERRORS_SUGGESTION: "rich.style.StyleType" = "dim"
STYLE_ERRORS_SUGGESTION_COMMAND: "rich.style.StyleType" = "blue"
Expand Down
8 changes: 6 additions & 2 deletions src/rich_click/rich_help_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

if TYPE_CHECKING:
import rich.align
import rich.box
import rich.highlighter
import rich.padding
import rich.style
Expand Down Expand Up @@ -80,27 +81,30 @@ class RichHelpConfiguration:
style_required_short: "rich.style.StyleType" = field(default="red")
style_required_long: "rich.style.StyleType" = field(default="dim red")
style_options_panel_border: "rich.style.StyleType" = field(default="dim")
style_options_panel_box: Optional[Union[str, "rich.box.Box"]] = field(default="ROUNDED")
align_options_panel: "rich.align.AlignMethod" = field(default="left")
style_options_table_show_lines: bool = field(default=False)
style_options_table_leading: int = field(default=0)
style_options_table_pad_edge: bool = field(default=False)
style_options_table_padding: "rich.padding.PaddingDimensions" = field(default_factory=lambda: (0, 1))
style_options_table_box: "rich.style.StyleType" = field(default="")
style_options_table_box: Optional[Union[str, "rich.box.Box"]] = field(default="")
style_options_table_row_styles: Optional[List["rich.style.StyleType"]] = field(default=None)
style_options_table_border_style: Optional["rich.style.StyleType"] = field(default=None)
style_commands_panel_border: "rich.style.StyleType" = field(default="dim")
style_commands_panel_box: Optional[Union[str, "rich.box.Box"]] = field(default="ROUNDED")
align_commands_panel: "rich.align.AlignMethod" = field(default="left")
style_commands_table_show_lines: bool = field(default=False)
style_commands_table_leading: int = field(default=0)
style_commands_table_pad_edge: bool = field(default=False)
style_commands_table_padding: "rich.padding.PaddingDimensions" = field(default_factory=lambda: (0, 1))
style_commands_table_box: "rich.style.StyleType" = field(default="")
style_commands_table_box: Optional[Union[str, "rich.box.Box"]] = field(default="")
style_commands_table_row_styles: Optional[List["rich.style.StyleType"]] = field(default=None)
style_commands_table_border_style: Optional["rich.style.StyleType"] = field(default=None)
style_commands_table_column_width_ratio: Optional[Union[Tuple[None, None], Tuple[int, int]]] = field(
default_factory=lambda: (None, None)
)
style_errors_panel_border: "rich.style.StyleType" = field(default="red")
style_errors_panel_box: Optional[Union[str, "rich.box.Box"]] = field(default="ROUNDED")
align_errors_panel: "rich.align.AlignMethod" = field(default="left")
style_errors_suggestion: "rich.style.StyleType" = field(default="dim")
style_errors_suggestion_command: "rich.style.StyleType" = field(default="blue")
Expand Down
Loading

0 comments on commit cfd41c6

Please sign in to comment.