Skip to content

Commit 1a82b37

Browse files
committed
Marked with_arparser_and_unknown_args deprecated and consolidated
implementation as an option to with_argparser instead.
1 parent c983aba commit 1a82b37

File tree

11 files changed

+52
-79
lines changed

11 files changed

+52
-79
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
## 1.3.0 (August 4, 2020)
2-
* Enchancements
2+
* Enhancements
33
* Added CommandSet - Enables defining a separate loadable module of commands to register/unregister
4-
with your cmd2 application.
4+
with your cmd2 application.
5+
* Other
6+
* Marked with_argparser_and_unknown_args pending deprecation and consolidated implementation into
7+
with_argparser
58

69
## 1.2.1 (July 14, 2020)
710
* Bug Fixes

cmd2/decorators.py

Lines changed: 33 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,10 @@ def with_argparser_and_unknown_args(parser: argparse.ArgumentParser, *,
172172
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
173173
preserve_quotes: bool = False) -> \
174174
Callable[[argparse.Namespace, List], Optional[bool]]:
175-
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing
175+
"""
176+
Deprecated decorator. Use `with_argparser(parser, with_unknown_args=True)` instead.
177+
178+
A decorator to alter a cmd2 method to populate its ``args`` argument by parsing
176179
arguments with the given instance of argparse.ArgumentParser, but also returning
177180
unknown args as a list.
178181
@@ -194,77 +197,23 @@ def with_argparser_and_unknown_args(parser: argparse.ArgumentParser, *,
194197
>>> parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
195198
>>>
196199
>>> class MyApp(cmd2.Cmd):
197-
>>> @cmd2.with_argparser_and_unknown_args(parser)
200+
>>> @cmd2.with_argparser(parser, with_unknown_args=True)
198201
>>> def do_argprint(self, args, unknown):
199202
>>> "Print the options and argument list this options command was called with."
200203
>>> self.poutput('args: {!r}'.format(args))
201204
>>> self.poutput('unknowns: {}'.format(unknown))
202205
"""
203-
import functools
204-
205-
def arg_decorator(func: Callable):
206-
@functools.wraps(func)
207-
def cmd_wrapper(*args: Tuple[Any, ...], **kwargs: Dict[str, Any]) -> Optional[bool]:
208-
"""
209-
Command function wrapper which translates command line into argparse Namespace and calls actual
210-
command function
211-
212-
:param args: All positional arguments to this function. We're expecting there to be:
213-
cmd2_app, statement: Union[Statement, str]
214-
contiguously somewhere in the list
215-
:param kwargs: any keyword arguments being passed to command function
216-
:return: return value of command function
217-
:raises: Cmd2ArgparseError if argparse has error parsing command line
218-
"""
219-
cmd2_app, statement = _parse_positionals(args)
220-
statement, parsed_arglist = cmd2_app.statement_parser.get_command_arg_list(command_name,
221-
statement,
222-
preserve_quotes)
223-
224-
if ns_provider is None:
225-
namespace = None
226-
else:
227-
namespace = ns_provider(cmd2_app)
228-
229-
try:
230-
ns, unknown = parser.parse_known_args(parsed_arglist, namespace)
231-
except SystemExit:
232-
raise Cmd2ArgparseError
233-
else:
234-
setattr(ns, '__statement__', statement)
206+
import warnings
207+
warnings.warn('This decorator will be deprecated. Use `with_argparser(parser, with_unknown_args=True)`.',
208+
PendingDeprecationWarning, stacklevel=2)
235209

236-
def get_handler(self: argparse.Namespace) -> Optional[Callable]:
237-
return getattr(self, constants.SUBCMD_HANDLER, None)
238-
239-
setattr(ns, 'get_handler', types.MethodType(get_handler, ns))
240-
241-
args_list = _arg_swap(args, statement, ns, unknown)
242-
return func(*args_list, **kwargs)
243-
244-
# argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
245-
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX):]
246-
_set_parser_prog(parser, command_name)
247-
248-
# If the description has not been set, then use the method docstring if one exists
249-
if parser.description is None and func.__doc__:
250-
parser.description = func.__doc__
251-
252-
# Set the command's help text as argparser.description (which can be None)
253-
cmd_wrapper.__doc__ = parser.description
254-
255-
# Set some custom attributes for this command
256-
setattr(cmd_wrapper, constants.CMD_ATTR_ARGPARSER, parser)
257-
setattr(cmd_wrapper, constants.CMD_ATTR_PRESERVE_QUOTES, preserve_quotes)
258-
259-
return cmd_wrapper
260-
261-
# noinspection PyTypeChecker
262-
return arg_decorator
210+
return with_argparser(parser, ns_provider=ns_provider, preserve_quotes=preserve_quotes, with_unknown_args=True)
263211

264212

265213
def with_argparser(parser: argparse.ArgumentParser, *,
266214
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
267-
preserve_quotes: bool = False) -> Callable[[argparse.Namespace], Optional[bool]]:
215+
preserve_quotes: bool = False,
216+
with_unknown_args: bool = False) -> Callable[[argparse.Namespace], Optional[bool]]:
268217
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
269218
with the given instance of argparse.ArgumentParser.
270219
@@ -273,6 +222,7 @@ def with_argparser(parser: argparse.ArgumentParser, *,
273222
argparse.Namespace. This is useful if the Namespace needs to be prepopulated with
274223
state data that affects parsing.
275224
:param preserve_quotes: if True, then arguments passed to argparse maintain their quotes
225+
:param with_unknown_args: if true, then capture unknown args
276226
:return: function that gets passed the argparse-parsed args in a Namespace
277227
A member called __statement__ is added to the Namespace to provide command functions access to the
278228
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, *,
290240
>>> def do_argprint(self, args):
291241
>>> "Print the options and argument list this options command was called with."
292242
>>> self.poutput('args: {!r}'.format(args))
243+
244+
:Example with unknown args:
245+
246+
>>> parser = argparse.ArgumentParser()
247+
>>> parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
248+
>>> parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
249+
>>> parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
250+
>>>
251+
>>> class MyApp(cmd2.Cmd):
252+
>>> @cmd2.with_argparser(parser, with_unknown_args=True)
253+
>>> def do_argprint(self, args, unknown):
254+
>>> "Print the options and argument list this options command was called with."
255+
>>> self.poutput('args: {!r}'.format(args))
256+
>>> self.poutput('unknowns: {}'.format(unknown))
257+
293258
"""
294259
import functools
295260

@@ -318,7 +283,11 @@ def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]:
318283
namespace = ns_provider(cmd2_app)
319284

320285
try:
321-
ns = parser.parse_args(parsed_arglist, namespace)
286+
if with_unknown_args:
287+
new_args = parser.parse_known_args(parsed_arglist, namespace)
288+
else:
289+
new_args = (parser.parse_args(parsed_arglist, namespace), )
290+
ns = new_args[0]
322291
except SystemExit:
323292
raise Cmd2ArgparseError
324293
else:
@@ -329,7 +298,7 @@ def get_handler(self: argparse.Namespace) -> Optional[Callable]:
329298

330299
setattr(ns, 'get_handler', types.MethodType(get_handler, ns))
331300

332-
args_list = _arg_swap(args, statement, ns)
301+
args_list = _arg_swap(args, statement, *new_args)
333302
return func(*args_list, **kwargs)
334303

335304
# argparser defaults the program name to sys.argv[0], but we want it to be the name of our command

docs/features/argument_processing.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ Here's what it looks like::
272272
dir_parser = argparse.ArgumentParser()
273273
dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line")
274274

275-
@with_argparser_and_unknown_args(dir_parser)
275+
@with_argparser(dir_parser, with_unknown_args=True)
276276
def do_dir(self, args, unknown):
277277
"""List contents of current directory."""
278278
# No arguments for this command

examples/arg_print.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def do_oprint(self, args):
5656
pprint_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
5757
pprint_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
5858

59-
@cmd2.with_argparser_and_unknown_args(pprint_parser)
59+
@cmd2.with_argparser(pprint_parser, with_unknown_args=True)
6060
def do_pprint(self, args, unknown):
6161
"""Print the options and argument list this options command was called with."""
6262
self.poutput('oprint was called with the following\n\toptions: {!r}\n\targuments: {}'.format(args, unknown))

examples/modular_commands/commandset_complex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def do_banana(self, cmd: cmd2.Cmd, statement: cmd2.Statement):
2323
cranberry_parser = cmd2.Cmd2ArgumentParser('cranberry')
2424
cranberry_parser.add_argument('arg1', choices=['lemonade', 'juice', 'sauce'])
2525

26-
@cmd2.with_argparser_and_unknown_args(cranberry_parser)
26+
@cmd2.with_argparser(cranberry_parser, with_unknown_args=True)
2727
def do_cranberry(self, cmd: cmd2.Cmd, ns: argparse.Namespace, unknown: List[str]):
2828
cmd.poutput('Cranberry {}!!'.format(ns.arg1))
2929
if unknown and len(unknown):

examples/python_scripting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def complete_cd(self, text, line, begidx, endidx):
9696
dir_parser = argparse.ArgumentParser()
9797
dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line")
9898

99-
@cmd2.with_argparser_and_unknown_args(dir_parser)
99+
@cmd2.with_argparser(dir_parser, with_unknown_args=True)
100100
def do_dir(self, args, unknown):
101101
"""List contents of current directory."""
102102
# No arguments for this command

isolated_tests/test_commandset/test_commandset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def do_banana(self, cmd: cmd2.Cmd, statement: cmd2.Statement):
2828
cranberry_parser = cmd2.Cmd2ArgumentParser('cranberry')
2929
cranberry_parser.add_argument('arg1', choices=['lemonade', 'juice', 'sauce'])
3030

31-
@cmd2.with_argparser_and_unknown_args(cranberry_parser)
31+
@cmd2.with_argparser(cranberry_parser, with_unknown_args=True)
3232
def do_cranberry(self, cmd: cmd2.Cmd, ns: argparse.Namespace, unknown: List[str]):
3333
cmd.poutput('Cranberry {}!!'.format(ns.arg1))
3434
if unknown and len(unknown):

tests/test_argparse.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def do_preservelist(self, arglist):
9292
known_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
9393
known_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
9494
known_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
95-
@cmd2.with_argparser_and_unknown_args(known_parser)
95+
@cmd2.with_argparser(known_parser, with_unknown_args=True)
9696
def do_speak(self, args, extra, *, keyword_arg: Optional[str] = None):
9797
"""Repeat what you tell me to."""
9898
words = []
@@ -112,11 +112,11 @@ def do_speak(self, args, extra, *, keyword_arg: Optional[str] = None):
112112
if keyword_arg is not None:
113113
print(keyword_arg)
114114

115-
@cmd2.with_argparser_and_unknown_args(argparse.ArgumentParser(), preserve_quotes=True)
115+
@cmd2.with_argparser(argparse.ArgumentParser(), preserve_quotes=True, with_unknown_args=True)
116116
def do_test_argparse_with_list_quotes(self, args, extra):
117117
self.stdout.write('{}'.format(' '.join(extra)))
118118

119-
@cmd2.with_argparser_and_unknown_args(argparse.ArgumentParser(), ns_provider=namespace_provider)
119+
@cmd2.with_argparser(argparse.ArgumentParser(), ns_provider=namespace_provider, with_unknown_args=True)
120120
def do_test_argparse_with_list_ns(self, args, extra):
121121
self.stdout.write('{}'.format(args.custom_stuff))
122122

tests/test_cmd2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,7 @@ def test_select_ctrl_c(outsim_app, monkeypatch, capsys):
12241224
class HelpNoDocstringApp(cmd2.Cmd):
12251225
greet_parser = argparse.ArgumentParser()
12261226
greet_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
1227-
@cmd2.with_argparser_and_unknown_args(greet_parser)
1227+
@cmd2.with_argparser(greet_parser, with_unknown_args=True)
12281228
def do_greet(self, opts, arg):
12291229
arg = ''.join(arg)
12301230
if opts.shout:
@@ -1268,7 +1268,7 @@ def __init__(self, *args, **kwargs):
12681268
orate_parser = argparse.ArgumentParser()
12691269
orate_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
12701270

1271-
@cmd2.with_argparser_and_unknown_args(orate_parser)
1271+
@cmd2.with_argparser(orate_parser, with_unknown_args=True)
12721272
def do_orate(self, opts, arg):
12731273
arg = ''.join(arg)
12741274
if opts.shout:

tests/test_completion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1132,7 +1132,7 @@ def base_sport(self, args):
11321132
parser_sport = base_subparsers.add_parser('sport', help='sport help')
11331133
sport_arg = parser_sport.add_argument('sport', help='Enter name of a sport', choices=sport_item_strs)
11341134

1135-
@cmd2.with_argparser_and_unknown_args(base_parser)
1135+
@cmd2.with_argparser(base_parser, with_unknown_args=True)
11361136
def do_base(self, args):
11371137
"""Base command help"""
11381138
func = getattr(args, 'func', None)

tests/test_transcript.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __init__(self, *args, **kwargs):
4141
speak_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
4242
speak_parser.add_argument('-r', '--repeat', type=int, help="output [n] times")
4343

44-
@cmd2.with_argparser_and_unknown_args(speak_parser)
44+
@cmd2.with_argparser(speak_parser, with_unknown_args=True)
4545
def do_speak(self, opts, arg):
4646
"""Repeats what you tell me to."""
4747
arg = ' '.join(arg)
@@ -61,7 +61,8 @@ def do_speak(self, opts, arg):
6161

6262
mumble_parser = argparse.ArgumentParser()
6363
mumble_parser.add_argument('-r', '--repeat', type=int, help="output [n] times")
64-
@cmd2.with_argparser_and_unknown_args(mumble_parser)
64+
65+
@cmd2.with_argparser(mumble_parser, with_unknown_args=True)
6566
def do_mumble(self, opts, arg):
6667
"""Mumbles what you tell me to."""
6768
repetitions = opts.repeat or 1

0 commit comments

Comments
 (0)