Skip to content

Commit 24592bc

Browse files
committed
Made type hints of with_category() and as_subcommand_to() more permissive
to better support order-independent stacking with other decorators.
1 parent c6f5a2c commit 24592bc

File tree

1 file changed

+19
-11
lines changed

1 file changed

+19
-11
lines changed

cmd2/decorators.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from typing import (
1010
TYPE_CHECKING,
1111
Any,
12-
ParamSpec,
1312
TypeAlias,
13+
TypeVar,
1414
overload,
1515
)
1616

@@ -28,14 +28,18 @@
2828
if TYPE_CHECKING: # pragma: no cover
2929
from .cmd2 import Cmd
3030

31-
P = ParamSpec("P")
31+
F = TypeVar("F", bound=Callable[..., Any])
3232

3333

3434
def with_category(
3535
category: str,
36-
) -> Callable[[UnboundCommandFunc[CmdOrSetT, P]], UnboundCommandFunc[CmdOrSetT, P]]:
36+
) -> Callable[[F], F]:
3737
"""Decorate a ``do_*`` command function to apply a category.
3838
39+
This decorator has permissive type hints to allow for order-independent stacking
40+
with other decorators that may modify the function signature or return type of the
41+
command function.
42+
3943
:param category: the name of the category in which this command should
4044
be grouped when displaying the list of commands.
4145
:return: a decorator that assigns the specified category to the command function
@@ -53,7 +57,7 @@ def do_echo(self, args)
5357
5458
"""
5559

56-
def cat_decorator(func: UnboundCommandFunc[CmdOrSetT, P]) -> UnboundCommandFunc[CmdOrSetT, P]:
60+
def cat_decorator(func: F) -> F:
5761
from .utils import categorize
5862

5963
categorize(func, category)
@@ -62,10 +66,6 @@ def cat_decorator(func: UnboundCommandFunc[CmdOrSetT, P]) -> UnboundCommandFunc[
6266
return cat_decorator
6367

6468

65-
# The standard cmd2 command function signature (e.g. do_command(self, statement))
66-
RawCommandFunc: TypeAlias = UnboundCommandFunc[CmdOrSetT, [Statement | str]]
67-
68-
6969
##########################
7070
# The _parse_positionals and _arg_swap functions allow for additional positional args to be preserved
7171
# in cmd2 command functions/callables. As long as the 2-ple of arguments we expect to be there can be
@@ -108,6 +108,10 @@ def _arg_swap(args: Sequence[Any], search_arg: Any, *replace_arg: Any) -> list[A
108108
return args_list
109109

110110

111+
# The standard cmd2 command function signature (e.g. do_command(self, statement))
112+
RawCommandFunc: TypeAlias = UnboundCommandFunc[CmdOrSetT, [Statement | str]]
113+
114+
111115
# Function signature for a command function that accepts a pre-processed argument list from user input
112116
ArgListCommandFunc: TypeAlias = UnboundCommandFunc[CmdOrSetT, [list[str]]]
113117

@@ -340,8 +344,12 @@ def as_subcommand_to(
340344
help: str | None = None, # noqa: A002
341345
aliases: Sequence[str] | None = None,
342346
**add_parser_kwargs: Any,
343-
) -> Callable[[ArgparseCommandFunc[CmdOrSetT]], ArgparseCommandFunc[CmdOrSetT]]:
344-
"""Tag this function as a subcommand to an existing argparse decorated command.
347+
) -> Callable[[F], F]:
348+
"""Tag a function as a subcommand to an existing argparse decorated command.
349+
350+
This decorator has permissive type hints to allow for order-independent stacking
351+
with other decorators that may modify the function signature or return type of the
352+
subcommand function.
345353
346354
:param command: Command Name. Space-delimited subcommands may optionally be specified
347355
:param subcommand: Subcommand name
@@ -355,7 +363,7 @@ def as_subcommand_to(
355363
:return: Wrapper function that can receive an argparse.Namespace
356364
"""
357365

358-
def arg_decorator(func: ArgparseCommandFunc[CmdOrSetT]) -> ArgparseCommandFunc[CmdOrSetT]:
366+
def arg_decorator(func: F) -> F:
359367
# Set some custom attributes for this command
360368
setattr(func, constants.SUBCMD_ATTR_COMMAND, command)
361369
setattr(func, constants.CMD_ATTR_ARGPARSER, parser)

0 commit comments

Comments
 (0)