rich-click 1.9.0.dev2__tar.gz → 1.9.0.dev3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {rich_click-1.9.0.dev2/src/rich_click.egg-info → rich_click-1.9.0.dev3}/PKG-INFO +5 -3
  2. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/README.md +2 -2
  3. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/pyproject.toml +2 -0
  4. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/__init__.py +1 -1
  5. rich_click-1.9.0.dev3/src/rich_click/_internal_types.py +250 -0
  6. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/cli.py +87 -132
  7. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/decorators.py +17 -1
  8. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/decorators.pyi +23 -221
  9. rich_click-1.9.0.dev3/src/rich_click/patch.py +270 -0
  10. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_click.py +13 -4
  11. rich_click-1.9.0.dev3/src/rich_click/rich_click_theme.py +1098 -0
  12. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_command.py +64 -13
  13. rich_click-1.9.0.dev3/src/rich_click/rich_command.pyi +428 -0
  14. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_help_configuration.py +110 -35
  15. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_help_rendering.py +144 -47
  16. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_panel.py +21 -33
  17. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/utils.py +1 -1
  18. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3/src/rich_click.egg-info}/PKG-INFO +5 -3
  19. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click.egg-info/SOURCES.txt +5 -2
  20. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click.egg-info/requires.txt +2 -0
  21. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/tests/test_config.py +3 -59
  22. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/tests/test_rich_click_cli.py +28 -27
  23. rich_click-1.9.0.dev3/tests/test_types.py +101 -0
  24. rich_click-1.9.0.dev2/src/rich_click/patch.py +0 -72
  25. rich_click-1.9.0.dev2/src/rich_click/rich_theme.py +0 -817
  26. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/LICENSE +0 -0
  27. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/setup.cfg +0 -0
  28. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/__main__.py +0 -0
  29. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/_compat_click.py +0 -0
  30. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/py.typed +0 -0
  31. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_box.py +0 -0
  32. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_context.py +0 -0
  33. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_group.py +0 -0
  34. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_help_formatter.py +0 -0
  35. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click/rich_parameter.py +0 -0
  36. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click.egg-info/dependency_links.txt +0 -0
  37. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click.egg-info/entry_points.txt +0 -0
  38. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/src/rich_click.egg-info/top_level.txt +0 -0
  39. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/tests/test_deprecations.py +0 -0
  40. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/tests/test_exit_code.py +0 -0
  41. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/tests/test_misc.py +0 -0
  42. {rich_click-1.9.0.dev2 → rich_click-1.9.0.dev3}/tests/test_no_rich_imports.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rich-click
3
- Version: 1.9.0.dev2
3
+ Version: 1.9.0.dev3
4
4
  Summary: Format click help output nicely with rich
5
5
  Author-email: Phil Ewels <phil@ewels.co.uk>
6
6
  Maintainer-email: Phil Ewels <phil@ewels.co.uk>, Daniel Reeves <xdanielreeves@gmail.com>
@@ -57,6 +57,7 @@ Requires-Dist: pytest>=8.3.5; extra == "dev"
57
57
  Requires-Dist: pytest-cov>=5; extra == "dev"
58
58
  Requires-Dist: rich-codex>=1.2.11; extra == "dev"
59
59
  Requires-Dist: ruff>=0.12.4; extra == "dev"
60
+ Requires-Dist: typer>=0.15; extra == "dev"
60
61
  Requires-Dist: types-setuptools>=75.8.0.20250110; extra == "dev"
61
62
  Provides-Extra: docs
62
63
  Requires-Dist: markdown-include>=0.8.1; extra == "docs"
@@ -70,6 +71,7 @@ Requires-Dist: mkdocs-material-extensions>=1.3.1; extra == "docs"
70
71
  Requires-Dist: mkdocs-rss-plugin>=1.15; extra == "docs"
71
72
  Requires-Dist: mkdocstrings[python]>=0.26.1; extra == "docs"
72
73
  Requires-Dist: rich-codex>=1.2.11; extra == "docs"
74
+ Requires-Dist: typer>=0.15; extra == "docs"
73
75
  Dynamic: license-file
74
76
 
75
77
  <p align="center">
@@ -109,12 +111,12 @@ Click, formatted with Rich, with minimal customization required.
109
111
  - 🌈 Rich command-line formatting of click help and error messages
110
112
  - 😌 Same API as Click: usage is simply `import rich_click as click`
111
113
  - 🎨 Over 100 themes that can be set by developers and end-users (`export RICH_CLICK_THEME=...`)
112
- - 💻 CLI tool to run on _other people's_ tools (prefix the command with `rich-click`)
114
+ - 💻 CLI tool to run on _other people's_ Click and Typer CLIs (prefix the command with `rich-click`)
113
115
  - 📦 Export help text as HTML or SVG
114
116
  - 🎁 Group commands and options into named panels
115
117
  - ❌ Well formatted error messages
116
118
  - 💫 Extensive customization
117
- - 🤖 IDE autocomplete of Click decorators for smooth developer experience.
119
+ - 🤖 IDE autocomplete of Click decorators for smooth developer experience
118
120
 
119
121
  ## Installation
120
122
 
@@ -35,12 +35,12 @@ Click, formatted with Rich, with minimal customization required.
35
35
  - 🌈 Rich command-line formatting of click help and error messages
36
36
  - 😌 Same API as Click: usage is simply `import rich_click as click`
37
37
  - 🎨 Over 100 themes that can be set by developers and end-users (`export RICH_CLICK_THEME=...`)
38
- - 💻 CLI tool to run on _other people's_ tools (prefix the command with `rich-click`)
38
+ - 💻 CLI tool to run on _other people's_ Click and Typer CLIs (prefix the command with `rich-click`)
39
39
  - 📦 Export help text as HTML or SVG
40
40
  - 🎁 Group commands and options into named panels
41
41
  - ❌ Well formatted error messages
42
42
  - 💫 Extensive customization
43
- - 🤖 IDE autocomplete of Click decorators for smooth developer experience.
43
+ - 🤖 IDE autocomplete of Click decorators for smooth developer experience
44
44
 
45
45
  ## Installation
46
46
 
@@ -46,6 +46,7 @@ optional-dependencies.dev = [
46
46
  "pytest-cov>=5",
47
47
  "rich-codex>=1.2.11",
48
48
  "ruff>=0.12.4",
49
+ "typer>=0.15",
49
50
  "types-setuptools>=75.8.0.20250110",
50
51
  ]
51
52
  optional-dependencies.docs = [
@@ -60,6 +61,7 @@ optional-dependencies.docs = [
60
61
  "mkdocs-rss-plugin>=1.15",
61
62
  "mkdocstrings[python]>=0.26.1",
62
63
  "rich-codex>=1.2.11",
64
+ "typer>=0.15",
63
65
  ]
64
66
  urls.Documentation = "https://github.com/ewels/rich-click"
65
67
  urls.Homepage = "https://github.com/ewels/rich-click"
@@ -8,7 +8,7 @@ customization required.
8
8
  from __future__ import annotations
9
9
 
10
10
 
11
- __version__ = "1.9.0.dev2"
11
+ __version__ = "1.9.0.dev3"
12
12
 
13
13
  # Import the entire click API here.
14
14
  # We need to manually import these instead of `from click import *` to force
@@ -0,0 +1,250 @@
1
+ """
2
+ Contains types for .pyi files.
3
+ Import from this at your own risk.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import (
9
+ TYPE_CHECKING,
10
+ Any,
11
+ Callable,
12
+ Dict,
13
+ Iterable,
14
+ List,
15
+ Literal,
16
+ Mapping,
17
+ MutableMapping,
18
+ Optional,
19
+ Tuple,
20
+ TypedDict,
21
+ Union,
22
+ )
23
+
24
+ from rich_click.utils import CommandGroupDict, OptionGroupDict
25
+
26
+
27
+ if TYPE_CHECKING:
28
+ import sys
29
+
30
+ from rich.align import AlignMethod
31
+ from rich.box import Box
32
+ from rich.console import Console, JustifyMethod
33
+ from rich.padding import PaddingDimensions
34
+ from rich.style import StyleType
35
+ from rich.text import Text, TextType
36
+
37
+ from rich_click.rich_help_configuration import (
38
+ CommandColumnType,
39
+ CommandHelpSectionType,
40
+ OptionColumnType,
41
+ OptionHelpSectionType,
42
+ RichHelpConfiguration,
43
+ )
44
+
45
+ if sys.version_info < (3, 11):
46
+ from typing_extensions import NotRequired
47
+ else:
48
+ from typing import NotRequired
49
+
50
+
51
+ class RichContextSettingsDict(TypedDict):
52
+ obj: NotRequired["Any | None"]
53
+ auto_envvar_prefix: NotRequired[Optional[str]]
54
+ default_map: NotRequired[MutableMapping[str, Any] | None]
55
+ terminal_width: NotRequired[Optional[int]]
56
+ max_content_width: NotRequired[Optional[int]]
57
+ resilient_parsing: NotRequired[bool]
58
+ allow_extra_args: NotRequired[Optional[bool]]
59
+ allow_interspersed_args: NotRequired[Optional[bool]]
60
+ ignore_unknown_options: NotRequired[Optional[bool]]
61
+ help_option_names: NotRequired[Optional[List[str]]]
62
+ token_normalize_func: NotRequired[Callable[[str], str] | None]
63
+ color: NotRequired[Optional[bool]]
64
+ show_default: NotRequired[Optional[bool]]
65
+ rich_console: NotRequired[Optional["Console"]]
66
+ rich_help_config: NotRequired[Optional[Union[Mapping[str, Any], RichHelpConfiguration]]]
67
+ export_console_as: NotRequired[Optional[Literal["html", "svg", "text"]]]
68
+ errors_in_output_format: NotRequired[Optional[bool]]
69
+ help_to_stderr: NotRequired[Optional[bool]]
70
+
71
+
72
+ class TableKwargs(TypedDict):
73
+ title: NotRequired[Optional["TextType"]]
74
+ caption: NotRequired[Optional["TextType"]]
75
+ width: NotRequired[Optional[int]]
76
+ min_width: NotRequired[Optional[int]]
77
+ box: NotRequired[Optional[Union[str, "Box"]]]
78
+ safe_box: NotRequired[Optional[bool]]
79
+ padding: NotRequired["PaddingDimensions"]
80
+ collapse_padding: NotRequired[bool]
81
+ pad_edge: NotRequired[bool]
82
+ expand: NotRequired[bool]
83
+ show_header: NotRequired[bool]
84
+ show_footer: NotRequired[bool]
85
+ show_edge: NotRequired[bool]
86
+ show_lines: NotRequired[bool]
87
+ leading: NotRequired[int]
88
+ style: NotRequired["StyleType"]
89
+ row_styles: NotRequired[Optional[Iterable["StyleType"]]]
90
+ header_style: NotRequired[Optional["StyleType"]]
91
+ footer_style: NotRequired[Optional["StyleType"]]
92
+ border_style: NotRequired[Optional["StyleType"]]
93
+ title_style: NotRequired[Optional["StyleType"]]
94
+ caption_style: NotRequired[Optional["StyleType"]]
95
+ title_justify: NotRequired["JustifyMethod"]
96
+ caption_justify: NotRequired["JustifyMethod"]
97
+ highlight: NotRequired[bool]
98
+
99
+
100
+ class PanelKwargs(TypedDict):
101
+ box: NotRequired[Union[str, "Box"]]
102
+ title: NotRequired[Optional["TextType"]]
103
+ title_align: NotRequired["AlignMethod"]
104
+ subtitle: NotRequired[Optional["TextType"]]
105
+ subtitle_align: NotRequired["AlignMethod"]
106
+ safe_box: NotRequired[Optional[bool]]
107
+ expand: NotRequired[bool]
108
+ style: NotRequired["StyleType"]
109
+ border_style: NotRequired["StyleType"]
110
+ width: NotRequired[Optional[int]]
111
+ height: NotRequired[Optional[int]]
112
+ padding: NotRequired["PaddingDimensions"]
113
+ highlight: NotRequired[bool]
114
+
115
+
116
+ class RichHelpConfigurationDict(TypedDict):
117
+ """Typed dict for rich_config() kwargs."""
118
+
119
+ theme: NotRequired[Optional[str]]
120
+ enable_theme_env_var: NotRequired[bool]
121
+ style_option: NotRequired["StyleType"]
122
+ style_option_negative: NotRequired[Optional["StyleType"]]
123
+ style_argument: NotRequired["StyleType"]
124
+ style_command: NotRequired["StyleType"]
125
+ style_command_aliases: NotRequired["StyleType"]
126
+ style_switch: NotRequired["StyleType"]
127
+ style_switch_negative: NotRequired[Optional["StyleType"]]
128
+ style_metavar: NotRequired["StyleType"]
129
+ style_metavar_append: NotRequired["StyleType"]
130
+ style_metavar_separator: NotRequired["StyleType"]
131
+ style_range_append: NotRequired["StyleType"]
132
+ style_header_text: NotRequired["StyleType"]
133
+ style_epilog_text: NotRequired["StyleType"]
134
+ style_footer_text: NotRequired["StyleType"]
135
+ style_usage: NotRequired["StyleType"]
136
+ style_usage_command: NotRequired["StyleType"]
137
+ style_usage_separator: NotRequired["StyleType"]
138
+ style_deprecated: NotRequired["StyleType"]
139
+ style_helptext_first_line: NotRequired["StyleType"]
140
+ style_helptext: NotRequired["StyleType"]
141
+ style_option_help: NotRequired["StyleType"]
142
+ style_command_help: NotRequired["StyleType"]
143
+ style_option_default: NotRequired["StyleType"]
144
+ style_option_envvar: NotRequired["StyleType"]
145
+ style_required_short: NotRequired["StyleType"]
146
+ style_required_long: NotRequired["StyleType"]
147
+ style_options_panel_border: NotRequired["StyleType"]
148
+ style_options_panel_box: NotRequired[Optional[Union[str, "Box"]]]
149
+ style_options_panel_help_style: NotRequired["StyleType"]
150
+ style_options_panel_title_style: NotRequired["StyleType"]
151
+ style_options_panel_padding: NotRequired["PaddingDimensions"]
152
+ style_options_panel_style: NotRequired["StyleType"]
153
+ align_options_panel: NotRequired["AlignMethod"]
154
+ style_options_table_show_lines: NotRequired[bool]
155
+ style_options_table_leading: NotRequired[int]
156
+ style_options_table_pad_edge: NotRequired[bool]
157
+ style_options_table_padding: NotRequired["PaddingDimensions"]
158
+ style_options_table_expand: NotRequired[bool]
159
+ style_options_table_box: NotRequired[Optional[Union[str, "Box"]]]
160
+ style_options_table_row_styles: NotRequired[Optional[List["StyleType"]]]
161
+ style_options_table_border_style: NotRequired[Optional["StyleType"]]
162
+ style_commands_panel_border: NotRequired["StyleType"]
163
+ panel_inline_help_in_title: NotRequired[bool]
164
+ panel_inline_help_delimiter: NotRequired[str]
165
+ style_commands_panel_box: NotRequired[Optional[Union[str, "Box"]]]
166
+ style_commands_panel_help_style: NotRequired["StyleType"]
167
+ style_commands_panel_title_style: NotRequired["StyleType"]
168
+ style_commands_panel_padding: NotRequired["PaddingDimensions"]
169
+ style_commands_panel_style: NotRequired["StyleType"]
170
+ align_commands_panel: NotRequired[AlignMethod]
171
+ style_commands_table_show_lines: NotRequired[bool]
172
+ style_commands_table_leading: NotRequired[int]
173
+ style_commands_table_pad_edge: NotRequired[bool]
174
+ style_commands_table_padding: NotRequired["PaddingDimensions"]
175
+ style_commands_table_expand: NotRequired[bool]
176
+ style_commands_table_box: NotRequired[Optional[Union[str, "Box"]]]
177
+ style_commands_table_row_styles: NotRequired[Optional[List["StyleType"]]]
178
+ style_commands_table_border_style: NotRequired[Optional["StyleType"]]
179
+ style_commands_table_column_width_ratio: NotRequired[Optional[Union[Tuple[None, None], Tuple[int, int]]]]
180
+ style_errors_panel_border: NotRequired["StyleType"]
181
+ style_errors_panel_box: NotRequired[Optional[Union[str, "Box"]]]
182
+ align_errors_panel: NotRequired[AlignMethod]
183
+ style_errors_suggestion: NotRequired["StyleType"]
184
+ style_errors_suggestion_command: NotRequired["StyleType"]
185
+ style_aborted: NotRequired["StyleType"]
186
+ style_padding_usage: NotRequired["StyleType"]
187
+ style_padding_helptext: NotRequired["StyleType"]
188
+ style_padding_epilog: NotRequired["StyleType"]
189
+
190
+ panel_title_padding: NotRequired[int]
191
+ width: NotRequired[Optional[int]]
192
+ max_width: NotRequired[Optional[int]]
193
+ color_system: NotRequired[Optional[Literal["auto", "standard", "256", "truecolor", "windows"]]]
194
+ force_terminal: NotRequired[Optional[bool]]
195
+ options_table_column_types: NotRequired[List["OptionColumnType"]]
196
+ commands_table_column_types: NotRequired[List["CommandColumnType"]]
197
+ options_table_help_sections: NotRequired[List["OptionHelpSectionType"]]
198
+ commands_table_help_sections: NotRequired[List["CommandHelpSectionType"]]
199
+
200
+ header_text: NotRequired[Optional[Union[str, "Text"]]]
201
+ footer_text: NotRequired[Optional[Union[str, "Text"]]]
202
+ panel_title_string: NotRequired[str]
203
+ deprecated_string: NotRequired[str]
204
+ deprecated_with_reason_string: NotRequired[str]
205
+ default_string: NotRequired[str]
206
+ envvar_string: NotRequired[str]
207
+ required_short_string: NotRequired[str]
208
+ required_long_string: NotRequired[str]
209
+ range_string: NotRequired[str]
210
+ append_metavars_help_string: NotRequired[str]
211
+ append_range_help_string: NotRequired[str]
212
+ arguments_panel_title: NotRequired[str]
213
+ options_panel_title: NotRequired[str]
214
+ commands_panel_title: NotRequired[str]
215
+ errors_panel_title: NotRequired[str]
216
+ delimiter_comma: NotRequired[str]
217
+ delimiter_slash: NotRequired[str]
218
+ errors_suggestion: NotRequired[Optional[Union[str, Text]]]
219
+ errors_epilogue: NotRequired[Optional[Union[str, Text]]]
220
+ aborted_text: NotRequired[str]
221
+ padding_header_text: NotRequired["PaddingDimensions"]
222
+ padding_helptext: NotRequired["PaddingDimensions"]
223
+ padding_helptext_deprecated: NotRequired["PaddingDimensions"]
224
+ padding_helptext_first_line: NotRequired["PaddingDimensions"]
225
+ padding_usage: NotRequired["PaddingDimensions"]
226
+ padding_epilog: NotRequired["PaddingDimensions"]
227
+ padding_footer_text: NotRequired["PaddingDimensions"]
228
+ padding_errors_panel: NotRequired["PaddingDimensions"]
229
+ padding_errors_suggestion: NotRequired["PaddingDimensions"]
230
+ padding_errors_epilogue: NotRequired["PaddingDimensions"]
231
+
232
+ # Behaviours
233
+ show_arguments: NotRequired[Optional[bool]]
234
+ show_metavars_column: NotRequired[Optional[bool]]
235
+ commands_before_options: NotRequired[bool]
236
+ append_metavars_help: NotRequired[Optional[bool]]
237
+ group_arguments_options: NotRequired[bool]
238
+ option_envvar_first: NotRequired[Optional[bool]]
239
+ text_markup: NotRequired[Literal["ansi", "rich", "markdown", None]]
240
+ text_kwargs: NotRequired[Optional[Dict[str, Any]]]
241
+ text_emojis: NotRequired[bool]
242
+ text_paragraph_linebreaks: NotRequired[Optional[Literal["\n", "\n\n"]]]
243
+ use_markdown: NotRequired[Optional[bool]]
244
+ use_markdown_emoji: NotRequired[Optional[bool]]
245
+ use_rich_markup: NotRequired[Optional[bool]]
246
+ command_groups: NotRequired[Dict[str, List[CommandGroupDict]]]
247
+ option_groups: NotRequired[Dict[str, List[OptionGroupDict]]]
248
+ use_click_short_help: NotRequired[bool]
249
+ highlighter_patterns: NotRequired[List[str]]
250
+ legacy_windows: NotRequired[Optional[bool]]
@@ -4,15 +4,18 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import sys
7
+ import warnings
8
+ from contextlib import contextmanager
7
9
  from functools import wraps
8
10
  from gettext import gettext
9
11
  from importlib import (
10
12
  import_module,
11
13
  metadata, # type: ignore[import,unused-ignore]
12
14
  )
13
- from typing import Any, Dict, List, Literal, Optional, Tuple
15
+ from typing import Any, Dict, Generator, List, Literal, Optional, Tuple
14
16
 
15
17
  import click
18
+ from click.core import ParameterSource
16
19
 
17
20
  from rich_click.decorators import argument as _rich_argument
18
21
  from rich_click.decorators import command as _rich_command
@@ -20,10 +23,18 @@ from rich_click.decorators import option as _rich_option
20
23
  from rich_click.decorators import option_panel, pass_context
21
24
  from rich_click.decorators import version_option as _rich_version_option
22
25
  from rich_click.patch import patch as _patch
26
+ from rich_click.rich_click_theme import COLORS, FORMATS, RichClickThemeNotFound, get_theme
23
27
  from rich_click.rich_context import RichContext
24
28
  from rich_click.rich_help_configuration import RichHelpConfiguration
25
29
 
26
30
 
31
+ DISABLE_WARNINGS_NOTE = (
32
+ "[red]You can pass [b]--suppress-warnings[/b] to the rich-click CLI"
33
+ " or set the [b]RICH_CLICK_CLI_SUPPRESS_WARNINGS[/b] environment variable"
34
+ " to disable this message.[/red]"
35
+ )
36
+
37
+
27
38
  def entry_points(*, group: str) -> "metadata.EntryPoints": # type: ignore[name-defined]
28
39
  """entry_points function that is compatible with Python 3.7+."""
29
40
  if sys.version_info >= (3, 10):
@@ -118,10 +129,9 @@ def _get_module_path_and_function_name(script: str, suppress_warnings: bool) ->
118
129
  " and you have set a custom PYTHONPATH."
119
130
  f"\n\nThe selected script is '{module_path}:{function_name}', which is being executed now."
120
131
  "\n\nIt is safer and recommended that you specify the MODULE:CLICK_COMMAND"
121
- f" ('{module_path}:{function_name}') instead of the script ('{script}'), like this:"
132
+ f" ('{module_path}:{function_name}') instead of the script ('{script}'), like this:[/]"
122
133
  f"\n\n>>> {' '.join(_args)}"
123
- "\n\nAlternatively, you can pass --suppress-warnings to the rich-click CLI,"
124
- " which will disable this message.[/]",
134
+ f"\n\n{DISABLE_WARNINGS_NOTE}",
125
135
  )
126
136
 
127
137
  if ":" in script and not module_path:
@@ -145,8 +155,6 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
145
155
  from rich.table import Table
146
156
  from rich.text import Text
147
157
 
148
- from rich_click.rich_theme import THEMES
149
-
150
158
  formatter = ctx.make_formatter()
151
159
 
152
160
  console = Console(
@@ -171,21 +179,21 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
171
179
  _theme = None
172
180
  else:
173
181
  _theme = selected
174
- if "-" in _theme:
175
- c, f, *_ = _theme.split("-")
176
- from rich_click.rich_theme import THEMES
177
-
178
- if _theme in THEMES:
179
- found = True
180
- elif _theme is not None:
181
- from rich_click.rich_theme import COLORS, FORMATS
182
-
183
- if _theme in FORMATS:
184
- f = _theme
185
- found = True
186
- elif _theme in COLORS:
187
- c = _theme
182
+ if _theme:
183
+ try:
184
+ get_theme(_theme)
185
+ except RichClickThemeNotFound:
186
+ found = False
187
+ else:
188
188
  found = True
189
+ if "-" in _theme:
190
+ c, f, *_ = _theme.split("-")
191
+ elif _theme in FORMATS:
192
+ f = _theme
193
+ found = True
194
+ elif _theme in COLORS:
195
+ c = _theme
196
+ found = True
189
197
 
190
198
  if selected is None:
191
199
  selected_text = "[red bold]RICH_CLICK_THEME[/] is not set"
@@ -194,8 +202,8 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
194
202
  else:
195
203
  selected_text = (
196
204
  f"[blue bold]RICH_CLICK_THEME[/blue bold]=[blue]{selected!r}[/blue]"
197
- f"\n\n[b]Colors:[/b] {c}"
198
- f"\n[b]Format:[/b] {f}"
205
+ f"\n\n[b]Colors:[/b] {c or '[dim](null)[/]'}"
206
+ f"\n[b]Format:[/b] {f or '[dim](null)[/]'}"
199
207
  )
200
208
 
201
209
  colors = Table(
@@ -211,83 +219,16 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
211
219
  )
212
220
  colors.columns[0].max_width = 1
213
221
  colors.columns[1].style = "bold"
214
- colors.add_row(
215
- "✓" if c == "default" or c is None else "",
216
- "default",
217
- "[cyan]▓▓[/] [yellow]▓▓[/] [green]▓▓[/]",
218
- "[b](Default)[/b] Original rich-click colors",
219
- )
220
- colors.add_row(
221
- "✓" if c == "solarized" else "",
222
- "solarized",
223
- "[bright_green]▓▓[/] [bright_red]▓▓[/] [bright_cyan]▓▓[/]",
224
- "Bright, colorful, vibrant accents",
225
- )
226
- colors.add_row(
227
- "✓" if c == "nord" else "",
228
- "nord",
229
- "[blue]▓▓[/] [bright_blue]▓▓[/] [cyan]▓▓[/]",
230
- "Many shades of cool colors",
231
- )
232
- colors.add_row(
233
- "✓" if c == "star" else "",
234
- "star",
235
- "[yellow]▓▓[/] [blue]▓▓[/] [default]▓▓[/]",
236
- "Litestar theme; royal feel",
237
- )
238
- colors.add_row(
239
- "✓" if c == "quartz" else "",
240
- "quartz",
241
- "[magenta]▓▓[/] [dim magenta]▓▓[/] [blue]▓▓[/]",
242
- "Dark and radiant",
243
- )
244
- colors.add_row(
245
- "✓" if c == "quartz2" else "",
246
- "quartz2",
247
- "[magenta]▓▓[/] [blue]▓▓[/] [yellow]▓▓[/]",
248
- "Remix of 'quartz' with accents",
249
- )
250
- colors.add_row(
251
- "✓" if c == "cargo" else "",
252
- "cargo",
253
- "[green]▓▓[/] [cyan]▓▓[/] [default]▓▓[/]",
254
- "Cargo CLI theme; legible and bold",
255
- )
256
- colors.add_row(
257
- "✓" if c == "forest" else "",
258
- "forest",
259
- "[green]▓▓[/] [yellow]▓▓[/] [cyan]▓▓[/]",
260
- "Earthy tones with analogous colors",
261
- )
262
- colors.add_row(
263
- "✓" if c == "dracula" else "",
264
- "dracula",
265
- "[magenta]▓▓[/] [red]▓▓[/] [yellow]▓▓[/]",
266
- "Vibrant high-contract dark theme",
267
- )
268
- colors.add_row(
269
- "✓" if c == "dracula2" else "",
270
- "dracula2",
271
- "[magenta on black]▓▓[/] [red on black]▓▓[/] [black]▓▓[/]",
272
- "Dracula theme with forced black background",
273
- )
274
- for _c in ["red", "green", "yellow", "blue", "magenta", "cyan"]:
222
+ for r in COLORS.values():
223
+ if r.hidden:
224
+ continue
225
+ selected_row = c == r.name or (c is None and r.name == "default")
275
226
  colors.add_row(
276
- "✓" if c == f"{_c}1" else "",
277
- f"{_c}1",
278
- f"[default]▓▓[/] [{_c}]▓▓[/] [dim {_c}]▓▓[/]",
279
- f"Simple theme with {_c} accents on section headers",
227
+ "*" if selected_row else "",
228
+ r.name,
229
+ " ".join([f"[{_}]▓▓[/]" for _ in r.primary_colors[:3]]),
230
+ r.description,
280
231
  )
281
- colors.add_row(
282
- "✓" if c == f"{_c}2" else "",
283
- f"{_c}2",
284
- f"[{_c}]▓▓[/] [default]▓▓[/] [dim]▓▓[/]",
285
- f"Simple theme with {_c} accents on object names",
286
- )
287
- colors.add_row(
288
- "✓" if c == "mono" else "", "mono", "[default]▓▓[/] [dim]▓▓[/]", "Monochromatic theme with no colors"
289
- )
290
- colors.add_row("✓" if c == "plain" else "", "plain", "[default]▓▓[/]", "No style at all")
291
232
 
292
233
  formats = Table(
293
234
  "",
@@ -300,13 +241,9 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
300
241
  expand=True,
301
242
  )
302
243
  formats.columns[1].style = "bold"
303
- formats.add_row(
304
- "✓" if f == "box" or f is None else "", "box", "[b](Default)[/b] Original rich-click format with boxes"
305
- )
306
- formats.add_row("✓" if f == "slim" else "", "slim", "Simple, classic, no-fuss CLI format")
307
- formats.add_row("✓" if f == "modern" else "", "modern", "Beautiful modern look")
308
- formats.add_row("✓" if f == "robo" else "", "robo", "Spacious with sharp corners")
309
- formats.add_row("✓" if f == "nu" else "", "nu", "Great balance of compactness, legibility, and style")
244
+ for r in FORMATS.values():
245
+ selected_row = f == r.name or (f is None and r.name == "box")
246
+ formats.add_row("*" if selected_row else "", r.name, r.description)
310
247
 
311
248
  how_to_use = Padding(
312
249
  Text("\n\n", overflow="fold").join(
@@ -398,6 +335,8 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
398
335
  "--suppress-warnings/--do-not-suppress-warnings",
399
336
  is_flag=True,
400
337
  default=False,
338
+ envvar="RICH_CLICK_CLI_SUPPRESS_WARNINGS",
339
+ show_envvar=True,
401
340
  panel="Advanced Options",
402
341
  help="Suppress warnings when there are conflicting entry_points." " This situation is extremely rare.",
403
342
  )
@@ -406,7 +345,8 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
406
345
  is_flag=True,
407
346
  default=True,
408
347
  panel="Advanced Options",
409
- help="If set, patch [code]rich_click.Command[/code], not just [code]click.Command[/code].",
348
+ help="If set, patch [reverse][option][b]rich_click.Command[/][/][/],"
349
+ " not just [reverse][option][b]click.Command[/][/][/].",
410
350
  )
411
351
  @_rich_option(
412
352
  "--themes",
@@ -467,14 +407,23 @@ https://ewels.github.io/rich-click/latest/documentation/rich_click_cli/[/]
467
407
 
468
408
  >>> [command]rich-click[/] [argument]my_package[/] [argument]cmd[/] [option]--foo[/] 3
469
409
  """ # noqa: D401
470
- if rich_config:
471
- if theme:
472
- rich_config.setdefault("theme", theme)
473
- cfg = RichHelpConfiguration.load_from_globals(**rich_config)
474
- elif theme:
475
- cfg = RichHelpConfiguration.load_from_globals(theme=theme)
476
- else:
477
- cfg = RichHelpConfiguration.load_from_globals()
410
+ try:
411
+ if rich_config:
412
+ if theme:
413
+ rich_config.setdefault("theme", theme)
414
+ import rich_click.rich_click as rc
415
+
416
+ rc._THEME_FROM_CLI = theme
417
+ cfg = RichHelpConfiguration.load_from_globals(**rich_config)
418
+ elif theme:
419
+ import rich_click.rich_click as rc
420
+
421
+ rc._THEME_FROM_CLI = theme
422
+ cfg = RichHelpConfiguration.load_from_globals(theme=theme)
423
+ else:
424
+ cfg = RichHelpConfiguration.load_from_globals()
425
+ except RichClickThemeNotFound as e:
426
+ raise click.ClickException(e.args[0] if e.args else "Theme not found")
478
427
 
479
428
  if (show_help or not script_and_args) and not ctx.resilient_parsing:
480
429
  cfg.use_markdown = False
@@ -488,21 +437,27 @@ https://ewels.github.io/rich-click/latest/documentation/rich_click_cli/[/]
488
437
  ctx.exit(2)
489
438
  ctx.exit(0)
490
439
 
491
- if theme:
492
- import rich_click.rich_click as rc
493
-
494
- rc._THEME_FROM_CLI = theme
495
- if rich_config:
496
- if theme:
497
- rich_config.setdefault("theme", theme)
498
- cfg = RichHelpConfiguration.load_from_globals(**rich_config)
499
- elif theme:
500
- cfg = RichHelpConfiguration.load_from_globals(theme=theme)
501
- else:
502
- cfg = RichHelpConfiguration.load_from_globals()
440
+ @contextmanager
441
+ def patch_ctx() -> Generator[None, None, None]:
442
+ if ctx.get_parameter_source("typer") != ParameterSource.ENVIRONMENT:
443
+ try:
444
+ with warnings.catch_warnings():
445
+ warnings.filterwarnings("error", category=ImportWarning)
446
+ yield
447
+ except ImportWarning as e:
448
+ if not suppress_warnings and any("Typer" in a for a in e.args):
449
+ import rich
450
+
451
+ rich.print(
452
+ f"[red]WARNING: Attempted to patch Typer, but Typer is not installed.[/red]"
453
+ f"\n\n{DISABLE_WARNINGS_NOTE}"
454
+ )
455
+ else:
456
+ yield
503
457
 
504
458
  # patch click before importing the program function
505
- _patch(rich_config=cfg, patch_rich_click=patch_rich_click)
459
+ with patch_ctx():
460
+ _patch(rich_config=cfg, patch_rich_click=patch_rich_click)
506
461
 
507
462
  script, *args = script_and_args
508
463
 
@@ -526,8 +481,8 @@ https://ewels.github.io/rich-click/latest/documentation/rich_click_cli/[/]
526
481
  # and PYTHONPATH does not include ".".
527
482
  sys.path.append(os.path.abspath("."))
528
483
  module = import_module(module_path)
529
- except ModuleNotFoundError:
530
- raise click.ClickException(f"Module '{module_path}' not found.")
484
+ except ModuleNotFoundError as e:
485
+ raise click.ClickException(e.args[0] if e.args else "Unknown error")
531
486
  try:
532
487
  f = getattr(module, function_name)
533
488
  except AttributeError:
@@ -541,8 +496,8 @@ https://ewels.github.io/rich-click/latest/documentation/rich_click_cli/[/]
541
496
 
542
497
  try:
543
498
  runpy.run_path(module_path, run_name="__main__")
544
- except ImportError:
545
- raise click.ClickException(f"File '{module_path}' not found.")
499
+ except ImportError as e:
500
+ raise click.ClickException(e.args[0] if e.args else "Unknown error")
546
501
 
547
502
  else:
548
503
 
@@ -557,8 +512,8 @@ https://ewels.github.io/rich-click/latest/documentation/rich_click_cli/[/]
557
512
  sys.path.append(os.path.abspath("."))
558
513
  try:
559
514
  runpy.run_module(module_path, run_name="__main__")
560
- except ImportError:
561
- raise click.ClickException(f"Module '{module_path}' not found.")
515
+ except ImportError as e:
516
+ raise click.ClickException(e.args[0] if e.args else "Unknown error")
562
517
 
563
518
  prog = module_path.split(".", 1)[0]
564
519
  sys.argv = [prog, *args]