Skip to content

Commit c19c018

Browse files
committed
Made __name__ a read-only propery for BoundCommandFunc and UnboundCommandFunc Protocol classes
Also: - Switched some types in cmd2.py from `Callable[..., Any]` to `BoundCommandFunc` - Removed a number of `cast` calls in cmd2.py which were no longer needed
1 parent 59455b6 commit c19c018

3 files changed

Lines changed: 45 additions & 31 deletions

File tree

cmd2/cmd2.py

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ def register_command_set(self, cmdset: CommandSet[Any]) -> None:
919919

920920
cmdset.on_register(self)
921921
methods = cast(
922-
list[tuple[str, Callable[..., Any]]],
922+
list[tuple[str, BoundCommandFunc]],
923923
inspect.getmembers(
924924
cmdset,
925925
predicate=lambda meth: ( # type: ignore[arg-type]
@@ -935,7 +935,7 @@ def register_command_set(self, cmdset: CommandSet[Any]) -> None:
935935
for cmd_func_name, command_method in methods:
936936
command = cmd_func_name[len(COMMAND_FUNC_PREFIX) :]
937937

938-
self._install_command_function(cmd_func_name, cast(BoundCommandFunc, command_method), type(cmdset).__name__)
938+
self._install_command_function(cmd_func_name, command_method, type(cmdset).__name__)
939939
installed_attributes.append(cmd_func_name)
940940

941941
completer_func_name = COMPLETER_FUNC_PREFIX + command
@@ -953,7 +953,7 @@ def register_command_set(self, cmdset: CommandSet[Any]) -> None:
953953
self._cmd_to_command_sets[command] = cmdset
954954

955955
# If this command is in a disabled category, then disable it
956-
command_category = self._get_command_category(cast(BoundCommandFunc, command_method))
956+
command_category = self._get_command_category(command_method)
957957
if command_category in self.disabled_categories:
958958
message_to_print = self.disabled_categories[command_category]
959959
self.disable_command(command, message_to_print)
@@ -1086,17 +1086,19 @@ def unregister_command_set(self, cmdset: CommandSet[Any]) -> None:
10861086
cmdset.on_unregister()
10871087
self._unregister_subcommands(cmdset)
10881088

1089-
methods: list[tuple[str, Callable[..., Any]]] = inspect.getmembers(
1090-
cmdset,
1091-
predicate=lambda meth: ( # type: ignore[arg-type]
1092-
isinstance(meth, Callable) # type: ignore[arg-type]
1093-
and hasattr(meth, "__name__")
1094-
and meth.__name__.startswith(COMMAND_FUNC_PREFIX)
1089+
methods: list[tuple[str, BoundCommandFunc]] = cast(
1090+
list[tuple[str, BoundCommandFunc]],
1091+
inspect.getmembers(
1092+
cmdset,
1093+
predicate=lambda meth: ( # type: ignore[arg-type]
1094+
isinstance(meth, Callable) # type: ignore[arg-type]
1095+
and hasattr(meth, "__name__")
1096+
and meth.__name__.startswith(COMMAND_FUNC_PREFIX)
1097+
),
10951098
),
10961099
)
10971100

1098-
for cmd_func_name, command_method_raw in methods:
1099-
command_method = cast(BoundCommandFunc, command_method_raw)
1101+
for cmd_func_name, command_method in methods:
11001102
command = cmd_func_name[len(COMMAND_FUNC_PREFIX) :]
11011103

11021104
# Enable the command before uninstalling it to make sure we remove both
@@ -1159,17 +1161,19 @@ def check_parser_uninstallable(parser: Cmd2ArgumentParser) -> None:
11591161
)
11601162
check_parser_uninstallable(subparser)
11611163

1162-
methods: list[tuple[str, Callable[..., Any]]] = inspect.getmembers(
1163-
cmdset,
1164-
predicate=lambda meth: ( # type: ignore[arg-type]
1165-
isinstance(meth, Callable) # type: ignore[arg-type]
1166-
and hasattr(meth, "__name__")
1167-
and meth.__name__.startswith(COMMAND_FUNC_PREFIX)
1164+
methods: list[tuple[str, BoundCommandFunc]] = cast(
1165+
list[tuple[str, BoundCommandFunc]],
1166+
inspect.getmembers(
1167+
cmdset,
1168+
predicate=lambda meth: ( # type: ignore[arg-type]
1169+
isinstance(meth, Callable) # type: ignore[arg-type]
1170+
and hasattr(meth, "__name__")
1171+
and meth.__name__.startswith(COMMAND_FUNC_PREFIX)
1172+
),
11681173
),
11691174
)
11701175

1171-
for cmd_func_name, command_method_raw in methods:
1172-
command_method = cast(BoundCommandFunc, command_method_raw)
1176+
for cmd_func_name, command_method in methods:
11731177
# We only need to check if it's safe to remove the parser if this
11741178
# is the actual command since command synonyms don't own it.
11751179
if cmd_func_name == command_method.__name__:
@@ -2829,9 +2833,10 @@ def _get_commands_aliases_and_macros_choices(self) -> Choices:
28292833

28302834
# Add commands
28312835
for command in self.get_visible_commands():
2832-
command_func = cast(BoundCommandFunc, self.get_command_func(command))
2833-
description = strip_doc_annotations(command_func.__doc__).splitlines()[0] if command_func.__doc__ else ""
2834-
items.append(CompletionItem(command, display_meta=description))
2836+
command_func = self.get_command_func(command)
2837+
if command_func is not None:
2838+
description = strip_doc_annotations(command_func.__doc__).splitlines()[0] if command_func.__doc__ else ""
2839+
items.append(CompletionItem(command, display_meta=description))
28352840

28362841
# Add aliases
28372842
for name, value in self.aliases.items():
@@ -4347,9 +4352,10 @@ def _build_command_info(self) -> tuple[dict[str, list[str]], list[str]]:
43474352
help_topics.remove(command)
43484353

43494354
# Store the command within its category
4350-
command_func = cast(BoundCommandFunc, self.get_command_func(command))
4351-
category = self._get_command_category(command_func)
4352-
cmds_cats.setdefault(category, []).append(command)
4355+
command_func = self.get_command_func(command)
4356+
if command_func is not None:
4357+
category = self._get_command_category(command_func)
4358+
cmds_cats.setdefault(category, []).append(command)
43534359

43544360
return cmds_cats, help_topics
43554361

@@ -5816,8 +5822,8 @@ def disable_category(self, category: str, message_to_print: str) -> None:
58165822
all_commands = self.get_all_commands()
58175823

58185824
for command in all_commands:
5819-
command_func = cast(BoundCommandFunc, self.get_command_func(command))
5820-
if self._get_command_category(command_func) == category:
5825+
command_func = self.get_command_func(command)
5826+
if command_func is not None and self._get_command_category(command_func) == category:
58215827
self.disable_command(command, message_to_print)
58225828

58235829
self.disabled_categories[category] = message_to_print

cmd2/types.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,13 @@
7272
class BoundCommandFunc(Protocol):
7373
"""Protocol for a command function bound to a command instance."""
7474

75-
__name__: str
7675
__qualname__: str
7776

77+
@property
78+
def __name__(self) -> str:
79+
"""The name of the bound command function."""
80+
...
81+
7882
def __call__(self, *args: Any, **kwargs: Any) -> bool | None:
7983
"""Invoke the bound command function."""
8084

@@ -84,9 +88,13 @@ def __call__(self, *args: Any, **kwargs: Any) -> bool | None:
8488
class UnboundCommandFunc(Protocol[CmdOrSetT, P]):
8589
"""Protocol for an unbound command function."""
8690

87-
__name__: str
8891
__qualname__: str
8992

93+
@property
94+
def __name__(self) -> str:
95+
"""The name of the unbound command function."""
96+
...
97+
9098
def __call__(self, __self: CmdOrSetT, /, *args: P.args, **kwargs: P.kwargs) -> bool | None:
9199
"""Invoke the unbound command function with its command instance."""
92100
...

tests/test_cmd2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
)
3939
from cmd2 import rich_utils as ru
4040
from cmd2 import string_utils as su
41-
from cmd2.types import BoundCommandFunc
4241

4342
from .conftest import (
4443
SHORTCUTS_TXT,
@@ -4178,7 +4177,8 @@ def test_help_disabled_no_help_func(base_app: cmd2.Cmd) -> None:
41784177

41794178
# Intentionally bypass disable_command() to test the fallback in do_help()
41804179
command = "quit"
4181-
command_func = cast(BoundCommandFunc, base_app.get_command_func(command))
4180+
command_func = base_app.get_command_func(command)
4181+
assert command_func is not None
41824182
base_app.disabled_commands[command] = DisabledCommand(command_func=command_func, help_func=None, completer_func=None)
41834183

41844184
_out, err = run_cmd(base_app, f"help {command}")

0 commit comments

Comments
 (0)