Skip to content

Commit 295ec39

Browse files
committed
Command function formats defined and decorators.py updated with the defined signatures
1 parent e4a4e45 commit 295ec39

File tree

2 files changed

+71
-22
lines changed

2 files changed

+71
-22
lines changed

cmd2/cmd2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4348,6 +4348,7 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
43484348
import tempfile
43494349

43504350
fd, fname = tempfile.mkstemp(suffix='.txt', text=True)
4351+
fobj: TextIO
43514352
with os.fdopen(fd, 'w') as fobj:
43524353
for command in history.values():
43534354
if command.statement.multiline_command:

cmd2/decorators.py

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ def cat_decorator(func: CommandFunc) -> CommandFunc:
6868
##########################
6969

7070

71+
RawCommandFuncOptionalBoolReturn = Callable[[Union[CommandSet, 'cmd2.Cmd'], Union[Statement, str]], Optional[bool]]
72+
73+
7174
def _parse_positionals(args: Tuple[Any, ...]) -> Tuple['cmd2.Cmd', Union[Statement, str]]:
7275
"""
7376
Helper function for cmd2 decorators to inspect the positional arguments until the cmd2.Cmd argument is found
@@ -107,9 +110,35 @@ def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) ->
107110
return args_list
108111

109112

113+
#: Function signature for an Command Function that accepts a pre-processed argument list from user input
114+
#: and optionally returns a boolean
115+
ArgListCommandFuncOptionalBoolReturn = Union[
116+
Callable[['cmd2.Cmd', List[str]], Optional[bool]],
117+
Callable[[CommandSet, List[str]], Optional[bool]],
118+
]
119+
#: Function signature for an Command Function that accepts a pre-processed argument list from user input
120+
#: and returns a boolean
121+
ArgListCommandFuncBoolReturn = Union[
122+
Callable[['cmd2.Cmd', List[str]], bool],
123+
Callable[[CommandSet, List[str]], bool],
124+
]
125+
#: Function signature for an Command Function that accepts a pre-processed argument list from user input
126+
#: and returns Nothing
127+
ArgListCommandFuncNoneReturn = Union[
128+
Callable[['cmd2.Cmd', List[str]], None],
129+
Callable[[CommandSet, List[str]], None],
130+
]
131+
132+
#: Aggregate of all accepted function signatures for Command Functions that accept a pre-processed argument list
133+
ArgListCommandFunc = Union[ArgListCommandFuncOptionalBoolReturn, ArgListCommandFuncBoolReturn, ArgListCommandFuncNoneReturn]
134+
135+
110136
def with_argument_list(
111-
func_arg: Optional[Callable[[List[str]], Optional[bool]]] = None, *, preserve_quotes: bool = False
112-
) -> Union[Callable[[List[str]], Optional[bool]]]:
137+
func_arg: Optional[ArgListCommandFunc] = None, *, preserve_quotes: bool = False,
138+
) -> Union[
139+
RawCommandFuncOptionalBoolReturn,
140+
Callable[[ArgListCommandFunc], RawCommandFuncOptionalBoolReturn]
141+
]:
113142
"""
114143
A decorator to alter the arguments passed to a ``do_*`` method. Default
115144
passes a string of whatever the user typed. With this decorator, the
@@ -129,7 +158,14 @@ def with_argument_list(
129158
"""
130159
import functools
131160

132-
def arg_decorator(func: Callable[[List[str]], Optional[bool]]) -> Callable[..., Optional[bool]]:
161+
def arg_decorator(func: ArgListCommandFunc) -> RawCommandFuncOptionalBoolReturn:
162+
"""
163+
Decorator function that ingests an Argument List function and returns a raw command function.
164+
The returned function will process the raw input into an argument list to be passed to the wrapped function.
165+
166+
:param func: The defined argument list command function
167+
:return: Function that takes raw input and converts to an argument list to pass to the wrapped function.
168+
"""
133169
@functools.wraps(func)
134170
def cmd_wrapper(*args: Any, **kwargs: Any) -> Optional[bool]:
135171
"""
@@ -144,9 +180,9 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> Optional[bool]:
144180
cmd2_app, statement = _parse_positionals(args)
145181
_, parsed_arglist = cmd2_app.statement_parser.get_command_arg_list(command_name, statement, preserve_quotes)
146182
args_list = _arg_swap(args, statement, parsed_arglist)
147-
return func(*args_list, **kwargs)
183+
return func(*args_list, **kwargs) # type: ignore[call-arg]
148184

149-
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
185+
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX):]
150186
cmd_wrapper.__doc__ = func.__doc__
151187
return cmd_wrapper
152188

@@ -159,7 +195,7 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> Optional[bool]:
159195

160196

161197
# noinspection PyProtectedMember
162-
def _set_parser_prog(parser: argparse.ArgumentParser, prog: str):
198+
def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None:
163199
"""
164200
Recursively set prog attribute of a parser and all of its subparsers so that the root command
165201
is a command name and not sys.argv[0].
@@ -201,30 +237,40 @@ def _set_parser_prog(parser: argparse.ArgumentParser, prog: str):
201237
break
202238

203239

204-
ArgparseCommandFunc = Union[
240+
#: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input
241+
#: and optionally returns a boolean
242+
ArgparseCommandFuncOptionalBoolReturn = Union[
205243
Callable[['cmd2.Cmd', argparse.Namespace], Optional[bool]],
206244
Callable[[CommandSet, argparse.Namespace], Optional[bool]],
207245
]
246+
#: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input
247+
#: and returns a boolean
208248
ArgparseCommandFuncBoolReturn = Union[
209249
Callable[['cmd2.Cmd', argparse.Namespace], bool],
210250
Callable[[CommandSet, argparse.Namespace], bool],
211251
]
252+
#: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input
253+
#: and returns nothing
212254
ArgparseCommandFuncNoneReturn = Union[
213255
Callable[['cmd2.Cmd', argparse.Namespace], None],
214256
Callable[[CommandSet, argparse.Namespace], None],
215257
]
216258

259+
#: Aggregate of all accepted function signatures for an argparse Command Function
260+
ArgparseCommandFunc = Union[
261+
ArgparseCommandFuncOptionalBoolReturn,
262+
ArgparseCommandFuncBoolReturn,
263+
ArgparseCommandFuncNoneReturn,
264+
]
265+
217266

218267
def with_argparser(
219268
parser: argparse.ArgumentParser,
220269
*,
221270
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
222271
preserve_quotes: bool = False,
223272
with_unknown_args: bool = False,
224-
) -> Callable[
225-
[Union[ArgparseCommandFunc, ArgparseCommandFuncNoneReturn, ArgparseCommandFuncBoolReturn]],
226-
Callable[[Union['cmd2.Cmd', CommandSet], str], Optional[bool]],
227-
]:
273+
) -> Callable[[ArgparseCommandFunc], RawCommandFuncOptionalBoolReturn]:
228274
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
229275
with the given instance of argparse.ArgumentParser.
230276
@@ -270,9 +316,15 @@ def with_argparser(
270316
"""
271317
import functools
272318

273-
def arg_decorator(
274-
func: Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn],
275-
) -> ArgparseCommandFunc:
319+
def arg_decorator(func: ArgparseCommandFunc) -> RawCommandFuncOptionalBoolReturn:
320+
"""
321+
Decorator function that ingests an Argparse Command Function and returns a raw command function.
322+
The returned function will process the raw input into an argparse Namespace to be passed to the wrapped function.
323+
324+
:param func: The defined argparse command function
325+
:return: Function that takes raw input and converts to an argparse Namespace to passed to the wrapped function.
326+
"""
327+
276328
@functools.wraps(func)
277329
def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
278330
"""
@@ -301,6 +353,7 @@ def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
301353
namespace = ns_provider(provider_self if not None else cmd2_app)
302354

303355
try:
356+
new_args: Union[Tuple[argparse.Namespace], Tuple[argparse.Namespace, List[str]]]
304357
if with_unknown_args:
305358
new_args = parser.parse_known_args(parsed_arglist, namespace)
306359
else:
@@ -322,7 +375,7 @@ def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
322375
delattr(ns, constants.NS_ATTR_SUBCMD_HANDLER)
323376

324377
args_list = _arg_swap(args, statement_arg, *new_args)
325-
return func(*args_list, **kwargs)
378+
return func(*args_list, **kwargs) # type: ignore[call-arg]
326379

327380
# argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
328381
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
@@ -352,10 +405,7 @@ def as_subcommand_to(
352405
*,
353406
help: Optional[str] = None,
354407
aliases: Optional[List[str]] = None,
355-
) -> Callable[
356-
[Union[ArgparseCommandFunc, ArgparseCommandFuncNoneReturn, ArgparseCommandFuncBoolReturn]],
357-
Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn],
358-
]:
408+
) -> Callable[[ArgparseCommandFunc], ArgparseCommandFunc]:
359409
"""
360410
Tag this method as a subcommand to an existing argparse decorated command.
361411
@@ -369,9 +419,7 @@ def as_subcommand_to(
369419
:return: Wrapper function that can receive an argparse.Namespace
370420
"""
371421

372-
def arg_decorator(
373-
func: Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn]
374-
) -> Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn]:
422+
def arg_decorator(func: ArgparseCommandFunc) -> ArgparseCommandFunc:
375423
_set_parser_prog(parser, command + ' ' + subcommand)
376424

377425
# If the description has not been set, then use the method docstring if one exists

0 commit comments

Comments
 (0)