Skip to content

Commit a614ab6

Browse files
authored
Broaden support for core classes (#82)
1 parent c013cb0 commit a614ab6

File tree

7 files changed

+66
-40
lines changed

7 files changed

+66
-40
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7+
## Unreleased
8+
9+
### Added
10+
11+
- Add support for `click.Command`-like, `click.Group`-like and `click.Context`-like objects without requiring them to be actual subclasses. (Pull #82)
12+
13+
### Fixed
14+
15+
- Remove explicit reference to `click.BaseCommand` and `click.MultiCommand` objects in anticipation of their deprecation. (Pull #82)
16+
717
## 0.8.1 - 2023-09-18
818

919
### Fixed

mkdocs_click/_docs.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
def make_command_docs(
1717
prog_name: str,
18-
command: click.BaseCommand,
18+
command: click.Command,
1919
depth: int = 0,
2020
style: str = "plain",
2121
remove_ascii_art: bool = False,
@@ -42,7 +42,7 @@ def make_command_docs(
4242

4343
def _recursively_make_command_docs(
4444
prog_name: str,
45-
command: click.BaseCommand,
45+
command: click.Command,
4646
parent: click.Context | None = None,
4747
depth: int = 0,
4848
style: str = "plain",
@@ -90,20 +90,26 @@ def _recursively_make_command_docs(
9090

9191

9292
def _build_command_context(
93-
prog_name: str, command: click.BaseCommand, parent: click.Context | None
93+
prog_name: str, command: click.Command, parent: click.Context | None
9494
) -> click.Context:
95-
return click.Context(cast(click.Command, command), info_name=prog_name, parent=parent)
95+
return _get_context_class(command)(
96+
cast(click.Command, command), info_name=prog_name, parent=parent
97+
)
9698

9799

98-
def _get_sub_commands(command: click.Command, ctx: click.Context) -> list[click.Command]:
100+
def _get_sub_commands(
101+
command: click.Command | click.Group, ctx: click.Context
102+
) -> list[click.Command]:
99103
"""Return subcommands of a Click command."""
100104
subcommands = getattr(command, "commands", {})
101105
if subcommands:
102106
return list(subcommands.values())
103107

104-
if not isinstance(command, click.MultiCommand):
108+
if not _is_command_group(command):
105109
return []
106110

111+
command = cast(click.Group, command)
112+
107113
subcommands = []
108114

109115
for name in command.list_commands(ctx):
@@ -360,3 +366,13 @@ def _make_subcommands_links(
360366
help_string = "*No description was provided with this command.*"
361367
yield f"- *{command_bullet}*: {help_string}"
362368
yield ""
369+
370+
371+
def _get_context_class(command: click.Command) -> type[click.Context]:
372+
# https://github.com/pallets/click/blob/8.1.8/src/click/core.py#L859-L862
373+
return command.context_class
374+
375+
376+
def _is_command_group(command: click.Command) -> bool:
377+
# https://github.com/pallets/click/blob/8.1.8/src/click/core.py#L1806-L1811
378+
return isinstance(command, click.Group) or hasattr(command, "command_class")

mkdocs_click/_extension.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def replace_command_docs(has_attr_list: bool = False, **options: Any) -> Iterato
2222

2323
module = options["module"]
2424
command = options["command"]
25-
prog_name = options.get("prog_name", None)
25+
prog_name = options.get("prog_name")
2626
depth = int(options.get("depth", 0))
2727
style = options.get("style", "plain")
2828
remove_ascii_art = options.get("remove_ascii_art", False)

mkdocs_click/_loader.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
from ._exceptions import MkDocsClickException
1212

1313

14-
def load_command(module: str, attribute: str) -> click.BaseCommand:
14+
def load_command(module: str, attribute: str) -> click.Command:
1515
"""
1616
Load and return the Click command object located at '<module>:<attribute>'.
1717
"""
1818
command = _load_obj(module, attribute)
1919

20-
if not isinstance(command, click.BaseCommand):
20+
if not (isinstance(command, click.Command) or hasattr(command, "context_class")):
2121
raise MkDocsClickException(
22-
f"{attribute!r} must be a 'click.BaseCommand' object, got {type(command)}"
22+
f"{attribute!r} must be a 'click.Command'-like object, got {type(command)}"
2323
)
2424

2525
return command

tests/app/cli.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def hidden():
5050
cli_named.add_command(hidden)
5151

5252

53-
class MultiCLI(click.MultiCommand):
53+
class GroupCLI(click.Group):
5454
def list_commands(self, ctx):
5555
return ["foo", "bar"]
5656

@@ -59,5 +59,5 @@ def get_command(self, ctx, name):
5959
return cmds.get(name)
6060

6161

62-
multi_named = MultiCLI(name="multi", help="Main entrypoint for this dummy program")
63-
multi = MultiCLI(help="Main entrypoint for this dummy program")
62+
group_named = GroupCLI(name="group", help="Main entrypoint for this dummy program")
63+
group = GroupCLI(help="Main entrypoint for this dummy program")

tests/test_docs.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def test_style_table(command, expected):
175175
assert output == expected
176176

177177

178-
class MultiCLI(click.MultiCommand):
178+
class GroupCLI(click.Group):
179179
def list_commands(self, ctx):
180180
return ["single-command"]
181181

@@ -184,26 +184,26 @@ def get_command(self, ctx, name):
184184

185185

186186
@pytest.mark.parametrize(
187-
"multi",
187+
"group",
188188
[
189-
pytest.param(MultiCLI("multi", help="Multi help"), id="explicit-name"),
190-
pytest.param(MultiCLI(help="Multi help"), id="no-name"),
189+
pytest.param(GroupCLI("group", help="Group help"), id="explicit-name"),
190+
pytest.param(GroupCLI(help="Group help"), id="no-name"),
191191
],
192192
)
193-
def test_custom_multicommand(multi):
193+
def test_custom_group(group):
194194
"""
195-
Custom `MultiCommand` objects are supported (i.e. not just `Group` multi-commands).
195+
Custom `Group` objects are supported.
196196
"""
197197
expected = dedent(
198198
"""
199-
# multi
199+
# group
200200
201-
Multi help
201+
Group help
202202
203203
**Usage:**
204204
205205
```text
206-
multi [OPTIONS] COMMAND [ARGS]...
206+
group [OPTIONS] COMMAND [ARGS]...
207207
```
208208
209209
**Options:**
@@ -219,7 +219,7 @@ def test_custom_multicommand(multi):
219219
**Usage:**
220220
221221
```text
222-
multi hello [OPTIONS]
222+
group hello [OPTIONS]
223223
```
224224
225225
**Options:**
@@ -231,31 +231,31 @@ def test_custom_multicommand(multi):
231231
"""
232232
).lstrip()
233233

234-
output = "\n".join(make_command_docs("multi", multi))
234+
output = "\n".join(make_command_docs("group", group))
235235
assert output == expected
236236

237237

238238
@pytest.mark.parametrize(
239-
"multi",
239+
"group",
240240
[
241-
pytest.param(MultiCLI("multi", help="Multi help"), id="explicit-name"),
242-
pytest.param(MultiCLI(help="Multi help"), id="no-name"),
241+
pytest.param(GroupCLI("group", help="Group help"), id="explicit-name"),
242+
pytest.param(GroupCLI(help="Group help"), id="no-name"),
243243
],
244244
)
245-
def test_custom_multicommand_with_list_subcommands(multi):
245+
def test_custom_group_with_list_subcommands(group):
246246
"""
247-
Custom `MultiCommand` objects are supported (i.e. not just `Group` multi-commands).
247+
Custom `Group` objects are supported.
248248
"""
249249
expected = dedent(
250250
"""
251-
# multi
251+
# group
252252
253-
Multi help
253+
Group help
254254
255255
**Usage:**
256256
257257
```text
258-
multi [OPTIONS] COMMAND [ARGS]...
258+
group [OPTIONS] COMMAND [ARGS]...
259259
```
260260
261261
**Options:**
@@ -275,7 +275,7 @@ def test_custom_multicommand_with_list_subcommands(multi):
275275
**Usage:**
276276
277277
```text
278-
multi hello [OPTIONS]
278+
group hello [OPTIONS]
279279
```
280280
281281
**Options:**
@@ -287,7 +287,7 @@ def test_custom_multicommand_with_list_subcommands(multi):
287287
"""
288288
).lstrip()
289289

290-
output = "\n".join(make_command_docs("multi", multi, list_subcommands=True))
290+
output = "\n".join(make_command_docs("group", group, list_subcommands=True))
291291
assert output == expected
292292

293293

tests/test_extension.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
[
2121
pytest.param("cli", "cli", id="cli-simple"),
2222
pytest.param("cli_named", "cli", id="cli-explicit-name"),
23-
pytest.param("multi_named", "multi", id="multi-explicit-name"),
24-
pytest.param("multi", "multi", id="no-name"),
23+
pytest.param("group_named", "group", id="group-explicit-name"),
24+
pytest.param("group", "group", id="no-name"),
2525
],
2626
)
2727
def test_extension(command, expected_name):
@@ -133,8 +133,8 @@ def test_enhanced_titles():
133133
[
134134
pytest.param("cli", "cli", id="cli-simple"),
135135
pytest.param("cli_named", "cli", id="cli-explicit-name"),
136-
pytest.param("multi_named", "multi", id="multi-explicit-name"),
137-
pytest.param("multi", "multi", id="no-name"),
136+
pytest.param("group_named", "group", id="group-explicit-name"),
137+
pytest.param("group", "group", id="no-name"),
138138
],
139139
)
140140
def test_extension_with_subcommand(command, expected_name):
@@ -162,8 +162,8 @@ def test_extension_with_subcommand(command, expected_name):
162162
[
163163
pytest.param("cli", "cli", id="cli-simple"),
164164
pytest.param("cli_named", "cli", id="cli-explicit-name"),
165-
pytest.param("multi_named", "multi", id="multi-explicit-name"),
166-
pytest.param("multi", "multi", id="no-name"),
165+
pytest.param("group_named", "group", id="group-explicit-name"),
166+
pytest.param("group", "group", id="no-name"),
167167
],
168168
)
169169
def test_enhanced_titles_with_subcommand(command, expected_name):

0 commit comments

Comments
 (0)