Skip to content

Commit

Permalink
feat: Add support for isolated configuration of Rich Console and help…
Browse files Browse the repository at this point in the history
… formatting

- Adhere to click `HelpFormatter` interface
- Add tests :D

See ewels#89 ewels#88 ewels#25 ewels#19
  • Loading branch information
BrutalSimplicity committed Oct 23, 2022
1 parent 1eef5ed commit 85a5ed6
Show file tree
Hide file tree
Showing 64 changed files with 3,272 additions and 851 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
# Default ignores that we can extend
ignore=D100,D102,D205,E203,E231,E731,W504,I001,W503
max-line-length=120
exclude = .git,__pycache__,.venv,build,sdist,tests/**,examples/**
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -762,4 +762,6 @@ FodyWeavers.xsd
### VisualStudio Patch ###
# Additional files built by Visual Studio

# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,pycharm+all,visualstudio,intellij+all,python
# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,pycharm+all,visualstudio,intellij+all,python

tmp
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repos:
- id: identity
- id: check-hooks-apply
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "" # Can not be removed, so leave this empty.
rev: "v3.0.0-alpha.3" # Can not be removed, so leave this empty.
hooks:
- id: prettier
- repo: local
Expand Down Expand Up @@ -38,7 +38,7 @@ repos:
args: ["--config=.flake8"]
language: python
types: [python]
exclude: ^examples/
exclude: ^examples/|^tests
require_serial: true
additional_dependencies:
- flake8
Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"python.defaultInterpreterPath": ".venv/bin/python",
"python.testing.pytestPath": ".venv/bin/pytest",
"python.linting.flake8Path": ".venv/bin/flake8",
"python.linting.flake8Enabled": true,
"python.testing.pytestEnabled": true
}
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"click>=7",
"rich>=10.7.0",
"importlib-metadata; python_version < '3.8'",
"typing_extensions",
],
extras_require={
"dev": ["pre-commit", "pytest", "typing_extensions"],
"dev": ["pre-commit", "pytest", "flake8", "flake8-docstrings"],
},
)
66 changes: 63 additions & 3 deletions src/rich_click/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@

__version__ = "1.6.0.dev0"

from typing import TYPE_CHECKING
from typing import Optional, TYPE_CHECKING

from click import * # noqa: F401, F403
from click import command as click_command
from click import Command
from click import group as click_group
from click import Group
from rich.console import Console

from . import rich_click # noqa: F401

from rich_click.rich_command import RichCommand
from rich_click.rich_group import RichGroup
from rich_click.rich_help_configuration import RichHelpConfiguration

# MyPy does not like star imports. Therefore when we are type checking, we import each individual module
# from click here. This way MyPy will recognize the import and not throw any errors. Furthermore, because of
Expand All @@ -41,7 +45,20 @@ def group(*args, cls=RichGroup, **kwargs):
Defines the group() function so that it uses the RichGroup class by default.
"""
return click_group(*args, cls=cls, **kwargs)

def wrapper(fn):
if hasattr(fn, "__rich_context_settings__"):
rich_context_settings = getattr(fn, "__rich_context_settings__", {})
console = rich_context_settings.get("rich_console", None)
help_config = rich_context_settings.get("help_config", None)
context_settings = kwargs.get("context_settings", {})
context_settings.update(rich_console=console, rich_help_config=help_config)
kwargs.update(context_settings=context_settings)
del fn.__rich_context_settings__
cmd = click_group(*args, cls=cls, **kwargs)(fn)
return cmd

return wrapper


def command(*args, cls=RichCommand, **kwargs):
Expand All @@ -50,4 +67,47 @@ def command(*args, cls=RichCommand, **kwargs):
Defines the command() function so that it uses the RichCommand class by default.
"""
return click_command(*args, cls=cls, **kwargs)

def wrapper(fn):
if hasattr(fn, "__rich_context_settings__"):
rich_context_settings = getattr(fn, "__rich_context_settings__", {})
console = rich_context_settings.get("rich_console", None)
help_config = rich_context_settings.get("help_config", None)
context_settings = kwargs.get("context_settings", {})
context_settings.update(rich_console=console, rich_help_config=help_config)
kwargs.update(context_settings=context_settings)
del fn.__rich_context_settings__
cmd = click_command(*args, cls=cls, **kwargs)(fn)
return cmd

return wrapper


class NotSupportedError(Exception):
"""Not Supported Error."""

pass


def rich_config(console: Optional[Console] = None, help_config: Optional[RichHelpConfiguration] = None):
"""Use decorator to configure Rich Click settings.
Args:
console: A Rich Console that will be accessible from the `RichContext`, `RichCommand`, and `RichGroup` instances
Defaults to None.
help_config: Rich help configuration that is used internally to format help messages and exceptions
Defaults to None.
"""

def decorator(obj):
if isinstance(obj, (RichCommand, RichGroup)):
obj.context_settings.update({"rich_console": console, "rich_help_config": help_config})
elif callable(obj) and not isinstance(obj, (Command, Group)):
setattr(obj, "__rich_context_settings__", {"rich_console": console, "rich_help_config": help_config})
else:
raise NotSupportedError("`rich_config` requires a `RichCommand` or `RichGroup`. Try using the cls keyword")

decorator.__doc__ = obj.__doc__
return obj

return decorator
Loading

0 comments on commit 85a5ed6

Please sign in to comment.