@@ -290,6 +290,7 @@ def do_build(self, target: str, common: CommonArgs):
290290 TypeVar ,
291291 Union ,
292292 Unpack ,
293+ cast ,
293294 get_args ,
294295 get_origin ,
295296 get_type_hints ,
@@ -321,7 +322,7 @@ def do_build(self, target: str, common: CommonArgs):
321322_NargsValue = int | str | tuple [int ] | tuple [int , int ] | tuple [int , float ]
322323
323324# Parser construction works with a command method before or after descriptor binding.
324- _CommandFunc : TypeAlias = BoundCommandFunc | UnboundCommandFunc [Any , ...]
325+ _CommandFunc : TypeAlias = BoundCommandFunc [...] | UnboundCommandFunc [Any , ...]
325326
326327
327328class Cmd2ParserKwargs (TypedDict , total = False ):
@@ -2880,28 +2881,36 @@ def parser_builder() -> Cmd2ArgumentParser:
28802881_DecoratorParams = ParamSpec ("_DecoratorParams" )
28812882
28822883
2883- class _AnnotatedCommand (Protocol [_CommandParams ]):
2884+ class _AnnotatedCommand (Protocol [CmdOrSetT , _CommandParams ]):
28842885 """A callable command method with the metadata used by ``with_annotated``."""
28852886
28862887 __name__ : str
28872888 __qualname__ : str
28882889
2889- def __call__ (self , * args : _CommandParams .args , ** kwargs : _CommandParams .kwargs ) -> bool | None : ...
2890+ def __call__ (self , __self : CmdOrSetT , / , * args : _CommandParams .args , ** kwargs : _CommandParams .kwargs ) -> bool | None : ...
2891+
2892+ @overload
2893+ def __get__ (self , instance : None , owner : Any = ...) -> "_AnnotatedCommand[CmdOrSetT, _CommandParams]" : ...
2894+
2895+ @overload
2896+ def __get__ (self , instance : CmdOrSetT , owner : Any = ...) -> BoundCommandFunc [_CommandParams ]: ...
28902897
28912898
28922899class _WithAnnotatedDecorator (Protocol ):
28932900 """The signature-preserving decorator ``with_annotated(...)`` returns (generic per call)."""
28942901
2895- def __call__ (self , fn : _AnnotatedCommand [_CommandParams ], / ) -> _AnnotatedCommand [_CommandParams ]: ...
2902+ def __call__ (
2903+ self , fn : _AnnotatedCommand [CmdOrSetT , _CommandParams ], /
2904+ ) -> _AnnotatedCommand [CmdOrSetT , _CommandParams ]: ...
28962905
28972906
28982907def _build_subcommand_handler (
2899- func : _AnnotatedCommand [_CommandParams ],
2908+ func : _AnnotatedCommand [Any , ... ],
29002909 subcommand_to : str ,
29012910 * ,
29022911 base_command : bool = False ,
29032912 options : _ParserBuildOptions ,
2904- ) -> tuple [_AnnotatedCommand [_CommandParams ], str , Callable [[], Cmd2ArgumentParser ]]:
2913+ ) -> tuple [_AnnotatedCommand [Any , ... ], str , Callable [[], Cmd2ArgumentParser ]]:
29052914 """Build a subcommand's parser and a handler that unpacks the Namespace into typed kwargs.
29062915
29072916 :param func: the subcommand handler function
@@ -2927,7 +2936,7 @@ def _build_subcommand_handler(
29272936 _leading_names , _var_positional_name = _var_positional_call_plan (func )
29282937
29292938 @functools .wraps (func )
2930- def handler (* args : _CommandParams . args , ** kwargs : _CommandParams . kwargs ) -> bool | None :
2939+ def handler (* args : Any , ** kwargs : Any ) -> bool | None :
29312940 """Unpack Namespace into typed kwargs for the subcommand handler."""
29322941 if kwargs :
29332942 raise TypeError ("subcommand handlers do not accept keyword arguments" )
@@ -2951,11 +2960,11 @@ def handler(*args: _CommandParams.args, **kwargs: _CommandParams.kwargs) -> bool
29512960 raise TypeError ("annotated command functions must return bool or None" )
29522961
29532962 parser_builder = _make_parser_builder (func , skip_params = _SKIP_PARAMS , base_command = base_command , options = options )
2954- return handler , subcmd_name , parser_builder
2963+ return cast ( _AnnotatedCommand [ Any , ...], handler ) , subcmd_name , parser_builder
29552964
29562965
29572966@overload
2958- def with_annotated (func : _AnnotatedCommand [_CommandParams ], / ) -> _AnnotatedCommand [_CommandParams ]: ...
2967+ def with_annotated (func : _AnnotatedCommand [CmdOrSetT , _CommandParams ], / ) -> _AnnotatedCommand [CmdOrSetT , _CommandParams ]: ...
29592968
29602969
29612970@overload
@@ -2982,7 +2991,7 @@ def with_annotated(
29822991
29832992
29842993def with_annotated (
2985- func : _AnnotatedCommand [_CommandParams ] | None = None ,
2994+ func : _AnnotatedCommand [CmdOrSetT , _CommandParams ] | None = None ,
29862995 * ,
29872996 ns_provider : Callable [..., argparse .Namespace ] | None = None ,
29882997 preserve_quotes : bool = False ,
@@ -3000,7 +3009,7 @@ def with_annotated(
30003009 subcommand_title : str | None = None ,
30013010 subcommand_description : str | None = None ,
30023011 ** parser_kwargs : Unpack [Cmd2ParserKwargs ],
3003- ) -> _AnnotatedCommand [_CommandParams ] | _WithAnnotatedDecorator :
3012+ ) -> _AnnotatedCommand [CmdOrSetT , _CommandParams ] | _WithAnnotatedDecorator :
30043013 """Decorate a ``do_*`` method to build its argparse parser from type annotations.
30053014
30063015 :param func: the command function (when used without parentheses)
@@ -3062,8 +3071,8 @@ def with_annotated(
30623071 )
30633072
30643073 def decorator (
3065- command_func : _AnnotatedCommand [_DecoratorParams ],
3066- ) -> _AnnotatedCommand [_DecoratorParams ]:
3074+ command_func : _AnnotatedCommand [CmdOrSetT , _DecoratorParams ],
3075+ ) -> _AnnotatedCommand [CmdOrSetT , _DecoratorParams ]:
30673076 if with_unknown_args :
30683077 unknown_param = inspect .signature (command_func ).parameters .get ("_unknown" )
30693078 if unknown_param is None :
@@ -3093,7 +3102,7 @@ def decorator(
30933102 parser_source = subcmd_parser_builder ,
30943103 )
30953104 setattr (handler , constants .SUBCOMMAND_ATTR_SPEC , subcommand_spec )
3096- return handler
3105+ return cast ( _AnnotatedCommand [ CmdOrSetT , _DecoratorParams ], handler )
30973106
30983107 command_name = command_func .__name__ [len (constants .COMMAND_FUNC_PREFIX ) :]
30993108
@@ -3170,9 +3179,9 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None:
31703179 )
31713180 setattr (cmd_wrapper , constants .ARGPARSE_COMMAND_ATTR_SPEC , argparse_command_spec )
31723181
3173- return cmd_wrapper
3182+ return cast ( _AnnotatedCommand [ CmdOrSetT , _DecoratorParams ], cmd_wrapper )
31743183
31753184 # Support both @with_annotated and @with_annotated(...)
31763185 if func is not None :
31773186 return decorator (func )
3178- return decorator
3187+ return cast ( _WithAnnotatedDecorator , decorator )
0 commit comments