rich-click 1.9.0.dev6__tar.gz → 1.9.2.dev0__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.dev6/src/rich_click.egg-info → rich_click-1.9.2.dev0}/PKG-INFO +3 -1
  2. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/pyproject.toml +2 -0
  3. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/__init__.py +1 -1
  4. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/_internal_types.py +5 -1
  5. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/cli.py +2 -4
  6. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_click.py +6 -1
  7. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_click_theme.py +8 -8
  8. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_command.py +11 -4
  9. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_context.py +1 -1
  10. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_help_configuration.py +5 -2
  11. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_help_formatter.py +8 -2
  12. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_help_rendering.py +24 -3
  13. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_panel.py +59 -55
  14. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/utils.py +22 -5
  15. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0/src/rich_click.egg-info}/PKG-INFO +3 -1
  16. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click.egg-info/requires.txt +4 -0
  17. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/LICENSE +0 -0
  18. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/README.md +0 -0
  19. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/setup.cfg +0 -0
  20. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/__main__.py +0 -0
  21. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/_click_types_cache.py +0 -0
  22. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/_compat_click.py +0 -0
  23. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/decorators.py +0 -0
  24. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/decorators.pyi +0 -0
  25. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/patch.py +0 -0
  26. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/py.typed +0 -0
  27. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_box.py +0 -0
  28. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_command.pyi +0 -0
  29. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_group.py +0 -0
  30. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click/rich_parameter.py +0 -0
  31. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click.egg-info/SOURCES.txt +0 -0
  32. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click.egg-info/dependency_links.txt +0 -0
  33. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click.egg-info/entry_points.txt +0 -0
  34. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/src/rich_click.egg-info/top_level.txt +0 -0
  35. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_config.py +0 -0
  36. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_deprecations.py +0 -0
  37. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_exit_code.py +0 -0
  38. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_misc.py +0 -0
  39. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_no_rich_imports.py +0 -0
  40. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_rich_click_cli.py +0 -0
  41. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_themes.py +0 -0
  42. {rich_click-1.9.0.dev6 → rich_click-1.9.2.dev0}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rich-click
3
- Version: 1.9.0.dev6
3
+ Version: 1.9.2.dev0
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>
@@ -44,6 +44,7 @@ Requires-Python: >=3.8
44
44
  Description-Content-Type: text/markdown
45
45
  License-File: LICENSE
46
46
  Requires-Dist: click>=8
47
+ Requires-Dist: colorama; platform_system == "Windows"
47
48
  Requires-Dist: rich>=12
48
49
  Requires-Dist: typing-extensions>=4; python_version < "3.11"
49
50
  Provides-Extra: dev
@@ -68,6 +69,7 @@ Requires-Dist: mkdocs-glightbox>=0.4; extra == "docs"
68
69
  Requires-Dist: mkdocs-include-markdown-plugin>=7.1.7; python_version >= "3.9" and extra == "docs"
69
70
  Requires-Dist: mkdocs-material[imaging]~=9.5.18; extra == "docs"
70
71
  Requires-Dist: mkdocs-material-extensions>=1.3.1; extra == "docs"
72
+ Requires-Dist: mkdocs-redirects>=1.2.2; extra == "docs"
71
73
  Requires-Dist: mkdocs-rss-plugin>=1.15; extra == "docs"
72
74
  Requires-Dist: mkdocstrings[python]>=0.26.1; extra == "docs"
73
75
  Requires-Dist: rich-codex>=1.2.11; extra == "docs"
@@ -32,6 +32,7 @@ dynamic = [
32
32
  ]
33
33
  dependencies = [
34
34
  "click>=8",
35
+ "colorama; platform_system=='Windows'",
35
36
  "rich>=12",
36
37
  "typing-extensions>=4; python_version<'3.11'",
37
38
  ]
@@ -58,6 +59,7 @@ optional-dependencies.docs = [
58
59
  "mkdocs-include-markdown-plugin>=7.1.7; python_version>='3.9'",
59
60
  "mkdocs-material[imaging]~=9.5.18",
60
61
  "mkdocs-material-extensions>=1.3.1",
62
+ "mkdocs-redirects>=1.2.2",
61
63
  "mkdocs-rss-plugin>=1.15",
62
64
  "mkdocstrings[python]>=0.26.1",
63
65
  "rich-codex>=1.2.11",
@@ -8,7 +8,7 @@ customization required.
8
8
  from __future__ import annotations
9
9
 
10
10
 
11
- __version__ = "1.9.0.dev6"
11
+ __version__ = "1.9.2.dev0"
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
@@ -34,6 +34,7 @@ if TYPE_CHECKING:
34
34
  from rich.style import StyleType
35
35
  from rich.text import Text, TextType
36
36
 
37
+ from rich_click.rich_click_theme import RichClickTheme
37
38
  from rich_click.rich_help_configuration import (
38
39
  CommandColumnType,
39
40
  CommandHelpSectionType,
@@ -116,7 +117,7 @@ class PanelKwargs(TypedDict):
116
117
  class RichHelpConfigurationDict(TypedDict):
117
118
  """Typed dict for rich_config() kwargs."""
118
119
 
119
- theme: NotRequired[Optional[str]]
120
+ theme: NotRequired[Optional[Union[str, "RichClickTheme"]]]
120
121
  enable_theme_env_var: NotRequired[bool]
121
122
  style_option: NotRequired["StyleType"]
122
123
  style_option_negative: NotRequired[Optional["StyleType"]]
@@ -138,6 +139,7 @@ class RichHelpConfigurationDict(TypedDict):
138
139
  style_deprecated: NotRequired["StyleType"]
139
140
  style_helptext_first_line: NotRequired["StyleType"]
140
141
  style_helptext: NotRequired["StyleType"]
142
+ style_helptext_aliases: NotRequired[Optional["StyleType"]]
141
143
  style_option_help: NotRequired["StyleType"]
142
144
  style_command_help: NotRequired["StyleType"]
143
145
  style_option_default: NotRequired["StyleType"]
@@ -210,6 +212,7 @@ class RichHelpConfigurationDict(TypedDict):
210
212
  range_string: NotRequired[str]
211
213
  append_metavars_help_string: NotRequired[str]
212
214
  append_range_help_string: NotRequired[str]
215
+ helptext_aliases_string: NotRequired[str]
213
216
  arguments_panel_title: NotRequired[str]
214
217
  options_panel_title: NotRequired[str]
215
218
  commands_panel_title: NotRequired[str]
@@ -247,5 +250,6 @@ class RichHelpConfigurationDict(TypedDict):
247
250
  command_groups: NotRequired[Dict[str, List[CommandGroupDict]]]
248
251
  option_groups: NotRequired[Dict[str, List[OptionGroupDict]]]
249
252
  use_click_short_help: NotRequired[bool]
253
+ helptext_show_aliases: NotRequired[bool]
250
254
  highlighter_patterns: NotRequired[List[str]]
251
255
  legacy_windows: NotRequired[Optional[bool]]
@@ -60,7 +60,6 @@ def patch(*args: Any, **kwargs: Any) -> None: # noqa: D103
60
60
 
61
61
 
62
62
  class _RichHelpConfigurationParamType(click.ParamType):
63
-
64
63
  name = "JSON"
65
64
 
66
65
  def __repr__(self) -> str:
@@ -72,7 +71,6 @@ class _RichHelpConfigurationParamType(click.ParamType):
72
71
  param: Optional[click.Parameter],
73
72
  ctx: Optional[click.Context],
74
73
  ) -> Optional[Dict[str, Any]]:
75
-
76
74
  if value is None:
77
75
  return value
78
76
  else:
@@ -276,7 +274,7 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
276
274
  Renderables(
277
275
  [
278
276
  Panel(how_to_use, title="How to use", width=100),
279
- Panel(Padding(selected_text, pad=1), title="RICH_CLICK_THEME=", width=100),
277
+ Panel(Padding(selected_text, pad=1), title="Local user setting", width=100),
280
278
  Panel(colors, title="Color Palettes"),
281
279
  Panel(formats, title="Formats"),
282
280
  ]
@@ -338,7 +336,7 @@ def list_themes(ctx: RichContext, param: click.Parameter, value: bool) -> None:
338
336
  envvar="RICH_CLICK_CLI_SUPPRESS_WARNINGS",
339
337
  show_envvar=True,
340
338
  panel="Advanced Options",
341
- help="Suppress warnings when there are conflicting entry_points." " This situation is extremely rare.",
339
+ help="Suppress warnings when there are conflicting entry_points. This situation is extremely rare.",
342
340
  )
343
341
  @_rich_option(
344
342
  "--patch-rich-click/--no-patch-rich-click",
@@ -21,10 +21,12 @@ if TYPE_CHECKING: # pragma: no cover
21
21
  from rich.style import StyleType
22
22
  from rich.text import Text
23
23
 
24
+ from rich_click.rich_click_theme import RichClickTheme
25
+
24
26
  #!STARTCONFIG
25
27
 
26
28
  # Default styles
27
- THEME: Optional[str] = None
29
+ THEME: Optional[Union[str, RichClickTheme]] = None
28
30
  ENABLE_THEME_ENV_VAR: bool = True
29
31
 
30
32
  STYLE_OPTION: "StyleType" = FROM_THEME
@@ -44,6 +46,7 @@ STYLE_USAGE_SEPARATOR: "StyleType" = FROM_THEME
44
46
  STYLE_DEPRECATED: "StyleType" = FROM_THEME
45
47
  STYLE_HELPTEXT_FIRST_LINE: "StyleType" = FROM_THEME
46
48
  STYLE_HELPTEXT: "StyleType" = FROM_THEME
49
+ STYLE_HELPTEXT_ALIASES: Optional["StyleType"] = None
47
50
  STYLE_OPTION_HELP: "StyleType" = FROM_THEME
48
51
  STYLE_COMMAND_HELP: "StyleType" = FROM_THEME
49
52
  STYLE_OPTION_DEFAULT: "StyleType" = FROM_THEME
@@ -124,6 +127,7 @@ REQUIRED_LONG_STRING: str = FROM_THEME
124
127
  RANGE_STRING: str = FROM_THEME
125
128
  APPEND_METAVARS_HELP_STRING: str = FROM_THEME
126
129
  APPEND_RANGE_HELP_STRING: str = FROM_THEME
130
+ HELPTEXT_ALIASES_STRING: str = "Aliases: {}"
127
131
  ARGUMENTS_PANEL_TITLE: str = "Arguments"
128
132
  OPTIONS_PANEL_TITLE: str = "Options"
129
133
  COMMANDS_PANEL_TITLE: str = "Commands"
@@ -165,6 +169,7 @@ COMMAND_GROUPS: Dict[str, List[CommandGroupDict]] = {}
165
169
  # Define sorted groups of panels to display options and arguments
166
170
  OPTION_GROUPS: Dict[str, List[OptionGroupDict]] = {}
167
171
  USE_CLICK_SHORT_HELP: bool = False # Use click's default function to truncate help text
172
+ HELPTEXT_SHOW_ALIASES: bool = True
168
173
 
169
174
  #!ENDCONFIG
170
175
 
@@ -1010,11 +1010,11 @@ FORMATS: Dict[str, RichClickTheme] = {
1010
1010
  "style_options_table_expand": True,
1011
1011
  "style_commands_table_expand": True,
1012
1012
  "panel_title_string": "{}",
1013
- "style_options_panel_padding": (0, 1),
1014
- "style_commands_panel_padding": (0, 1),
1015
- "padding_header_text": (1, 3, 0, 3),
1016
- "padding_helptext": (0, 3, 1, 3),
1017
- "padding_usage": (1, 3, 1, 3),
1013
+ "style_options_panel_padding": 0,
1014
+ "style_commands_panel_padding": 0,
1015
+ "padding_header_text": (1, 2, 0, 3),
1016
+ "padding_helptext": (0, 2, 1, 2),
1017
+ "padding_usage": (1, 2, 1, 2),
1018
1018
  "options_table_column_types": ["opt_short", "opt_long", "metavar", "help"],
1019
1019
  "commands_table_column_types": ["name", "aliases", "help"],
1020
1020
  "options_table_help_sections": ["required", "help", "envvar", "default", "deprecated"],
@@ -1027,14 +1027,14 @@ FORMATS: Dict[str, RichClickTheme] = {
1027
1027
  "panel_inline_help_in_title": True,
1028
1028
  "delimiter_comma": ", ",
1029
1029
  "delimiter_slash": " / ",
1030
- "panel_title_padding": 1,
1030
+ "panel_title_padding": 0,
1031
1031
  "required_short_string": "*",
1032
1032
  "panel_inline_help_delimiter": " - ",
1033
1033
  "required_long_string": "\\[required]",
1034
1034
  "range_string": "[{}]",
1035
1035
  "append_range_help_string": "\\[{}]",
1036
- "padding_epilog": (0, 3, 1, 3),
1037
- "padding_footer_text": (0, 3, 1, 3),
1036
+ "padding_epilog": (0, 2, 1, 2),
1037
+ "padding_footer_text": (0, 2, 1, 2),
1038
1038
  },
1039
1039
  ),
1040
1040
  # Spacious with sharp corners
@@ -134,9 +134,7 @@ class RichCommand(Command):
134
134
  def _error_formatter(self) -> RichHelpFormatter:
135
135
  from click import get_current_context
136
136
 
137
- try:
138
- ctx: RichContext = get_current_context() # type: ignore[assignment]
139
- except RuntimeError:
137
+ def _get_formatter() -> RichHelpFormatter:
140
138
  config = self._generate_rich_help_config()
141
139
  formatter = self.context_class.formatter_class(
142
140
  config=config,
@@ -144,8 +142,17 @@ class RichCommand(Command):
144
142
  self.context_class.export_console_as if self.context_class.errors_in_output_format else None
145
143
  ),
146
144
  )
145
+ return formatter
146
+
147
+ try:
148
+ ctx: RichContext = get_current_context() # type: ignore[assignment]
149
+ except RuntimeError:
150
+ formatter = _get_formatter()
147
151
  else:
148
- formatter = ctx.make_formatter(error_mode=True)
152
+ if not isinstance(ctx, RichContext):
153
+ formatter = _get_formatter()
154
+ else:
155
+ formatter = ctx.make_formatter(error_mode=True)
149
156
  return formatter
150
157
 
151
158
  @overload
@@ -111,7 +111,7 @@ class RichContext(click.Context):
111
111
  exc_value: Optional[BaseException],
112
112
  tb: Optional[TracebackType],
113
113
  ) -> None:
114
- return super().__exit__(exc_type, exc_value, tb)
114
+ super().__exit__(exc_type, exc_value, tb)
115
115
 
116
116
 
117
117
  def get_current_context(silent: bool = False) -> Optional[RichContext]:
@@ -104,7 +104,7 @@ class RichHelpConfiguration:
104
104
  for a given field, the right-most field is used.
105
105
  """
106
106
 
107
- theme: Optional[str] = field(default=None)
107
+ theme: Optional[Union[str, RichClickTheme]] = field(default=None)
108
108
  enable_theme_env_var: bool = field(default=True)
109
109
 
110
110
  # Default styles
@@ -128,6 +128,7 @@ class RichHelpConfiguration:
128
128
  style_deprecated: "StyleType" = field(default=FROM_THEME)
129
129
  style_helptext_first_line: "StyleType" = field(default=FROM_THEME)
130
130
  style_helptext: "StyleType" = field(default=FROM_THEME)
131
+ style_helptext_aliases: Optional["StyleType"] = field(default=None)
131
132
  style_option_help: "StyleType" = field(default=FROM_THEME)
132
133
  style_command_help: "StyleType" = field(default=FROM_THEME)
133
134
  style_option_default: "StyleType" = field(default=FROM_THEME)
@@ -204,6 +205,7 @@ class RichHelpConfiguration:
204
205
  range_string: str = field(default=FROM_THEME)
205
206
  append_metavars_help_string: str = field(default=FROM_THEME)
206
207
  append_range_help_string: str = field(default=FROM_THEME)
208
+ helptext_aliases_string: str = field(default="Aliases: {}")
207
209
  arguments_panel_title: str = field(default="Arguments")
208
210
  options_panel_title: str = field(default="Options")
209
211
  commands_panel_title: str = field(default="Commands")
@@ -258,6 +260,7 @@ class RichHelpConfiguration:
258
260
  """Define sorted groups of panels to display options and arguments"""
259
261
  use_click_short_help: bool = field(default=False)
260
262
  """Use click's default function to truncate help text"""
263
+ helptext_show_aliases: bool = field(default=True)
261
264
  highlighter: Optional["Highlighter"] = field(default=None, repr=False, compare=False)
262
265
  """(Deprecated) Rich regex highlighter for help highlighting"""
263
266
 
@@ -386,7 +389,7 @@ class RichHelpConfiguration:
386
389
  return inst
387
390
 
388
391
  def apply_theme(self, force_default: bool = False) -> None:
389
- theme = None
392
+ theme: Optional[Union[str, RichClickTheme]] = None
390
393
  raise_key_error = True
391
394
 
392
395
  import rich_click.rich_click as rc
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- import os
3
+ import io
4
4
  import sys
5
5
  from contextlib import contextmanager
6
6
  from functools import cached_property
@@ -78,7 +78,7 @@ def create_console(
78
78
  )
79
79
  # Defaults for console.color_system change when file is in __init__.
80
80
  # Workaround: set file after __init__.
81
- console.file = file or open(os.devnull, "w")
81
+ console.file = file or io.StringIO()
82
82
  max_width = max_width if max_width is not None else config.max_width
83
83
  if isinstance(max_width, int):
84
84
  console.width = min(max_width, console.size.width)
@@ -262,6 +262,12 @@ class RichHelpFormatter(click.HelpFormatter):
262
262
  from rich.console import COLOR_SYSTEMS
263
263
  from rich.segment import Segment
264
264
 
265
+ if self.console.legacy_windows:
266
+ # Handle legacy windows
267
+ import colorama # type: ignore[import-untyped]
268
+
269
+ colorama.init()
270
+
265
271
  if self.console.no_color:
266
272
  res = "".join(
267
273
  (
@@ -116,7 +116,7 @@ def _get_help_text(
116
116
  )
117
117
 
118
118
  # Fetch and dedent the help text
119
- help_text = inspect.cleandoc(obj.help)
119
+ help_text = inspect.cleandoc(obj.help or "")
120
120
 
121
121
  # Trim off anything that comes after \f on its own line
122
122
  help_text = help_text.partition("\f")[0]
@@ -187,6 +187,15 @@ def _get_help_text(
187
187
  # Join with double linebreaks if markdown
188
188
  remaining_lines = lb.join(remaining_paragraphs)
189
189
  yield formatter.rich_text(remaining_lines, formatter.config.style_helptext)
190
+ if getattr(obj, "aliases", None) and formatter.config.helptext_show_aliases:
191
+ yield Text.from_markup(
192
+ formatter.config.helptext_aliases_string.format(", ".join(obj.aliases)), # type: ignore[attr-defined]
193
+ style=(
194
+ formatter.config.style_helptext_aliases
195
+ if formatter.config.style_helptext_aliases is not None
196
+ else formatter.config.style_helptext
197
+ ),
198
+ )
190
199
 
191
200
 
192
201
  def _get_deprecated_text(
@@ -553,6 +562,18 @@ def _get_parameter_default(
553
562
  finally:
554
563
  ctx.resilient_parsing = resilient
555
564
 
565
+ notset: Tuple[Any, ...]
566
+ try:
567
+ # try/except because it's unclear whether later versions of click will change this.
568
+ if not CLICK_IS_BEFORE_VERSION_82:
569
+ from click.core import UNSET # type: ignore[attr-defined,unused-ignore]
570
+
571
+ notset = (UNSET, None)
572
+ else:
573
+ notset = (None,)
574
+ except ImportError:
575
+ notset = (None,)
576
+
556
577
  if (not CLICK_IS_VERSION_80 and param.show_default is not None) or param.show_default:
557
578
  if isinstance(param.show_default, str):
558
579
  show_default_is_str = show_default = True
@@ -563,7 +584,7 @@ def _get_parameter_default(
563
584
 
564
585
  default_string: Optional[str] = None
565
586
 
566
- if show_default_is_str or (show_default and (default_value is not None)):
587
+ if show_default_is_str or (show_default and (default_value not in notset)):
567
588
  if show_default_is_str:
568
589
  default_string = f"({param.show_default})"
569
590
  elif isinstance(default_value, (list, tuple)):
@@ -959,7 +980,7 @@ def get_rich_usage(formatter: RichHelpFormatter, prog: str, args: str = "", pref
959
980
  def get_rich_help_text(self: Command, ctx: RichContext, formatter: RichHelpFormatter) -> None:
960
981
  """Write rich help text to the formatter if it exists."""
961
982
  # Print command / group help if we have some
962
- if self.help:
983
+ if self.help or self.deprecated or getattr(self, "aliases", None):
963
984
  # Print with some padding
964
985
  formatter.write(
965
986
  Padding(
@@ -17,10 +17,9 @@ from typing import (
17
17
  Union,
18
18
  )
19
19
 
20
- import click
21
- import click.core
22
- from click import Group
20
+ from click import Context, Parameter
23
21
 
22
+ from rich_click._click_types_cache import Argument, Command, Group
24
23
  from rich_click.rich_help_configuration import ColumnType, CommandColumnType, OptionColumnType
25
24
  from rich_click.rich_parameter import RichArgument, RichParameter
26
25
  from rich_click.utils import CommandGroupDict, OptionGroupDict
@@ -38,7 +37,7 @@ if TYPE_CHECKING:
38
37
 
39
38
 
40
39
  ColT = TypeVar("ColT", bound=ColumnType)
41
- CT = TypeVar("CT", click.Command, click.Parameter)
40
+ CT = TypeVar("CT", Command, Parameter)
42
41
  GroupType = TypeVar("GroupType", OptionGroupDict, CommandGroupDict)
43
42
 
44
43
 
@@ -90,7 +89,7 @@ class RichPanel(Generic[CT, ColT]):
90
89
 
91
90
  return get_box(box)
92
91
 
93
- def to_info_dict(self, ctx: click.Context) -> Dict[str, Any]:
92
+ def to_info_dict(self, ctx: Context) -> Dict[str, Any]:
94
93
  if self._object_attr is NotImplemented:
95
94
  raise NotImplementedError()
96
95
  return {
@@ -101,11 +100,11 @@ class RichPanel(Generic[CT, ColT]):
101
100
  }
102
101
 
103
102
  @classmethod
104
- def list_all_objects(cls, ctx: click.Context) -> List[Tuple[str, CT]]:
103
+ def list_all_objects(cls, ctx: Context) -> List[Tuple[str, CT]]:
105
104
  """List all objects of the command that this panel type works with."""
106
105
  raise NotImplementedError()
107
106
 
108
- def get_objects(self, command: click.Command, ctx: click.Context) -> Generator[CT, None, None]:
107
+ def get_objects(self, command: Command, ctx: Context) -> Generator[CT, None, None]:
109
108
  """List the proper names assigned to the panel."""
110
109
  raise NotImplementedError()
111
110
 
@@ -163,7 +162,7 @@ class RichPanel(Generic[CT, ColT]):
163
162
  return f"<{self.__class__.__name__} {self.name}>"
164
163
 
165
164
 
166
- class RichOptionPanel(RichPanel[click.Parameter, OptionColumnType]):
165
+ class RichOptionPanel(RichPanel[Parameter, OptionColumnType]):
167
166
  """Panel for parameters."""
168
167
 
169
168
  _highlight: ClassVar[bool] = True
@@ -180,10 +179,10 @@ class RichOptionPanel(RichPanel[click.Parameter, OptionColumnType]):
180
179
  self.options = options or []
181
180
 
182
181
  @classmethod
183
- def list_all_objects(cls, ctx: click.Context) -> List[Tuple[str, click.Parameter]]:
182
+ def list_all_objects(cls, ctx: Context) -> List[Tuple[str, Parameter]]:
184
183
  return [(i.name, i) for i in ctx.command.get_params(ctx)] # type: ignore[misc]
185
184
 
186
- def get_objects(self, command: click.Command, ctx: click.Context) -> Generator[click.Parameter, None, None]:
185
+ def get_objects(self, command: Command, ctx: Context) -> Generator[Parameter, None, None]:
187
186
  """List the objects assigned to the panel."""
188
187
  for opt in self.options:
189
188
  # Get the param
@@ -214,7 +213,6 @@ class RichOptionPanel(RichPanel[click.Parameter, OptionColumnType]):
214
213
  table = self._get_base_table(**t_styles)
215
214
  rows = []
216
215
  for param in self.get_objects(command, ctx):
217
-
218
216
  from rich_click.rich_help_rendering import get_parameter_rich_table_row
219
217
 
220
218
  cols = (
@@ -225,17 +223,18 @@ class RichOptionPanel(RichPanel[click.Parameter, OptionColumnType]):
225
223
 
226
224
  rows.append(cols)
227
225
 
228
- if True:
229
- rows = list(
230
- map(
231
- list,
232
- zip(*[col for col in zip(*rows) if any(cell for cell in col)]),
233
- )
234
- )
226
+ headers = [i.replace("_", " ").title() for i in formatter.config.options_table_column_types]
227
+
228
+ filtered = [(h, c) for h, c in zip(headers, zip(*rows)) if any(cell for cell in c)]
229
+ headers, rows = zip(*filtered) if filtered else ([], [])
230
+ rows = list(map(list, zip(*rows))) if rows else []
235
231
 
236
232
  for row in rows:
237
233
  table.add_row(*row)
238
234
 
235
+ for col, header in zip(table.columns, headers):
236
+ col.header = header
237
+
239
238
  return table
240
239
 
241
240
  def render(
@@ -297,7 +296,7 @@ class RichOptionPanel(RichPanel[click.Parameter, OptionColumnType]):
297
296
  return panel
298
297
 
299
298
 
300
- class RichCommandPanel(RichPanel[click.Command, CommandColumnType]):
299
+ class RichCommandPanel(RichPanel[Command, CommandColumnType]):
301
300
  """Panel for parameters."""
302
301
 
303
302
  _object_attr: ClassVar[str] = "commands"
@@ -313,13 +312,13 @@ class RichCommandPanel(RichPanel[click.Command, CommandColumnType]):
313
312
  self.commands = commands or []
314
313
 
315
314
  @classmethod
316
- def list_all_objects(cls, ctx: click.Context) -> List[Tuple[str, click.Command]]:
315
+ def list_all_objects(cls, ctx: Context) -> List[Tuple[str, Command]]:
317
316
  if not isinstance(ctx.command, Group):
318
317
  return []
319
318
 
320
319
  return list(sorted(list(ctx.command.commands.items())))
321
320
 
322
- def get_objects(self, command: click.Command, ctx: click.Context) -> Generator[click.Command, None, None]:
321
+ def get_objects(self, command: Command, ctx: Context) -> Generator[Command, None, None]:
323
322
  """List the objects assigned to the panel."""
324
323
  if not isinstance(command, Group):
325
324
  return
@@ -388,17 +387,18 @@ class RichCommandPanel(RichPanel[click.Command, CommandColumnType]):
388
387
 
389
388
  rows.append(cols)
390
389
 
391
- if True:
392
- rows = list(
393
- map(
394
- list,
395
- zip(*[col for col in zip(*rows) if any(cell for cell in col)]),
396
- )
397
- )
390
+ headers = [i.replace("_", " ").title() for i in formatter.config.commands_table_column_types]
391
+
392
+ filtered = [(h, c) for h, c in zip(headers, zip(*rows)) if any(cell for cell in c)]
393
+ headers, rows = zip(*filtered) if filtered else ([], [])
394
+ rows = list(map(list, zip(*rows))) if rows else []
398
395
 
399
396
  for row in rows:
400
397
  table.add_row(*row)
401
398
 
399
+ for col, header in zip(table.columns, headers):
400
+ col.header = header
401
+
402
402
  return table
403
403
 
404
404
  def render(
@@ -494,25 +494,26 @@ def _resolve_panels_from_config(
494
494
 
495
495
  # Step 2: Match currently executing command to keys
496
496
  # Assign wildcards, but make sure we do not overwrite anything already defined.
497
- for _path in paths:
498
- for mtch in reversed(sorted([_ for _ in groups if fnmatch(_path, _)])):
499
- wildcard_option_groups = groups[mtch]
500
- for grp in wildcard_option_groups:
501
- grp = grp.copy()
502
- opts: List[str] = grp.get(panel_cls._object_attr, []) # type: ignore[assignment]
503
- traversed = []
504
- for opt in grp.get(panel_cls._object_attr, []): # type: ignore[attr-defined]
505
- if grp.get("deduplicate", True) and opt in [
506
- _opt
507
- for _grp in final_groups_list
508
- for _opt in [*traversed, *_grp.get(panel_cls._object_attr, [])] # type: ignore[has-type]
509
- ]:
510
- opts.remove(opt)
511
- traversed.append(opt)
512
- grp[panel_cls._object_attr] = opts # type: ignore[literal-required]
513
- final_groups_list.append(grp)
514
-
515
- return [panel_cls(**grp) for grp in final_groups_list] # type: ignore[misc]
497
+ for mtch in reversed(sorted([_ for _ in groups if any(fnmatch(_path, _) for _path in paths)])):
498
+ wildcard_option_groups = groups[mtch]
499
+ for grp in wildcard_option_groups:
500
+ grp = grp.copy()
501
+ opts: List[str] = grp.get(panel_cls._object_attr, []) # type: ignore[assignment]
502
+ traversed = []
503
+ for opt in grp.get(panel_cls._object_attr, []): # type: ignore[attr-defined]
504
+
505
+ if grp.get("deduplicate", True) and opt in [
506
+ _opt
507
+ for _grp in final_groups_list
508
+ for _opt in _grp.get(panel_cls._object_attr, []) # type: ignore[attr-defined]
509
+ ]:
510
+ opts.remove(opt)
511
+ traversed.append(opt)
512
+ grp[panel_cls._object_attr] = opts # type: ignore[literal-required]
513
+ grp.pop("deduplicate", None)
514
+ final_groups_list.append(grp)
515
+
516
+ return [panel_cls(**grp) for grp in final_groups_list] # type: ignore[misc,arg-type]
516
517
 
517
518
 
518
519
  def construct_panels(
@@ -527,6 +528,9 @@ def construct_panels(
527
528
  # then we respect intra-type sort order but not inter-type sort order.
528
529
  defined_commands = False
529
530
  defined_options = False
531
+ # Handling ordering is tricky with the "groups" feature.
532
+ # it's safest to flag and treat specially.
533
+ using_groups_feat = False
530
534
 
531
535
  # Start with list of panels already defined.
532
536
  defined_panels: Dict[Tuple[str, str], RichPanel[Any, Any]] = {}
@@ -544,7 +548,7 @@ def construct_panels(
544
548
  )
545
549
  if option_groups_from_config:
546
550
  defined_panels.update({(p._object_attr, p.name): p for p in option_groups_from_config})
547
- defined_options = True
551
+ using_groups_feat = True
548
552
 
549
553
  if isinstance(command, Group):
550
554
  if formatter.config.command_groups:
@@ -553,7 +557,7 @@ def construct_panels(
553
557
  )
554
558
  if command_groups_from_config:
555
559
  defined_panels.update({(p._object_attr, p.name): p for p in command_groups_from_config})
556
- defined_commands = True
560
+ using_groups_feat = True
557
561
 
558
562
  # Separate out default panels because we need to sort them properly later.
559
563
  # We will reversed() through this so order is flipped.
@@ -564,7 +568,7 @@ def construct_panels(
564
568
  post_default_panels: Dict[Tuple[str, str], List[str]]
565
569
  if isinstance(command, Group):
566
570
  if formatter.config.commands_before_options:
567
- if defined_commands != defined_options:
571
+ if defined_commands != defined_options or using_groups_feat:
568
572
  pre_default_panels = {
569
573
  ("commands", formatter.config.commands_panel_title): [],
570
574
  }
@@ -579,7 +583,7 @@ def construct_panels(
579
583
  ("options", formatter.config.options_panel_title): [],
580
584
  }
581
585
  else:
582
- if defined_commands != defined_options:
586
+ if defined_commands != defined_options or using_groups_feat:
583
587
  pre_default_panels = {
584
588
  ("options", formatter.config.arguments_panel_title): [],
585
589
  ("options", formatter.config.options_panel_title): [],
@@ -622,7 +626,7 @@ def construct_panels(
622
626
  if ("options", formatter.config.arguments_panel_title) in defined_panels:
623
627
  _show_arguments = True
624
628
 
625
- objs: List[Tuple[str, str, Union[click.core.Parameter, click.core.Command]]] = [
629
+ objs: List[Tuple[str, str, Union[Parameter, Command]]] = [
626
630
  ("options", name, o) for name, o in formatter.option_panel_class.list_all_objects(ctx)
627
631
  ]
628
632
  if isinstance(command, Group):
@@ -639,9 +643,9 @@ def construct_panels(
639
643
  if getattr(obj, "hidden", False):
640
644
  continue
641
645
  names = {name, obj.name}
642
- if isinstance(obj, click.core.Parameter):
646
+ if isinstance(obj, Parameter):
643
647
  names.update(obj.opts)
644
- elif isinstance(obj, click.core.Command) and obj.callback is not None:
648
+ elif isinstance(obj, Command) and obj.callback is not None:
645
649
  names.add(obj.callback.__name__)
646
650
  assigned_to = set()
647
651
  for n in names:
@@ -666,7 +670,7 @@ def construct_panels(
666
670
  if not panel_list:
667
671
  inferred = True
668
672
  if typ == "options":
669
- if not formatter.config.group_arguments_options and isinstance(obj, click.Argument):
673
+ if not formatter.config.group_arguments_options and isinstance(obj, Argument):
670
674
  if _show_arguments is not False:
671
675
  panel_list = [formatter.config.arguments_panel_title]
672
676
  else:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import sys
4
- from typing import Any, Dict, List, Optional, Type, TypedDict
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, TypedDict
5
5
 
6
6
 
7
7
  if sys.version_info < (3, 11):
@@ -9,6 +9,11 @@ if sys.version_info < (3, 11):
9
9
  else:
10
10
  from typing import NotRequired
11
11
 
12
+ if TYPE_CHECKING:
13
+ from rich.style import StyleType
14
+
15
+ from rich_click.rich_help_configuration import CommandColumnType, OptionColumnType
16
+
12
17
 
13
18
  notset: Any = object()
14
19
 
@@ -46,8 +51,14 @@ class CommandGroupDict(TypedDict):
46
51
 
47
52
  name: NotRequired[str]
48
53
  commands: NotRequired[List[str]]
49
- table_styles: NotRequired[Dict[str, Any]]
50
- panel_styles: NotRequired[Dict[str, Any]]
54
+ help: NotRequired[Optional[str]]
55
+ help_style: NotRequired[Optional["StyleType"]]
56
+ table_styles: NotRequired[Optional[Dict[str, Any]]]
57
+ panel_styles: NotRequired[Optional[Dict[str, Any]]]
58
+ column_types: NotRequired[Optional[List["CommandColumnType"]]]
59
+ inline_help_in_title: NotRequired[Optional[bool]]
60
+ title_style: NotRequired[Optional["StyleType"]]
61
+
51
62
  deduplicate: NotRequired[bool]
52
63
 
53
64
 
@@ -56,6 +67,12 @@ class OptionGroupDict(TypedDict):
56
67
 
57
68
  name: NotRequired[str]
58
69
  options: NotRequired[List[str]]
59
- table_styles: NotRequired[Dict[str, Any]]
60
- panel_styles: NotRequired[Dict[str, Any]]
70
+ help: NotRequired[Optional[str]]
71
+ help_style: NotRequired[Optional["StyleType"]]
72
+ table_styles: NotRequired[Optional[Dict[str, Any]]]
73
+ panel_styles: NotRequired[Optional[Dict[str, Any]]]
74
+ column_types: NotRequired[Optional[List["OptionColumnType"]]]
75
+ inline_help_in_title: NotRequired[Optional[bool]]
76
+ title_style: NotRequired[Optional["StyleType"]]
77
+
61
78
  deduplicate: NotRequired[bool]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rich-click
3
- Version: 1.9.0.dev6
3
+ Version: 1.9.2.dev0
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>
@@ -44,6 +44,7 @@ Requires-Python: >=3.8
44
44
  Description-Content-Type: text/markdown
45
45
  License-File: LICENSE
46
46
  Requires-Dist: click>=8
47
+ Requires-Dist: colorama; platform_system == "Windows"
47
48
  Requires-Dist: rich>=12
48
49
  Requires-Dist: typing-extensions>=4; python_version < "3.11"
49
50
  Provides-Extra: dev
@@ -68,6 +69,7 @@ Requires-Dist: mkdocs-glightbox>=0.4; extra == "docs"
68
69
  Requires-Dist: mkdocs-include-markdown-plugin>=7.1.7; python_version >= "3.9" and extra == "docs"
69
70
  Requires-Dist: mkdocs-material[imaging]~=9.5.18; extra == "docs"
70
71
  Requires-Dist: mkdocs-material-extensions>=1.3.1; extra == "docs"
72
+ Requires-Dist: mkdocs-redirects>=1.2.2; extra == "docs"
71
73
  Requires-Dist: mkdocs-rss-plugin>=1.15; extra == "docs"
72
74
  Requires-Dist: mkdocstrings[python]>=0.26.1; extra == "docs"
73
75
  Requires-Dist: rich-codex>=1.2.11; extra == "docs"
@@ -1,6 +1,9 @@
1
1
  click>=8
2
2
  rich>=12
3
3
 
4
+ [:platform_system == "Windows"]
5
+ colorama
6
+
4
7
  [:python_version < "3.11"]
5
8
  typing-extensions>=4
6
9
 
@@ -26,6 +29,7 @@ mkdocs-github-admonitions-plugin>=0.1.1
26
29
  mkdocs-glightbox>=0.4
27
30
  mkdocs-material[imaging]~=9.5.18
28
31
  mkdocs-material-extensions>=1.3.1
32
+ mkdocs-redirects>=1.2.2
29
33
  mkdocs-rss-plugin>=1.15
30
34
  mkdocstrings[python]>=0.26.1
31
35
  rich-codex>=1.2.11
File without changes