cmd2 2.4.3__py3-none-any.whl → 2.5.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cmd2/__init__.py +2 -6
- cmd2/ansi.py +16 -16
- cmd2/argparse_completer.py +1 -10
- cmd2/argparse_custom.py +27 -81
- cmd2/clipboard.py +3 -22
- cmd2/cmd2.py +652 -358
- cmd2/command_definition.py +43 -8
- cmd2/constants.py +3 -0
- cmd2/decorators.py +135 -128
- cmd2/exceptions.py +0 -1
- cmd2/history.py +52 -23
- cmd2/parsing.py +54 -49
- cmd2/plugin.py +8 -6
- cmd2/py_bridge.py +15 -4
- cmd2/rl_utils.py +57 -23
- cmd2/table_creator.py +1 -0
- cmd2/transcript.py +3 -2
- cmd2/utils.py +56 -24
- {cmd2-2.4.3.dist-info → cmd2-2.5.9.dist-info}/LICENSE +1 -1
- cmd2-2.5.9.dist-info/METADATA +218 -0
- cmd2-2.5.9.dist-info/RECORD +24 -0
- {cmd2-2.4.3.dist-info → cmd2-2.5.9.dist-info}/WHEEL +1 -1
- cmd2-2.4.3.dist-info/METADATA +0 -226
- cmd2-2.4.3.dist-info/RECORD +0 -24
- {cmd2-2.4.3.dist-info → cmd2-2.5.9.dist-info}/top_level.txt +0 -0
cmd2/command_definition.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
"""
|
3
3
|
Supports the definition of commands in separate classes to be composed into cmd2.Cmd
|
4
4
|
"""
|
5
|
+
|
5
6
|
from typing import (
|
6
7
|
TYPE_CHECKING,
|
7
8
|
Callable,
|
@@ -9,6 +10,7 @@ from typing import (
|
|
9
10
|
Mapping,
|
10
11
|
Optional,
|
11
12
|
Type,
|
13
|
+
TypeVar,
|
12
14
|
)
|
13
15
|
|
14
16
|
from .constants import (
|
@@ -29,8 +31,10 @@ if TYPE_CHECKING: # pragma: no cover
|
|
29
31
|
#: Further refinements are needed to define the input parameters
|
30
32
|
CommandFunc = Callable[..., Optional[bool]]
|
31
33
|
|
34
|
+
CommandSetType = TypeVar('CommandSetType', bound=Type['CommandSet'])
|
35
|
+
|
32
36
|
|
33
|
-
def with_default_category(category: str, *, heritable: bool = True) -> Callable[[
|
37
|
+
def with_default_category(category: str, *, heritable: bool = True) -> Callable[[CommandSetType], CommandSetType]:
|
34
38
|
"""
|
35
39
|
Decorator that applies a category to all ``do_*`` command methods in a class that do not already
|
36
40
|
have a category specified.
|
@@ -41,7 +45,7 @@ def with_default_category(category: str, *, heritable: bool = True) -> Callable[
|
|
41
45
|
override the default category.
|
42
46
|
|
43
47
|
If `heritable` is set to False, then only the commands declared locally to this CommandSet will be placed in the
|
44
|
-
specified category. Dynamically created commands
|
48
|
+
specified category. Dynamically created commands and commands declared in sub-classes will not receive this
|
45
49
|
category.
|
46
50
|
|
47
51
|
:param category: category to put all uncategorized commands in
|
@@ -49,7 +53,7 @@ def with_default_category(category: str, *, heritable: bool = True) -> Callable[
|
|
49
53
|
:return: decorator function
|
50
54
|
"""
|
51
55
|
|
52
|
-
def decorate_class(cls:
|
56
|
+
def decorate_class(cls: CommandSetType) -> CommandSetType:
|
53
57
|
if heritable:
|
54
58
|
setattr(cls, CLASS_ATTR_DEFAULT_HELP_CATEGORY, category)
|
55
59
|
|
@@ -91,10 +95,31 @@ class CommandSet(object):
|
|
91
95
|
"""
|
92
96
|
|
93
97
|
def __init__(self) -> None:
|
94
|
-
|
98
|
+
# Private reference to the CLI instance in which this CommandSet running.
|
99
|
+
# This will be set when the CommandSet is registered and it should be
|
100
|
+
# accessed by child classes using the self._cmd property.
|
101
|
+
self.__cmd_internal: Optional[cmd2.Cmd] = None
|
102
|
+
|
95
103
|
self._settables: Dict[str, Settable] = {}
|
96
104
|
self._settable_prefix = self.__class__.__name__
|
97
105
|
|
106
|
+
@property
|
107
|
+
def _cmd(self) -> 'cmd2.Cmd':
|
108
|
+
"""
|
109
|
+
Property for child classes to access self.__cmd_internal.
|
110
|
+
|
111
|
+
Using this property ensures that self.__cmd_internal has been set
|
112
|
+
and it tells type checkers that it's no longer a None type.
|
113
|
+
|
114
|
+
Override this property if you need to change its return type to a
|
115
|
+
child class of Cmd.
|
116
|
+
|
117
|
+
:raises: CommandSetRegistrationError if CommandSet is not registered.
|
118
|
+
"""
|
119
|
+
if self.__cmd_internal is None:
|
120
|
+
raise CommandSetRegistrationError('This CommandSet is not registered')
|
121
|
+
return self.__cmd_internal
|
122
|
+
|
98
123
|
def on_register(self, cmd: 'cmd2.Cmd') -> None:
|
99
124
|
"""
|
100
125
|
Called by cmd2.Cmd as the first step to registering a CommandSet. The commands defined in this class have
|
@@ -102,9 +127,10 @@ class CommandSet(object):
|
|
102
127
|
requiring access to the Cmd object (e.g. configure commands and their parsers based on CLI state data).
|
103
128
|
|
104
129
|
:param cmd: The cmd2 main application
|
130
|
+
:raises: CommandSetRegistrationError if CommandSet is already registered.
|
105
131
|
"""
|
106
|
-
if self.
|
107
|
-
self.
|
132
|
+
if self.__cmd_internal is None:
|
133
|
+
self.__cmd_internal = cmd
|
108
134
|
else:
|
109
135
|
raise CommandSetRegistrationError('This CommandSet has already been registered')
|
110
136
|
|
@@ -128,7 +154,7 @@ class CommandSet(object):
|
|
128
154
|
Called by ``cmd2.Cmd`` after a CommandSet has been unregistered and all its commands removed from the CLI.
|
129
155
|
Subclasses can override this to perform remaining cleanup steps.
|
130
156
|
"""
|
131
|
-
self.
|
157
|
+
self.__cmd_internal = None
|
132
158
|
|
133
159
|
@property
|
134
160
|
def settable_prefix(self) -> str:
|
@@ -144,7 +170,7 @@ class CommandSet(object):
|
|
144
170
|
|
145
171
|
:param settable: Settable object being added
|
146
172
|
"""
|
147
|
-
if self.
|
173
|
+
if self.__cmd_internal is not None:
|
148
174
|
if not self._cmd.always_prefix_settables:
|
149
175
|
if settable.name in self._cmd.settables.keys() and settable.name not in self._settables.keys():
|
150
176
|
raise KeyError(f'Duplicate settable: {settable.name}')
|
@@ -165,3 +191,12 @@ class CommandSet(object):
|
|
165
191
|
del self._settables[name]
|
166
192
|
except KeyError:
|
167
193
|
raise KeyError(name + " is not a settable parameter")
|
194
|
+
|
195
|
+
def sigint_handler(self) -> bool:
|
196
|
+
"""
|
197
|
+
Handle a SIGINT that occurred for a command in this CommandSet.
|
198
|
+
|
199
|
+
:return: True if this completes the interrupt handling and no KeyboardInterrupt will be raised.
|
200
|
+
False to raise a KeyboardInterrupt.
|
201
|
+
"""
|
202
|
+
return False
|
cmd2/constants.py
CHANGED
cmd2/decorators.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# coding=utf-8
|
2
2
|
"""Decorators for ``cmd2`` commands"""
|
3
|
+
|
3
4
|
import argparse
|
4
5
|
from typing import (
|
5
6
|
TYPE_CHECKING,
|
@@ -10,6 +11,8 @@ from typing import (
|
|
10
11
|
Optional,
|
11
12
|
Sequence,
|
12
13
|
Tuple,
|
14
|
+
Type,
|
15
|
+
TypeVar,
|
13
16
|
Union,
|
14
17
|
)
|
15
18
|
|
@@ -29,9 +32,6 @@ from .exceptions import (
|
|
29
32
|
from .parsing import (
|
30
33
|
Statement,
|
31
34
|
)
|
32
|
-
from .utils import (
|
33
|
-
strip_doc_annotations,
|
34
|
-
)
|
35
35
|
|
36
36
|
if TYPE_CHECKING: # pragma: no cover
|
37
37
|
import cmd2
|
@@ -43,15 +43,17 @@ def with_category(category: str) -> Callable[[CommandFunc], CommandFunc]:
|
|
43
43
|
:param category: the name of the category in which this command should
|
44
44
|
be grouped when displaying the list of commands.
|
45
45
|
|
46
|
-
|
46
|
+
Example:
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
```py
|
49
|
+
class MyApp(cmd2.Cmd):
|
50
|
+
@cmd2.with_category('Text Functions')
|
51
|
+
def do_echo(self, args)
|
52
|
+
self.poutput(args)
|
53
|
+
```
|
52
54
|
|
53
55
|
For an alternative approach to categorizing commands using a function, see
|
54
|
-
|
56
|
+
[cmd2.utils.categorize][]
|
55
57
|
"""
|
56
58
|
|
57
59
|
def cat_decorator(func: CommandFunc) -> CommandFunc:
|
@@ -65,16 +67,18 @@ def with_category(category: str) -> Callable[[CommandFunc], CommandFunc]:
|
|
65
67
|
return cat_decorator
|
66
68
|
|
67
69
|
|
70
|
+
CommandParent = TypeVar('CommandParent', bound=Union['cmd2.Cmd', CommandSet])
|
71
|
+
CommandParentType = TypeVar('CommandParentType', bound=Union[Type['cmd2.Cmd'], Type[CommandSet]])
|
72
|
+
|
73
|
+
|
74
|
+
RawCommandFuncOptionalBoolReturn = Callable[[CommandParent, Union[Statement, str]], Optional[bool]]
|
75
|
+
|
76
|
+
|
68
77
|
##########################
|
69
78
|
# The _parse_positionals and _arg_swap functions allow for additional positional args to be preserved
|
70
79
|
# in cmd2 command functions/callables. As long as the 2-ple of arguments we expect to be there can be
|
71
80
|
# found we can swap out the statement with each decorator's specific parameters
|
72
81
|
##########################
|
73
|
-
|
74
|
-
|
75
|
-
RawCommandFuncOptionalBoolReturn = Callable[[Union[CommandSet, 'cmd2.Cmd'], Union[Statement, str]], Optional[bool]]
|
76
|
-
|
77
|
-
|
78
82
|
def _parse_positionals(args: Tuple[Any, ...]) -> Tuple['cmd2.Cmd', Union[Statement, str]]:
|
79
83
|
"""
|
80
84
|
Helper function for cmd2 decorators to inspect the positional arguments until the cmd2.Cmd argument is found
|
@@ -87,7 +91,7 @@ def _parse_positionals(args: Tuple[Any, ...]) -> Tuple['cmd2.Cmd', Union[Stateme
|
|
87
91
|
Cmd,
|
88
92
|
)
|
89
93
|
|
90
|
-
if
|
94
|
+
if isinstance(arg, (Cmd, CommandSet)) and len(args) > pos + 1:
|
91
95
|
if isinstance(arg, CommandSet):
|
92
96
|
arg = arg._cmd
|
93
97
|
next_arg = args[pos + 1]
|
@@ -96,7 +100,7 @@ def _parse_positionals(args: Tuple[Any, ...]) -> Tuple['cmd2.Cmd', Union[Stateme
|
|
96
100
|
|
97
101
|
# This shouldn't happen unless we forget to pass statement in `Cmd.onecmd` or
|
98
102
|
# somehow call the unbound class method.
|
99
|
-
raise TypeError('Expected arguments: cmd: cmd2.Cmd, statement: Union[Statement, str] Not found')
|
103
|
+
raise TypeError('Expected arguments: cmd: cmd2.Cmd, statement: Union[Statement, str] Not found')
|
100
104
|
|
101
105
|
|
102
106
|
def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) -> List[Any]:
|
@@ -114,54 +118,53 @@ def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) ->
|
|
114
118
|
return args_list
|
115
119
|
|
116
120
|
|
117
|
-
#: Function signature for
|
121
|
+
#: Function signature for a command function that accepts a pre-processed argument list from user input
|
118
122
|
#: and optionally returns a boolean
|
119
|
-
ArgListCommandFuncOptionalBoolReturn =
|
120
|
-
|
121
|
-
Callable[[CommandSet, List[str]], Optional[bool]],
|
122
|
-
]
|
123
|
-
#: Function signature for an Command Function that accepts a pre-processed argument list from user input
|
123
|
+
ArgListCommandFuncOptionalBoolReturn = Callable[[CommandParent, List[str]], Optional[bool]]
|
124
|
+
#: Function signature for a command function that accepts a pre-processed argument list from user input
|
124
125
|
#: and returns a boolean
|
125
|
-
ArgListCommandFuncBoolReturn =
|
126
|
-
|
127
|
-
Callable[[CommandSet, List[str]], bool],
|
128
|
-
]
|
129
|
-
#: Function signature for an Command Function that accepts a pre-processed argument list from user input
|
126
|
+
ArgListCommandFuncBoolReturn = Callable[[CommandParent, List[str]], bool]
|
127
|
+
#: Function signature for a command function that accepts a pre-processed argument list from user input
|
130
128
|
#: and returns Nothing
|
131
|
-
ArgListCommandFuncNoneReturn =
|
132
|
-
Callable[['cmd2.Cmd', List[str]], None],
|
133
|
-
Callable[[CommandSet, List[str]], None],
|
134
|
-
]
|
129
|
+
ArgListCommandFuncNoneReturn = Callable[[CommandParent, List[str]], None]
|
135
130
|
|
136
|
-
#: Aggregate of all accepted function signatures for
|
137
|
-
ArgListCommandFunc = Union[
|
131
|
+
#: Aggregate of all accepted function signatures for command functions that accept a pre-processed argument list
|
132
|
+
ArgListCommandFunc = Union[
|
133
|
+
ArgListCommandFuncOptionalBoolReturn[CommandParent],
|
134
|
+
ArgListCommandFuncBoolReturn[CommandParent],
|
135
|
+
ArgListCommandFuncNoneReturn[CommandParent],
|
136
|
+
]
|
138
137
|
|
139
138
|
|
140
139
|
def with_argument_list(
|
141
|
-
func_arg: Optional[ArgListCommandFunc] = None,
|
140
|
+
func_arg: Optional[ArgListCommandFunc[CommandParent]] = None,
|
142
141
|
*,
|
143
142
|
preserve_quotes: bool = False,
|
144
|
-
) -> Union[
|
143
|
+
) -> Union[
|
144
|
+
RawCommandFuncOptionalBoolReturn[CommandParent],
|
145
|
+
Callable[[ArgListCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]],
|
146
|
+
]:
|
145
147
|
"""
|
146
148
|
A decorator to alter the arguments passed to a ``do_*`` method. Default
|
147
149
|
passes a string of whatever the user typed. With this decorator, the
|
148
150
|
decorated method will receive a list of arguments parsed from user input.
|
149
151
|
|
150
|
-
:param func_arg: Single-element positional argument list containing ``
|
152
|
+
:param func_arg: Single-element positional argument list containing ``doi_*`` method
|
151
153
|
this decorator is wrapping
|
152
154
|
:param preserve_quotes: if ``True``, then argument quotes will not be stripped
|
153
155
|
:return: function that gets passed a list of argument strings
|
154
156
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
157
|
+
Example:
|
158
|
+
```py
|
159
|
+
class MyApp(cmd2.Cmd):
|
160
|
+
@cmd2.with_argument_list
|
161
|
+
def do_echo(self, arglist):
|
162
|
+
self.poutput(' '.join(arglist)
|
163
|
+
```
|
161
164
|
"""
|
162
165
|
import functools
|
163
166
|
|
164
|
-
def arg_decorator(func: ArgListCommandFunc) -> RawCommandFuncOptionalBoolReturn:
|
167
|
+
def arg_decorator(func: ArgListCommandFunc[CommandParent]) -> RawCommandFuncOptionalBoolReturn[CommandParent]:
|
165
168
|
"""
|
166
169
|
Decorator function that ingests an Argument List function and returns a raw command function.
|
167
170
|
The returned function will process the raw input into an argument list to be passed to the wrapped function.
|
@@ -191,14 +194,11 @@ def with_argument_list(
|
|
191
194
|
return cmd_wrapper
|
192
195
|
|
193
196
|
if callable(func_arg):
|
194
|
-
# noinspection PyTypeChecker
|
195
197
|
return arg_decorator(func_arg)
|
196
198
|
else:
|
197
|
-
# noinspection PyTypeChecker
|
198
199
|
return arg_decorator
|
199
200
|
|
200
201
|
|
201
|
-
# noinspection PyProtectedMember
|
202
202
|
def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None:
|
203
203
|
"""
|
204
204
|
Recursively set prog attribute of a parser and all of its subparsers so that the root command
|
@@ -209,6 +209,7 @@ def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None:
|
|
209
209
|
"""
|
210
210
|
# Set the prog value for this parser
|
211
211
|
parser.prog = prog
|
212
|
+
req_args: List[str] = []
|
212
213
|
|
213
214
|
# Set the prog value for the parser's subcommands
|
214
215
|
for action in parser._actions:
|
@@ -233,94 +234,107 @@ def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None:
|
|
233
234
|
if subcmd_parser in processed_parsers:
|
234
235
|
continue
|
235
236
|
|
236
|
-
subcmd_prog = parser.prog
|
237
|
+
subcmd_prog = parser.prog
|
238
|
+
if req_args:
|
239
|
+
subcmd_prog += " " + " ".join(req_args)
|
240
|
+
subcmd_prog += " " + subcmd_name
|
237
241
|
_set_parser_prog(subcmd_parser, subcmd_prog)
|
238
242
|
processed_parsers.append(subcmd_parser)
|
239
243
|
|
240
244
|
# We can break since argparse only allows 1 group of subcommands per level
|
241
245
|
break
|
242
246
|
|
247
|
+
# Need to save required args so they can be prepended to the subcommand usage
|
248
|
+
elif action.required:
|
249
|
+
req_args.append(action.dest)
|
243
250
|
|
244
|
-
#: Function signature for a Command Function that uses an argparse.ArgumentParser to process user input
|
245
|
-
#: and optionally returns a boolean
|
246
|
-
ArgparseCommandFuncOptionalBoolReturn = Union[
|
247
|
-
Callable[['cmd2.Cmd', argparse.Namespace], Optional[bool]],
|
248
|
-
Callable[[CommandSet, argparse.Namespace], Optional[bool]],
|
249
|
-
]
|
250
|
-
#: Function signature for a Command Function that uses an argparse.ArgumentParser to process user input
|
251
|
-
#: and returns a boolean
|
252
|
-
ArgparseCommandFuncBoolReturn = Union[
|
253
|
-
Callable[['cmd2.Cmd', argparse.Namespace], bool],
|
254
|
-
Callable[[CommandSet, argparse.Namespace], bool],
|
255
|
-
]
|
256
|
-
#: Function signature for an Command Function that uses an argparse.ArgumentParser to process user input
|
257
|
-
#: and returns nothing
|
258
|
-
ArgparseCommandFuncNoneReturn = Union[
|
259
|
-
Callable[['cmd2.Cmd', argparse.Namespace], None],
|
260
|
-
Callable[[CommandSet, argparse.Namespace], None],
|
261
|
-
]
|
262
251
|
|
263
|
-
#:
|
252
|
+
#: Function signatures for command functions that use an argparse.ArgumentParser to process user input
|
253
|
+
#: and optionally return a boolean
|
254
|
+
ArgparseCommandFuncOptionalBoolReturn = Callable[[CommandParent, argparse.Namespace], Optional[bool]]
|
255
|
+
ArgparseCommandFuncWithUnknownArgsOptionalBoolReturn = Callable[[CommandParent, argparse.Namespace, List[str]], Optional[bool]]
|
256
|
+
|
257
|
+
#: Function signatures for command functions that use an argparse.ArgumentParser to process user input
|
258
|
+
#: and return a boolean
|
259
|
+
ArgparseCommandFuncBoolReturn = Callable[[CommandParent, argparse.Namespace], bool]
|
260
|
+
ArgparseCommandFuncWithUnknownArgsBoolReturn = Callable[[CommandParent, argparse.Namespace, List[str]], bool]
|
261
|
+
|
262
|
+
#: Function signatures for command functions that use an argparse.ArgumentParser to process user input
|
263
|
+
#: and return nothing
|
264
|
+
ArgparseCommandFuncNoneReturn = Callable[[CommandParent, argparse.Namespace], None]
|
265
|
+
ArgparseCommandFuncWithUnknownArgsNoneReturn = Callable[[CommandParent, argparse.Namespace, List[str]], None]
|
266
|
+
|
267
|
+
#: Aggregate of all accepted function signatures for an argparse command function
|
264
268
|
ArgparseCommandFunc = Union[
|
265
|
-
ArgparseCommandFuncOptionalBoolReturn,
|
266
|
-
|
267
|
-
|
269
|
+
ArgparseCommandFuncOptionalBoolReturn[CommandParent],
|
270
|
+
ArgparseCommandFuncWithUnknownArgsOptionalBoolReturn[CommandParent],
|
271
|
+
ArgparseCommandFuncBoolReturn[CommandParent],
|
272
|
+
ArgparseCommandFuncWithUnknownArgsBoolReturn[CommandParent],
|
273
|
+
ArgparseCommandFuncNoneReturn[CommandParent],
|
274
|
+
ArgparseCommandFuncWithUnknownArgsNoneReturn[CommandParent],
|
268
275
|
]
|
269
276
|
|
270
277
|
|
271
278
|
def with_argparser(
|
272
|
-
parser:
|
279
|
+
parser: Union[
|
280
|
+
argparse.ArgumentParser, # existing parser
|
281
|
+
Callable[[], argparse.ArgumentParser], # function or staticmethod
|
282
|
+
Callable[[CommandParentType], argparse.ArgumentParser], # Cmd or CommandSet classmethod
|
283
|
+
],
|
273
284
|
*,
|
274
285
|
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
|
275
286
|
preserve_quotes: bool = False,
|
276
287
|
with_unknown_args: bool = False,
|
277
|
-
) -> Callable[[ArgparseCommandFunc], RawCommandFuncOptionalBoolReturn]:
|
288
|
+
) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]:
|
278
289
|
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
|
279
290
|
with the given instance of argparse.ArgumentParser.
|
280
291
|
|
281
|
-
:param parser: unique instance of ArgumentParser
|
292
|
+
:param parser: unique instance of ArgumentParser or a callable that returns an ArgumentParser
|
282
293
|
:param ns_provider: An optional function that accepts a cmd2.Cmd or cmd2.CommandSet object as an argument and returns an
|
283
294
|
argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that
|
284
295
|
affects parsing.
|
285
296
|
:param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes
|
286
297
|
:param with_unknown_args: if true, then capture unknown args
|
287
298
|
:return: function that gets passed argparse-parsed args in a ``Namespace``
|
288
|
-
A
|
289
|
-
in the ``Namespace`` to provide access to the
|
299
|
+
A [cmd2.argparse_custom.Cmd2AttributeWrapper][] called ``cmd2_statement`` is included
|
300
|
+
in the ``Namespace`` to provide access to the [cmd2.Statement][] object that was created when
|
290
301
|
parsing the command line. This can be useful if the command function needs to know the command line.
|
291
302
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
303
|
+
Example:
|
304
|
+
|
305
|
+
```py
|
306
|
+
parser = cmd2.Cmd2ArgumentParser()
|
307
|
+
parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
|
308
|
+
parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
|
309
|
+
parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
|
310
|
+
parser.add_argument('words', nargs='+', help='words to print')
|
311
|
+
|
312
|
+
class MyApp(cmd2.Cmd):
|
313
|
+
@cmd2.with_argparser(parser, preserve_quotes=True)
|
314
|
+
def do_argprint(self, args):
|
315
|
+
"Print the options and argument list this options command was called with."
|
316
|
+
self.poutput(f'args: {args!r}')
|
317
|
+
```
|
318
|
+
|
319
|
+
Example with unknown args:
|
320
|
+
|
321
|
+
```py
|
322
|
+
parser = cmd2.Cmd2ArgumentParser()
|
323
|
+
parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
|
324
|
+
parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
|
325
|
+
parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
|
326
|
+
|
327
|
+
class MyApp(cmd2.Cmd):
|
328
|
+
@cmd2.with_argparser(parser, with_unknown_args=True)
|
329
|
+
def do_argprint(self, args, unknown):
|
330
|
+
"Print the options and argument list this options command was called with."
|
331
|
+
self.poutput(f'args: {args!r}')
|
332
|
+
self.poutput(f'unknowns: {unknown}')
|
333
|
+
```
|
320
334
|
"""
|
321
335
|
import functools
|
322
336
|
|
323
|
-
def arg_decorator(func: ArgparseCommandFunc) -> RawCommandFuncOptionalBoolReturn:
|
337
|
+
def arg_decorator(func: ArgparseCommandFunc[CommandParent]) -> RawCommandFuncOptionalBoolReturn[CommandParent]:
|
324
338
|
"""
|
325
339
|
Decorator function that ingests an Argparse Command Function and returns a raw command function.
|
326
340
|
The returned function will process the raw input into an argparse Namespace to be passed to the wrapped function.
|
@@ -347,6 +361,12 @@ def with_argparser(
|
|
347
361
|
command_name, statement_arg, preserve_quotes
|
348
362
|
)
|
349
363
|
|
364
|
+
# Pass cmd_wrapper instead of func, since it contains the parser info.
|
365
|
+
arg_parser = cmd2_app._command_parsers.get(cmd_wrapper)
|
366
|
+
if arg_parser is None:
|
367
|
+
# This shouldn't be possible to reach
|
368
|
+
raise ValueError(f'No argument parser found for {command_name}') # pragma: no cover
|
369
|
+
|
350
370
|
if ns_provider is None:
|
351
371
|
namespace = None
|
352
372
|
else:
|
@@ -359,9 +379,9 @@ def with_argparser(
|
|
359
379
|
try:
|
360
380
|
new_args: Union[Tuple[argparse.Namespace], Tuple[argparse.Namespace, List[str]]]
|
361
381
|
if with_unknown_args:
|
362
|
-
new_args =
|
382
|
+
new_args = arg_parser.parse_known_args(parsed_arglist, namespace)
|
363
383
|
else:
|
364
|
-
new_args = (
|
384
|
+
new_args = (arg_parser.parse_args(parsed_arglist, namespace),)
|
365
385
|
ns = new_args[0]
|
366
386
|
except SystemExit:
|
367
387
|
raise Cmd2ArgparseError
|
@@ -381,16 +401,7 @@ def with_argparser(
|
|
381
401
|
args_list = _arg_swap(args, statement_arg, *new_args)
|
382
402
|
return func(*args_list, **kwargs) # type: ignore[call-arg]
|
383
403
|
|
384
|
-
# argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
|
385
404
|
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
|
386
|
-
_set_parser_prog(parser, command_name)
|
387
|
-
|
388
|
-
# If the description has not been set, then use the method docstring if one exists
|
389
|
-
if parser.description is None and func.__doc__:
|
390
|
-
parser.description = strip_doc_annotations(func.__doc__)
|
391
|
-
|
392
|
-
# Set the command's help text as argparser.description (which can be None)
|
393
|
-
cmd_wrapper.__doc__ = parser.description
|
394
405
|
|
395
406
|
# Set some custom attributes for this command
|
396
407
|
setattr(cmd_wrapper, constants.CMD_ATTR_ARGPARSER, parser)
|
@@ -398,18 +409,21 @@ def with_argparser(
|
|
398
409
|
|
399
410
|
return cmd_wrapper
|
400
411
|
|
401
|
-
# noinspection PyTypeChecker
|
402
412
|
return arg_decorator
|
403
413
|
|
404
414
|
|
405
415
|
def as_subcommand_to(
|
406
416
|
command: str,
|
407
417
|
subcommand: str,
|
408
|
-
parser:
|
418
|
+
parser: Union[
|
419
|
+
argparse.ArgumentParser, # existing parser
|
420
|
+
Callable[[], argparse.ArgumentParser], # function or staticmethod
|
421
|
+
Callable[[CommandParentType], argparse.ArgumentParser], # Cmd or CommandSet classmethod
|
422
|
+
],
|
409
423
|
*,
|
410
424
|
help: Optional[str] = None,
|
411
425
|
aliases: Optional[List[str]] = None,
|
412
|
-
) -> Callable[[ArgparseCommandFunc], ArgparseCommandFunc]:
|
426
|
+
) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]:
|
413
427
|
"""
|
414
428
|
Tag this method as a subcommand to an existing argparse decorated command.
|
415
429
|
|
@@ -417,25 +431,19 @@ def as_subcommand_to(
|
|
417
431
|
:param subcommand: Subcommand name
|
418
432
|
:param parser: argparse Parser for this subcommand
|
419
433
|
:param help: Help message for this subcommand which displays in the list of subcommands of the command we are adding to.
|
420
|
-
This is passed as the help argument to
|
434
|
+
This is passed as the help argument to subparsers.add_parser().
|
421
435
|
:param aliases: Alternative names for this subcommand. This is passed as the alias argument to
|
422
|
-
|
436
|
+
subparsers.add_parser().
|
423
437
|
:return: Wrapper function that can receive an argparse.Namespace
|
424
438
|
"""
|
425
439
|
|
426
|
-
def arg_decorator(func: ArgparseCommandFunc) -> ArgparseCommandFunc:
|
427
|
-
_set_parser_prog(parser, command + ' ' + subcommand)
|
428
|
-
|
429
|
-
# If the description has not been set, then use the method docstring if one exists
|
430
|
-
if parser.description is None and func.__doc__:
|
431
|
-
parser.description = func.__doc__
|
432
|
-
|
440
|
+
def arg_decorator(func: ArgparseCommandFunc[CommandParent]) -> ArgparseCommandFunc[CommandParent]:
|
433
441
|
# Set some custom attributes for this command
|
434
442
|
setattr(func, constants.SUBCMD_ATTR_COMMAND, command)
|
435
443
|
setattr(func, constants.CMD_ATTR_ARGPARSER, parser)
|
436
444
|
setattr(func, constants.SUBCMD_ATTR_NAME, subcommand)
|
437
445
|
|
438
|
-
# Keyword arguments for
|
446
|
+
# Keyword arguments for subparsers.add_parser()
|
439
447
|
add_parser_kwargs: Dict[str, Any] = dict()
|
440
448
|
if help is not None:
|
441
449
|
add_parser_kwargs['help'] = help
|
@@ -446,5 +454,4 @@ def as_subcommand_to(
|
|
446
454
|
|
447
455
|
return func
|
448
456
|
|
449
|
-
# noinspection PyTypeChecker
|
450
457
|
return arg_decorator
|