summaryrefslogtreecommitdiff
path: root/cmd2
diff options
context:
space:
mode:
authorEric Lin <anselor@gmail.com>2021-04-03 16:50:38 -0400
committeranselor <anselor@gmail.com>2021-04-06 14:54:28 -0400
commit295ec399a90469d6d80be12171ae03b312cc8837 (patch)
tree4e3d0fa28ff7f7d2a34ae4c3d273444a27be5d93 /cmd2
parente4a4e4520c4384847241c3c50e64d5c9bc19acbf (diff)
downloadcmd2-git-295ec399a90469d6d80be12171ae03b312cc8837.tar.gz
Command function formats defined and decorators.py updated with the defined signatures
Diffstat (limited to 'cmd2')
-rw-r--r--cmd2/cmd2.py1
-rw-r--r--cmd2/decorators.py92
2 files changed, 71 insertions, 22 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 559c01e3..b006789c 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -4348,6 +4348,7 @@ class Cmd(cmd.Cmd):
import tempfile
fd, fname = tempfile.mkstemp(suffix='.txt', text=True)
+ fobj: TextIO
with os.fdopen(fd, 'w') as fobj:
for command in history.values():
if command.statement.multiline_command:
diff --git a/cmd2/decorators.py b/cmd2/decorators.py
index 25c9a892..b62acaf8 100644
--- a/cmd2/decorators.py
+++ b/cmd2/decorators.py
@@ -68,6 +68,9 @@ def with_category(category: str) -> Callable[[CommandFunc], CommandFunc]:
##########################
+RawCommandFuncOptionalBoolReturn = Callable[[Union[CommandSet, 'cmd2.Cmd'], Union[Statement, str]], Optional[bool]]
+
+
def _parse_positionals(args: Tuple[Any, ...]) -> Tuple['cmd2.Cmd', Union[Statement, str]]:
"""
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) ->
return args_list
+#: Function signature for an Command Function that accepts a pre-processed argument list from user input
+#: and optionally returns a boolean
+ArgListCommandFuncOptionalBoolReturn = Union[
+ Callable[['cmd2.Cmd', List[str]], Optional[bool]],
+ Callable[[CommandSet, List[str]], Optional[bool]],
+]
+#: Function signature for an Command Function that accepts a pre-processed argument list from user input
+#: and returns a boolean
+ArgListCommandFuncBoolReturn = Union[
+ Callable[['cmd2.Cmd', List[str]], bool],
+ Callable[[CommandSet, List[str]], bool],
+]
+#: Function signature for an Command Function that accepts a pre-processed argument list from user input
+#: and returns Nothing
+ArgListCommandFuncNoneReturn = Union[
+ Callable[['cmd2.Cmd', List[str]], None],
+ Callable[[CommandSet, List[str]], None],
+]
+
+#: Aggregate of all accepted function signatures for Command Functions that accept a pre-processed argument list
+ArgListCommandFunc = Union[ArgListCommandFuncOptionalBoolReturn, ArgListCommandFuncBoolReturn, ArgListCommandFuncNoneReturn]
+
+
def with_argument_list(
- func_arg: Optional[Callable[[List[str]], Optional[bool]]] = None, *, preserve_quotes: bool = False
-) -> Union[Callable[[List[str]], Optional[bool]]]:
+ func_arg: Optional[ArgListCommandFunc] = None, *, preserve_quotes: bool = False,
+) -> Union[
+ RawCommandFuncOptionalBoolReturn,
+ Callable[[ArgListCommandFunc], RawCommandFuncOptionalBoolReturn]
+]:
"""
A decorator to alter the arguments passed to a ``do_*`` method. Default
passes a string of whatever the user typed. With this decorator, the
@@ -129,7 +158,14 @@ def with_argument_list(
"""
import functools
- def arg_decorator(func: Callable[[List[str]], Optional[bool]]) -> Callable[..., Optional[bool]]:
+ def arg_decorator(func: ArgListCommandFunc) -> RawCommandFuncOptionalBoolReturn:
+ """
+ Decorator function that ingests an Argument List function and returns a raw command function.
+ The returned function will process the raw input into an argument list to be passed to the wrapped function.
+
+ :param func: The defined argument list command function
+ :return: Function that takes raw input and converts to an argument list to pass to the wrapped function.
+ """
@functools.wraps(func)
def cmd_wrapper(*args: Any, **kwargs: Any) -> Optional[bool]:
"""
@@ -144,9 +180,9 @@ def with_argument_list(
cmd2_app, statement = _parse_positionals(args)
_, parsed_arglist = cmd2_app.statement_parser.get_command_arg_list(command_name, statement, preserve_quotes)
args_list = _arg_swap(args, statement, parsed_arglist)
- return func(*args_list, **kwargs)
+ return func(*args_list, **kwargs) # type: ignore[call-arg]
- command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
+ command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX):]
cmd_wrapper.__doc__ = func.__doc__
return cmd_wrapper
@@ -159,7 +195,7 @@ def with_argument_list(
# noinspection PyProtectedMember
-def _set_parser_prog(parser: argparse.ArgumentParser, prog: str):
+def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None:
"""
Recursively set prog attribute of a parser and all of its subparsers so that the root command
is a command name and not sys.argv[0].
@@ -201,19 +237,32 @@ def _set_parser_prog(parser: argparse.ArgumentParser, prog: str):
break
-ArgparseCommandFunc = Union[
+#: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input
+#: and optionally returns a boolean
+ArgparseCommandFuncOptionalBoolReturn = Union[
Callable[['cmd2.Cmd', argparse.Namespace], Optional[bool]],
Callable[[CommandSet, argparse.Namespace], Optional[bool]],
]
+#: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input
+#: and returns a boolean
ArgparseCommandFuncBoolReturn = Union[
Callable[['cmd2.Cmd', argparse.Namespace], bool],
Callable[[CommandSet, argparse.Namespace], bool],
]
+#: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input
+#: and returns nothing
ArgparseCommandFuncNoneReturn = Union[
Callable[['cmd2.Cmd', argparse.Namespace], None],
Callable[[CommandSet, argparse.Namespace], None],
]
+#: Aggregate of all accepted function signatures for an argparse Command Function
+ArgparseCommandFunc = Union[
+ ArgparseCommandFuncOptionalBoolReturn,
+ ArgparseCommandFuncBoolReturn,
+ ArgparseCommandFuncNoneReturn,
+]
+
def with_argparser(
parser: argparse.ArgumentParser,
@@ -221,10 +270,7 @@ def with_argparser(
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
preserve_quotes: bool = False,
with_unknown_args: bool = False,
-) -> Callable[
- [Union[ArgparseCommandFunc, ArgparseCommandFuncNoneReturn, ArgparseCommandFuncBoolReturn]],
- Callable[[Union['cmd2.Cmd', CommandSet], str], Optional[bool]],
-]:
+) -> Callable[[ArgparseCommandFunc], RawCommandFuncOptionalBoolReturn]:
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
with the given instance of argparse.ArgumentParser.
@@ -270,9 +316,15 @@ def with_argparser(
"""
import functools
- def arg_decorator(
- func: Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn],
- ) -> ArgparseCommandFunc:
+ def arg_decorator(func: ArgparseCommandFunc) -> RawCommandFuncOptionalBoolReturn:
+ """
+ Decorator function that ingests an Argparse Command Function and returns a raw command function.
+ The returned function will process the raw input into an argparse Namespace to be passed to the wrapped function.
+
+ :param func: The defined argparse command function
+ :return: Function that takes raw input and converts to an argparse Namespace to passed to the wrapped function.
+ """
+
@functools.wraps(func)
def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
"""
@@ -301,6 +353,7 @@ def with_argparser(
namespace = ns_provider(provider_self if not None else cmd2_app)
try:
+ new_args: Union[Tuple[argparse.Namespace], Tuple[argparse.Namespace, List[str]]]
if with_unknown_args:
new_args = parser.parse_known_args(parsed_arglist, namespace)
else:
@@ -322,7 +375,7 @@ def with_argparser(
delattr(ns, constants.NS_ATTR_SUBCMD_HANDLER)
args_list = _arg_swap(args, statement_arg, *new_args)
- return func(*args_list, **kwargs)
+ return func(*args_list, **kwargs) # type: ignore[call-arg]
# argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
@@ -352,10 +405,7 @@ def as_subcommand_to(
*,
help: Optional[str] = None,
aliases: Optional[List[str]] = None,
-) -> Callable[
- [Union[ArgparseCommandFunc, ArgparseCommandFuncNoneReturn, ArgparseCommandFuncBoolReturn]],
- Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn],
-]:
+) -> Callable[[ArgparseCommandFunc], ArgparseCommandFunc]:
"""
Tag this method as a subcommand to an existing argparse decorated command.
@@ -369,9 +419,7 @@ def as_subcommand_to(
:return: Wrapper function that can receive an argparse.Namespace
"""
- def arg_decorator(
- func: Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn]
- ) -> Union[ArgparseCommandFunc, ArgparseCommandFuncBoolReturn, ArgparseCommandFuncNoneReturn]:
+ def arg_decorator(func: ArgparseCommandFunc) -> ArgparseCommandFunc:
_set_parser_prog(parser, command + ' ' + subcommand)
# If the description has not been set, then use the method docstring if one exists