summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Lin <anselor@gmail.com>2020-08-04 13:46:48 -0400
committeranselor <anselor@gmail.com>2020-08-04 15:24:53 -0400
commit1a82b37b6c23490b1d9127095e3ee4f2db21a85b (patch)
treeeb45b08c177ec9d032436af5d158a0ad065dc967
parentc983abaa881a3ac6110a90194def660489b2d5cc (diff)
downloadcmd2-git-1a82b37b6c23490b1d9127095e3ee4f2db21a85b.tar.gz
Marked with_arparser_and_unknown_args deprecated and consolidated
implementation as an option to with_argparser instead.
-rw-r--r--CHANGELOG.md7
-rw-r--r--cmd2/decorators.py97
-rw-r--r--docs/features/argument_processing.rst2
-rwxr-xr-xexamples/arg_print.py2
-rw-r--r--examples/modular_commands/commandset_complex.py2
-rwxr-xr-xexamples/python_scripting.py2
-rw-r--r--isolated_tests/test_commandset/test_commandset.py2
-rw-r--r--tests/test_argparse.py6
-rwxr-xr-xtests/test_cmd2.py4
-rwxr-xr-xtests/test_completion.py2
-rw-r--r--tests/test_transcript.py5
11 files changed, 52 insertions, 79 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3dfb1f8..b358e4d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,10 @@
## 1.3.0 (August 4, 2020)
-* Enchancements
+* Enhancements
* Added CommandSet - Enables defining a separate loadable module of commands to register/unregister
- with your cmd2 application.
+ with your cmd2 application.
+* Other
+ * Marked with_argparser_and_unknown_args pending deprecation and consolidated implementation into
+ with_argparser
## 1.2.1 (July 14, 2020)
* Bug Fixes
diff --git a/cmd2/decorators.py b/cmd2/decorators.py
index 5947020f..7fee3295 100644
--- a/cmd2/decorators.py
+++ b/cmd2/decorators.py
@@ -172,7 +172,10 @@ def with_argparser_and_unknown_args(parser: argparse.ArgumentParser, *,
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
preserve_quotes: bool = False) -> \
Callable[[argparse.Namespace, List], Optional[bool]]:
- """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing
+ """
+ Deprecated decorator. Use `with_argparser(parser, with_unknown_args=True)` instead.
+
+ A decorator to alter a cmd2 method to populate its ``args`` argument by parsing
arguments with the given instance of argparse.ArgumentParser, but also returning
unknown args as a list.
@@ -194,77 +197,23 @@ def with_argparser_and_unknown_args(parser: argparse.ArgumentParser, *,
>>> parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
>>>
>>> class MyApp(cmd2.Cmd):
- >>> @cmd2.with_argparser_and_unknown_args(parser)
+ >>> @cmd2.with_argparser(parser, with_unknown_args=True)
>>> def do_argprint(self, args, unknown):
>>> "Print the options and argument list this options command was called with."
>>> self.poutput('args: {!r}'.format(args))
>>> self.poutput('unknowns: {}'.format(unknown))
"""
- import functools
-
- def arg_decorator(func: Callable):
- @functools.wraps(func)
- def cmd_wrapper(*args: Tuple[Any, ...], **kwargs: Dict[str, Any]) -> Optional[bool]:
- """
- Command function wrapper which translates command line into argparse Namespace and calls actual
- command function
-
- :param args: All positional arguments to this function. We're expecting there to be:
- cmd2_app, statement: Union[Statement, str]
- contiguously somewhere in the list
- :param kwargs: any keyword arguments being passed to command function
- :return: return value of command function
- :raises: Cmd2ArgparseError if argparse has error parsing command line
- """
- cmd2_app, statement = _parse_positionals(args)
- statement, parsed_arglist = cmd2_app.statement_parser.get_command_arg_list(command_name,
- statement,
- preserve_quotes)
-
- if ns_provider is None:
- namespace = None
- else:
- namespace = ns_provider(cmd2_app)
-
- try:
- ns, unknown = parser.parse_known_args(parsed_arglist, namespace)
- except SystemExit:
- raise Cmd2ArgparseError
- else:
- setattr(ns, '__statement__', statement)
+ import warnings
+ warnings.warn('This decorator will be deprecated. Use `with_argparser(parser, with_unknown_args=True)`.',
+ PendingDeprecationWarning, stacklevel=2)
- def get_handler(self: argparse.Namespace) -> Optional[Callable]:
- return getattr(self, constants.SUBCMD_HANDLER, None)
-
- setattr(ns, 'get_handler', types.MethodType(get_handler, ns))
-
- args_list = _arg_swap(args, statement, ns, unknown)
- return func(*args_list, **kwargs)
-
- # 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):]
- _set_parser_prog(parser, command_name)
-
- # If the description has not been set, then use the method docstring if one exists
- if parser.description is None and func.__doc__:
- parser.description = func.__doc__
-
- # Set the command's help text as argparser.description (which can be None)
- cmd_wrapper.__doc__ = parser.description
-
- # Set some custom attributes for this command
- setattr(cmd_wrapper, constants.CMD_ATTR_ARGPARSER, parser)
- setattr(cmd_wrapper, constants.CMD_ATTR_PRESERVE_QUOTES, preserve_quotes)
-
- return cmd_wrapper
-
- # noinspection PyTypeChecker
- return arg_decorator
+ return with_argparser(parser, ns_provider=ns_provider, preserve_quotes=preserve_quotes, with_unknown_args=True)
def with_argparser(parser: argparse.ArgumentParser, *,
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
- preserve_quotes: bool = False) -> Callable[[argparse.Namespace], Optional[bool]]:
+ preserve_quotes: bool = False,
+ with_unknown_args: bool = False) -> Callable[[argparse.Namespace], Optional[bool]]:
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
with the given instance of argparse.ArgumentParser.
@@ -273,6 +222,7 @@ def with_argparser(parser: argparse.ArgumentParser, *,
argparse.Namespace. This is useful if the Namespace needs to be prepopulated with
state data that affects parsing.
:param preserve_quotes: if True, then arguments passed to argparse maintain their quotes
+ :param with_unknown_args: if true, then capture unknown args
:return: function that gets passed the argparse-parsed args in a Namespace
A member called __statement__ is added to the Namespace to provide command functions access to the
Statement object. This can be useful if the command function needs to know the command line.
@@ -290,6 +240,21 @@ def with_argparser(parser: argparse.ArgumentParser, *,
>>> def do_argprint(self, args):
>>> "Print the options and argument list this options command was called with."
>>> self.poutput('args: {!r}'.format(args))
+
+ :Example with unknown args:
+
+ >>> parser = argparse.ArgumentParser()
+ >>> parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
+ >>> parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
+ >>> parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
+ >>>
+ >>> class MyApp(cmd2.Cmd):
+ >>> @cmd2.with_argparser(parser, with_unknown_args=True)
+ >>> def do_argprint(self, args, unknown):
+ >>> "Print the options and argument list this options command was called with."
+ >>> self.poutput('args: {!r}'.format(args))
+ >>> self.poutput('unknowns: {}'.format(unknown))
+
"""
import functools
@@ -318,7 +283,11 @@ def with_argparser(parser: argparse.ArgumentParser, *,
namespace = ns_provider(cmd2_app)
try:
- ns = parser.parse_args(parsed_arglist, namespace)
+ if with_unknown_args:
+ new_args = parser.parse_known_args(parsed_arglist, namespace)
+ else:
+ new_args = (parser.parse_args(parsed_arglist, namespace), )
+ ns = new_args[0]
except SystemExit:
raise Cmd2ArgparseError
else:
@@ -329,7 +298,7 @@ def with_argparser(parser: argparse.ArgumentParser, *,
setattr(ns, 'get_handler', types.MethodType(get_handler, ns))
- args_list = _arg_swap(args, statement, ns)
+ args_list = _arg_swap(args, statement, *new_args)
return func(*args_list, **kwargs)
# argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
diff --git a/docs/features/argument_processing.rst b/docs/features/argument_processing.rst
index f98a686a..9e65742e 100644
--- a/docs/features/argument_processing.rst
+++ b/docs/features/argument_processing.rst
@@ -272,7 +272,7 @@ Here's what it looks like::
dir_parser = argparse.ArgumentParser()
dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line")
- @with_argparser_and_unknown_args(dir_parser)
+ @with_argparser(dir_parser, with_unknown_args=True)
def do_dir(self, args, unknown):
"""List contents of current directory."""
# No arguments for this command
diff --git a/examples/arg_print.py b/examples/arg_print.py
index 3f7f3815..dbf740ff 100755
--- a/examples/arg_print.py
+++ b/examples/arg_print.py
@@ -56,7 +56,7 @@ class ArgumentAndOptionPrinter(cmd2.Cmd):
pprint_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
pprint_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
- @cmd2.with_argparser_and_unknown_args(pprint_parser)
+ @cmd2.with_argparser(pprint_parser, with_unknown_args=True)
def do_pprint(self, args, unknown):
"""Print the options and argument list this options command was called with."""
self.poutput('oprint was called with the following\n\toptions: {!r}\n\targuments: {}'.format(args, unknown))
diff --git a/examples/modular_commands/commandset_complex.py b/examples/modular_commands/commandset_complex.py
index 5a031bd0..ec5a9e13 100644
--- a/examples/modular_commands/commandset_complex.py
+++ b/examples/modular_commands/commandset_complex.py
@@ -23,7 +23,7 @@ class CommandSetA(cmd2.CommandSet):
cranberry_parser = cmd2.Cmd2ArgumentParser('cranberry')
cranberry_parser.add_argument('arg1', choices=['lemonade', 'juice', 'sauce'])
- @cmd2.with_argparser_and_unknown_args(cranberry_parser)
+ @cmd2.with_argparser(cranberry_parser, with_unknown_args=True)
def do_cranberry(self, cmd: cmd2.Cmd, ns: argparse.Namespace, unknown: List[str]):
cmd.poutput('Cranberry {}!!'.format(ns.arg1))
if unknown and len(unknown):
diff --git a/examples/python_scripting.py b/examples/python_scripting.py
index 69cfb672..6e4295d4 100755
--- a/examples/python_scripting.py
+++ b/examples/python_scripting.py
@@ -96,7 +96,7 @@ class CmdLineApp(cmd2.Cmd):
dir_parser = argparse.ArgumentParser()
dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line")
- @cmd2.with_argparser_and_unknown_args(dir_parser)
+ @cmd2.with_argparser(dir_parser, with_unknown_args=True)
def do_dir(self, args, unknown):
"""List contents of current directory."""
# No arguments for this command
diff --git a/isolated_tests/test_commandset/test_commandset.py b/isolated_tests/test_commandset/test_commandset.py
index 90f0448c..83ae4646 100644
--- a/isolated_tests/test_commandset/test_commandset.py
+++ b/isolated_tests/test_commandset/test_commandset.py
@@ -28,7 +28,7 @@ class CommandSetA(cmd2.CommandSet):
cranberry_parser = cmd2.Cmd2ArgumentParser('cranberry')
cranberry_parser.add_argument('arg1', choices=['lemonade', 'juice', 'sauce'])
- @cmd2.with_argparser_and_unknown_args(cranberry_parser)
+ @cmd2.with_argparser(cranberry_parser, with_unknown_args=True)
def do_cranberry(self, cmd: cmd2.Cmd, ns: argparse.Namespace, unknown: List[str]):
cmd.poutput('Cranberry {}!!'.format(ns.arg1))
if unknown and len(unknown):
diff --git a/tests/test_argparse.py b/tests/test_argparse.py
index daf43497..1334f9e3 100644
--- a/tests/test_argparse.py
+++ b/tests/test_argparse.py
@@ -92,7 +92,7 @@ class ArgparseApp(cmd2.Cmd):
known_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
known_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
known_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
- @cmd2.with_argparser_and_unknown_args(known_parser)
+ @cmd2.with_argparser(known_parser, with_unknown_args=True)
def do_speak(self, args, extra, *, keyword_arg: Optional[str] = None):
"""Repeat what you tell me to."""
words = []
@@ -112,11 +112,11 @@ class ArgparseApp(cmd2.Cmd):
if keyword_arg is not None:
print(keyword_arg)
- @cmd2.with_argparser_and_unknown_args(argparse.ArgumentParser(), preserve_quotes=True)
+ @cmd2.with_argparser(argparse.ArgumentParser(), preserve_quotes=True, with_unknown_args=True)
def do_test_argparse_with_list_quotes(self, args, extra):
self.stdout.write('{}'.format(' '.join(extra)))
- @cmd2.with_argparser_and_unknown_args(argparse.ArgumentParser(), ns_provider=namespace_provider)
+ @cmd2.with_argparser(argparse.ArgumentParser(), ns_provider=namespace_provider, with_unknown_args=True)
def do_test_argparse_with_list_ns(self, args, extra):
self.stdout.write('{}'.format(args.custom_stuff))
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index bc0e0a94..8688e124 100755
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -1224,7 +1224,7 @@ def test_select_ctrl_c(outsim_app, monkeypatch, capsys):
class HelpNoDocstringApp(cmd2.Cmd):
greet_parser = argparse.ArgumentParser()
greet_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
- @cmd2.with_argparser_and_unknown_args(greet_parser)
+ @cmd2.with_argparser(greet_parser, with_unknown_args=True)
def do_greet(self, opts, arg):
arg = ''.join(arg)
if opts.shout:
@@ -1268,7 +1268,7 @@ class MultilineApp(cmd2.Cmd):
orate_parser = argparse.ArgumentParser()
orate_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
- @cmd2.with_argparser_and_unknown_args(orate_parser)
+ @cmd2.with_argparser(orate_parser, with_unknown_args=True)
def do_orate(self, opts, arg):
arg = ''.join(arg)
if opts.shout:
diff --git a/tests/test_completion.py b/tests/test_completion.py
index a380d43a..48a055d0 100755
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -1132,7 +1132,7 @@ class SubcommandsWithUnknownExample(cmd2.Cmd):
parser_sport = base_subparsers.add_parser('sport', help='sport help')
sport_arg = parser_sport.add_argument('sport', help='Enter name of a sport', choices=sport_item_strs)
- @cmd2.with_argparser_and_unknown_args(base_parser)
+ @cmd2.with_argparser(base_parser, with_unknown_args=True)
def do_base(self, args):
"""Base command help"""
func = getattr(args, 'func', None)
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index 69389b7f..55d60e18 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -41,7 +41,7 @@ class CmdLineApp(cmd2.Cmd):
speak_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
speak_parser.add_argument('-r', '--repeat', type=int, help="output [n] times")
- @cmd2.with_argparser_and_unknown_args(speak_parser)
+ @cmd2.with_argparser(speak_parser, with_unknown_args=True)
def do_speak(self, opts, arg):
"""Repeats what you tell me to."""
arg = ' '.join(arg)
@@ -61,7 +61,8 @@ class CmdLineApp(cmd2.Cmd):
mumble_parser = argparse.ArgumentParser()
mumble_parser.add_argument('-r', '--repeat', type=int, help="output [n] times")
- @cmd2.with_argparser_and_unknown_args(mumble_parser)
+
+ @cmd2.with_argparser(mumble_parser, with_unknown_args=True)
def do_mumble(self, opts, arg):
"""Mumbles what you tell me to."""
repetitions = opts.repeat or 1