cmd2 2.6.2__py3-none-any.whl → 3.0.0b1__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 +41 -38
- cmd2/argparse_completer.py +80 -81
- cmd2/argparse_custom.py +359 -151
- cmd2/clipboard.py +1 -1
- cmd2/cmd2.py +1272 -845
- cmd2/colors.py +270 -0
- cmd2/command_definition.py +13 -5
- cmd2/constants.py +0 -6
- cmd2/decorators.py +41 -104
- cmd2/exceptions.py +1 -1
- cmd2/history.py +7 -11
- cmd2/parsing.py +12 -17
- cmd2/plugin.py +1 -2
- cmd2/py_bridge.py +15 -10
- cmd2/rich_utils.py +451 -0
- cmd2/rl_utils.py +12 -8
- cmd2/string_utils.py +166 -0
- cmd2/styles.py +72 -0
- cmd2/terminal_utils.py +144 -0
- cmd2/transcript.py +7 -9
- cmd2/utils.py +88 -508
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/METADATA +23 -44
- cmd2-3.0.0b1.dist-info/RECORD +27 -0
- cmd2/ansi.py +0 -1093
- cmd2/table_creator.py +0 -1122
- cmd2-2.6.2.dist-info/RECORD +0 -24
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/WHEEL +0 -0
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {cmd2-2.6.2.dist-info → cmd2-3.0.0b1.dist-info}/top_level.txt +0 -0
cmd2/colors.py
ADDED
@@ -0,0 +1,270 @@
|
|
1
|
+
"""Provides a convenient StrEnum for Rich color names."""
|
2
|
+
|
3
|
+
import sys
|
4
|
+
|
5
|
+
if sys.version_info >= (3, 11):
|
6
|
+
from enum import StrEnum
|
7
|
+
else:
|
8
|
+
from backports.strenum import StrEnum
|
9
|
+
|
10
|
+
|
11
|
+
class Color(StrEnum):
|
12
|
+
"""An enumeration of all color names supported by the Rich library.
|
13
|
+
|
14
|
+
Using this enum allows for autocompletion and prevents typos when referencing
|
15
|
+
color names. The members can be used for both foreground and background colors.
|
16
|
+
|
17
|
+
Aside from DEFAULT, these colors come from the rich.color.ANSI_COLOR_NAMES dictionary.
|
18
|
+
|
19
|
+
Note: The terminal color settings determines the appearance of the follow 16 colors.
|
20
|
+
|
21
|
+
| | |
|
22
|
+
|----------------|---------------|
|
23
|
+
| BLACK | BRIGHT_WHITE |
|
24
|
+
| BLUE | BRIGHT_YELLOW |
|
25
|
+
| BRIGHT_BLACK | CYAN |
|
26
|
+
| BRIGHT_BLUE | GREEN |
|
27
|
+
| BRIGHT_CYAN | MAGENTA |
|
28
|
+
| BRIGHT_GREEN | RED |
|
29
|
+
| BRIGHT_MAGENTA | WHITE |
|
30
|
+
| BRIGHT_RED | YELLOW |
|
31
|
+
"""
|
32
|
+
|
33
|
+
DEFAULT = "default"
|
34
|
+
"""Represents the terminal's default foreground or background color."""
|
35
|
+
|
36
|
+
AQUAMARINE1 = "aquamarine1"
|
37
|
+
AQUAMARINE3 = "aquamarine3"
|
38
|
+
BLACK = "black"
|
39
|
+
BLUE = "blue"
|
40
|
+
BLUE1 = "blue1"
|
41
|
+
BLUE3 = "blue3"
|
42
|
+
BLUE_VIOLET = "blue_violet"
|
43
|
+
BRIGHT_BLACK = "bright_black"
|
44
|
+
BRIGHT_BLUE = "bright_blue"
|
45
|
+
BRIGHT_CYAN = "bright_cyan"
|
46
|
+
BRIGHT_GREEN = "bright_green"
|
47
|
+
BRIGHT_MAGENTA = "bright_magenta"
|
48
|
+
BRIGHT_RED = "bright_red"
|
49
|
+
BRIGHT_WHITE = "bright_white"
|
50
|
+
BRIGHT_YELLOW = "bright_yellow"
|
51
|
+
CADET_BLUE = "cadet_blue"
|
52
|
+
CHARTREUSE1 = "chartreuse1"
|
53
|
+
CHARTREUSE2 = "chartreuse2"
|
54
|
+
CHARTREUSE3 = "chartreuse3"
|
55
|
+
CHARTREUSE4 = "chartreuse4"
|
56
|
+
CORNFLOWER_BLUE = "cornflower_blue"
|
57
|
+
CORNSILK1 = "cornsilk1"
|
58
|
+
CYAN = "cyan"
|
59
|
+
CYAN1 = "cyan1"
|
60
|
+
CYAN2 = "cyan2"
|
61
|
+
CYAN3 = "cyan3"
|
62
|
+
DARK_BLUE = "dark_blue"
|
63
|
+
DARK_CYAN = "dark_cyan"
|
64
|
+
DARK_GOLDENROD = "dark_goldenrod"
|
65
|
+
DARK_GREEN = "dark_green"
|
66
|
+
DARK_KHAKI = "dark_khaki"
|
67
|
+
DARK_MAGENTA = "dark_magenta"
|
68
|
+
DARK_OLIVE_GREEN1 = "dark_olive_green1"
|
69
|
+
DARK_OLIVE_GREEN2 = "dark_olive_green2"
|
70
|
+
DARK_OLIVE_GREEN3 = "dark_olive_green3"
|
71
|
+
DARK_ORANGE = "dark_orange"
|
72
|
+
DARK_ORANGE3 = "dark_orange3"
|
73
|
+
DARK_RED = "dark_red"
|
74
|
+
DARK_SEA_GREEN = "dark_sea_green"
|
75
|
+
DARK_SEA_GREEN1 = "dark_sea_green1"
|
76
|
+
DARK_SEA_GREEN2 = "dark_sea_green2"
|
77
|
+
DARK_SEA_GREEN3 = "dark_sea_green3"
|
78
|
+
DARK_SEA_GREEN4 = "dark_sea_green4"
|
79
|
+
DARK_SLATE_GRAY1 = "dark_slate_gray1"
|
80
|
+
DARK_SLATE_GRAY2 = "dark_slate_gray2"
|
81
|
+
DARK_SLATE_GRAY3 = "dark_slate_gray3"
|
82
|
+
DARK_TURQUOISE = "dark_turquoise"
|
83
|
+
DARK_VIOLET = "dark_violet"
|
84
|
+
DEEP_PINK1 = "deep_pink1"
|
85
|
+
DEEP_PINK2 = "deep_pink2"
|
86
|
+
DEEP_PINK3 = "deep_pink3"
|
87
|
+
DEEP_PINK4 = "deep_pink4"
|
88
|
+
DEEP_SKY_BLUE1 = "deep_sky_blue1"
|
89
|
+
DEEP_SKY_BLUE2 = "deep_sky_blue2"
|
90
|
+
DEEP_SKY_BLUE3 = "deep_sky_blue3"
|
91
|
+
DEEP_SKY_BLUE4 = "deep_sky_blue4"
|
92
|
+
DODGER_BLUE1 = "dodger_blue1"
|
93
|
+
DODGER_BLUE2 = "dodger_blue2"
|
94
|
+
DODGER_BLUE3 = "dodger_blue3"
|
95
|
+
GOLD1 = "gold1"
|
96
|
+
GOLD3 = "gold3"
|
97
|
+
GRAY0 = "gray0"
|
98
|
+
GRAY3 = "gray3"
|
99
|
+
GRAY7 = "gray7"
|
100
|
+
GRAY11 = "gray11"
|
101
|
+
GRAY15 = "gray15"
|
102
|
+
GRAY19 = "gray19"
|
103
|
+
GRAY23 = "gray23"
|
104
|
+
GRAY27 = "gray27"
|
105
|
+
GRAY30 = "gray30"
|
106
|
+
GRAY35 = "gray35"
|
107
|
+
GRAY37 = "gray37"
|
108
|
+
GRAY39 = "gray39"
|
109
|
+
GRAY42 = "gray42"
|
110
|
+
GRAY46 = "gray46"
|
111
|
+
GRAY50 = "gray50"
|
112
|
+
GRAY53 = "gray53"
|
113
|
+
GRAY54 = "gray54"
|
114
|
+
GRAY58 = "gray58"
|
115
|
+
GRAY62 = "gray62"
|
116
|
+
GRAY63 = "gray63"
|
117
|
+
GRAY66 = "gray66"
|
118
|
+
GRAY69 = "gray69"
|
119
|
+
GRAY70 = "gray70"
|
120
|
+
GRAY74 = "gray74"
|
121
|
+
GRAY78 = "gray78"
|
122
|
+
GRAY82 = "gray82"
|
123
|
+
GRAY84 = "gray84"
|
124
|
+
GRAY85 = "gray85"
|
125
|
+
GRAY89 = "gray89"
|
126
|
+
GRAY93 = "gray93"
|
127
|
+
GRAY100 = "gray100"
|
128
|
+
GREEN = "green"
|
129
|
+
GREEN1 = "green1"
|
130
|
+
GREEN3 = "green3"
|
131
|
+
GREEN4 = "green4"
|
132
|
+
GREEN_YELLOW = "green_yellow"
|
133
|
+
GREY0 = "grey0"
|
134
|
+
GREY3 = "grey3"
|
135
|
+
GREY7 = "grey7"
|
136
|
+
GREY11 = "grey11"
|
137
|
+
GREY15 = "grey15"
|
138
|
+
GREY19 = "grey19"
|
139
|
+
GREY23 = "grey23"
|
140
|
+
GREY27 = "grey27"
|
141
|
+
GREY30 = "grey30"
|
142
|
+
GREY35 = "grey35"
|
143
|
+
GREY37 = "grey37"
|
144
|
+
GREY39 = "grey39"
|
145
|
+
GREY42 = "grey42"
|
146
|
+
GREY46 = "grey46"
|
147
|
+
GREY50 = "grey50"
|
148
|
+
GREY53 = "grey53"
|
149
|
+
GREY54 = "grey54"
|
150
|
+
GREY58 = "grey58"
|
151
|
+
GREY62 = "grey62"
|
152
|
+
GREY63 = "grey63"
|
153
|
+
GREY66 = "grey66"
|
154
|
+
GREY69 = "grey69"
|
155
|
+
GREY70 = "grey70"
|
156
|
+
GREY74 = "grey74"
|
157
|
+
GREY78 = "grey78"
|
158
|
+
GREY82 = "grey82"
|
159
|
+
GREY84 = "grey84"
|
160
|
+
GREY85 = "grey85"
|
161
|
+
GREY89 = "grey89"
|
162
|
+
GREY93 = "grey93"
|
163
|
+
GREY100 = "grey100"
|
164
|
+
HONEYDEW2 = "honeydew2"
|
165
|
+
HOT_PINK = "hot_pink"
|
166
|
+
HOT_PINK2 = "hot_pink2"
|
167
|
+
HOT_PINK3 = "hot_pink3"
|
168
|
+
INDIAN_RED = "indian_red"
|
169
|
+
INDIAN_RED1 = "indian_red1"
|
170
|
+
KHAKI1 = "khaki1"
|
171
|
+
KHAKI3 = "khaki3"
|
172
|
+
LIGHT_CORAL = "light_coral"
|
173
|
+
LIGHT_CYAN1 = "light_cyan1"
|
174
|
+
LIGHT_CYAN3 = "light_cyan3"
|
175
|
+
LIGHT_GOLDENROD1 = "light_goldenrod1"
|
176
|
+
LIGHT_GOLDENROD2 = "light_goldenrod2"
|
177
|
+
LIGHT_GOLDENROD3 = "light_goldenrod3"
|
178
|
+
LIGHT_GREEN = "light_green"
|
179
|
+
LIGHT_PINK1 = "light_pink1"
|
180
|
+
LIGHT_PINK3 = "light_pink3"
|
181
|
+
LIGHT_PINK4 = "light_pink4"
|
182
|
+
LIGHT_SALMON1 = "light_salmon1"
|
183
|
+
LIGHT_SALMON3 = "light_salmon3"
|
184
|
+
LIGHT_SEA_GREEN = "light_sea_green"
|
185
|
+
LIGHT_SKY_BLUE1 = "light_sky_blue1"
|
186
|
+
LIGHT_SKY_BLUE3 = "light_sky_blue3"
|
187
|
+
LIGHT_SLATE_BLUE = "light_slate_blue"
|
188
|
+
LIGHT_SLATE_GRAY = "light_slate_gray"
|
189
|
+
LIGHT_SLATE_GREY = "light_slate_grey"
|
190
|
+
LIGHT_STEEL_BLUE = "light_steel_blue"
|
191
|
+
LIGHT_STEEL_BLUE1 = "light_steel_blue1"
|
192
|
+
LIGHT_STEEL_BLUE3 = "light_steel_blue3"
|
193
|
+
LIGHT_YELLOW3 = "light_yellow3"
|
194
|
+
MAGENTA = "magenta"
|
195
|
+
MAGENTA1 = "magenta1"
|
196
|
+
MAGENTA2 = "magenta2"
|
197
|
+
MAGENTA3 = "magenta3"
|
198
|
+
MEDIUM_ORCHID = "medium_orchid"
|
199
|
+
MEDIUM_ORCHID1 = "medium_orchid1"
|
200
|
+
MEDIUM_ORCHID3 = "medium_orchid3"
|
201
|
+
MEDIUM_PURPLE = "medium_purple"
|
202
|
+
MEDIUM_PURPLE1 = "medium_purple1"
|
203
|
+
MEDIUM_PURPLE2 = "medium_purple2"
|
204
|
+
MEDIUM_PURPLE3 = "medium_purple3"
|
205
|
+
MEDIUM_PURPLE4 = "medium_purple4"
|
206
|
+
MEDIUM_SPRING_GREEN = "medium_spring_green"
|
207
|
+
MEDIUM_TURQUOISE = "medium_turquoise"
|
208
|
+
MEDIUM_VIOLET_RED = "medium_violet_red"
|
209
|
+
MISTY_ROSE1 = "misty_rose1"
|
210
|
+
MISTY_ROSE3 = "misty_rose3"
|
211
|
+
NAVAJO_WHITE1 = "navajo_white1"
|
212
|
+
NAVAJO_WHITE3 = "navajo_white3"
|
213
|
+
NAVY_BLUE = "navy_blue"
|
214
|
+
ORANGE1 = "orange1"
|
215
|
+
ORANGE3 = "orange3"
|
216
|
+
ORANGE4 = "orange4"
|
217
|
+
ORANGE_RED1 = "orange_red1"
|
218
|
+
ORCHID = "orchid"
|
219
|
+
ORCHID1 = "orchid1"
|
220
|
+
ORCHID2 = "orchid2"
|
221
|
+
PALE_GREEN1 = "pale_green1"
|
222
|
+
PALE_GREEN3 = "pale_green3"
|
223
|
+
PALE_TURQUOISE1 = "pale_turquoise1"
|
224
|
+
PALE_TURQUOISE4 = "pale_turquoise4"
|
225
|
+
PALE_VIOLET_RED1 = "pale_violet_red1"
|
226
|
+
PINK1 = "pink1"
|
227
|
+
PINK3 = "pink3"
|
228
|
+
PLUM1 = "plum1"
|
229
|
+
PLUM2 = "plum2"
|
230
|
+
PLUM3 = "plum3"
|
231
|
+
PLUM4 = "plum4"
|
232
|
+
PURPLE = "purple"
|
233
|
+
PURPLE3 = "purple3"
|
234
|
+
PURPLE4 = "purple4"
|
235
|
+
RED = "red"
|
236
|
+
RED1 = "red1"
|
237
|
+
RED3 = "red3"
|
238
|
+
ROSY_BROWN = "rosy_brown"
|
239
|
+
ROYAL_BLUE1 = "royal_blue1"
|
240
|
+
SALMON1 = "salmon1"
|
241
|
+
SANDY_BROWN = "sandy_brown"
|
242
|
+
SEA_GREEN1 = "sea_green1"
|
243
|
+
SEA_GREEN2 = "sea_green2"
|
244
|
+
SEA_GREEN3 = "sea_green3"
|
245
|
+
SKY_BLUE1 = "sky_blue1"
|
246
|
+
SKY_BLUE2 = "sky_blue2"
|
247
|
+
SKY_BLUE3 = "sky_blue3"
|
248
|
+
SLATE_BLUE1 = "slate_blue1"
|
249
|
+
SLATE_BLUE3 = "slate_blue3"
|
250
|
+
SPRING_GREEN1 = "spring_green1"
|
251
|
+
SPRING_GREEN2 = "spring_green2"
|
252
|
+
SPRING_GREEN3 = "spring_green3"
|
253
|
+
SPRING_GREEN4 = "spring_green4"
|
254
|
+
STEEL_BLUE = "steel_blue"
|
255
|
+
STEEL_BLUE1 = "steel_blue1"
|
256
|
+
STEEL_BLUE3 = "steel_blue3"
|
257
|
+
TAN = "tan"
|
258
|
+
THISTLE1 = "thistle1"
|
259
|
+
THISTLE3 = "thistle3"
|
260
|
+
TURQUOISE2 = "turquoise2"
|
261
|
+
TURQUOISE4 = "turquoise4"
|
262
|
+
VIOLET = "violet"
|
263
|
+
WHEAT1 = "wheat1"
|
264
|
+
WHEAT4 = "wheat4"
|
265
|
+
WHITE = "white"
|
266
|
+
YELLOW = "yellow"
|
267
|
+
YELLOW1 = "yellow1"
|
268
|
+
YELLOW2 = "yellow2"
|
269
|
+
YELLOW3 = "yellow3"
|
270
|
+
YELLOW4 = "yellow4"
|
cmd2/command_definition.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
from collections.abc import Callable, Mapping
|
4
4
|
from typing import (
|
5
5
|
TYPE_CHECKING,
|
6
|
-
Optional,
|
7
6
|
TypeVar,
|
8
7
|
)
|
9
8
|
|
@@ -23,7 +22,7 @@ if TYPE_CHECKING: # pragma: no cover
|
|
23
22
|
|
24
23
|
#: Callable signature for a basic command function
|
25
24
|
#: Further refinements are needed to define the input parameters
|
26
|
-
CommandFunc = Callable[...,
|
25
|
+
CommandFunc = Callable[..., bool | None]
|
27
26
|
|
28
27
|
CommandSetType = TypeVar('CommandSetType', bound=type['CommandSet'])
|
29
28
|
|
@@ -91,7 +90,7 @@ class CommandSet:
|
|
91
90
|
This will be set when the CommandSet is registered and it should be
|
92
91
|
accessed by child classes using the self._cmd property.
|
93
92
|
"""
|
94
|
-
self.__cmd_internal:
|
93
|
+
self.__cmd_internal: cmd2.Cmd | None = None
|
95
94
|
|
96
95
|
self._settables: dict[str, Settable] = {}
|
97
96
|
self._settable_prefix = self.__class__.__name__
|
@@ -103,8 +102,17 @@ class CommandSet:
|
|
103
102
|
Using this property ensures that self.__cmd_internal has been set
|
104
103
|
and it tells type checkers that it's no longer a None type.
|
105
104
|
|
106
|
-
Override this property
|
107
|
-
|
105
|
+
Override this property to specify a more specific return type for static
|
106
|
+
type checking. The typing.cast function can be used to assert to the
|
107
|
+
type checker that the parent cmd2.Cmd instance is of a more specific
|
108
|
+
subclass, enabling better autocompletion and type safety in the child class.
|
109
|
+
|
110
|
+
For example:
|
111
|
+
|
112
|
+
@property
|
113
|
+
def _cmd(self) -> CustomCmdApp:
|
114
|
+
return cast(CustomCmdApp, super()._cmd)
|
115
|
+
|
108
116
|
|
109
117
|
:raises CommandSetRegistrationError: if CommandSet is not registered.
|
110
118
|
"""
|
cmd2/constants.py
CHANGED
@@ -18,9 +18,6 @@ MULTILINE_TERMINATOR = ';'
|
|
18
18
|
|
19
19
|
LINE_FEED = '\n'
|
20
20
|
|
21
|
-
# One character ellipsis
|
22
|
-
HORIZONTAL_ELLIPSIS = '…'
|
23
|
-
|
24
21
|
DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'run_script', '@@': '_relative_run_script'}
|
25
22
|
|
26
23
|
# Used as the command name placeholder in disabled command messages.
|
@@ -55,6 +52,3 @@ PARSER_ATTR_COMMANDSET = 'command_set'
|
|
55
52
|
|
56
53
|
# custom attributes added to argparse Namespaces
|
57
54
|
NS_ATTR_SUBCMD_HANDLER = '__subcmd_handler__'
|
58
|
-
|
59
|
-
# For cases prior to Python 3.11 when shutil.get_terminal_size().columns can return 0.
|
60
|
-
DEFAULT_TERMINAL_WIDTH = 80
|
cmd2/decorators.py
CHANGED
@@ -5,7 +5,6 @@ from collections.abc import Callable, Sequence
|
|
5
5
|
from typing import (
|
6
6
|
TYPE_CHECKING,
|
7
7
|
Any,
|
8
|
-
Optional,
|
9
8
|
TypeVar,
|
10
9
|
Union,
|
11
10
|
)
|
@@ -62,10 +61,10 @@ def with_category(category: str) -> Callable[[CommandFunc], CommandFunc]:
|
|
62
61
|
|
63
62
|
|
64
63
|
CommandParent = TypeVar('CommandParent', bound=Union['cmd2.Cmd', CommandSet])
|
65
|
-
CommandParentType = TypeVar('CommandParentType', bound=
|
64
|
+
CommandParentType = TypeVar('CommandParentType', bound=type['cmd2.Cmd'] | type[CommandSet])
|
66
65
|
|
67
66
|
|
68
|
-
RawCommandFuncOptionalBoolReturn = Callable[[CommandParent,
|
67
|
+
RawCommandFuncOptionalBoolReturn = Callable[[CommandParent, Statement | str], bool | None]
|
69
68
|
|
70
69
|
|
71
70
|
##########################
|
@@ -73,7 +72,7 @@ RawCommandFuncOptionalBoolReturn = Callable[[CommandParent, Union[Statement, str
|
|
73
72
|
# in cmd2 command functions/callables. As long as the 2-ple of arguments we expect to be there can be
|
74
73
|
# found we can swap out the statement with each decorator's specific parameters
|
75
74
|
##########################
|
76
|
-
def _parse_positionals(args: tuple[Any, ...]) -> tuple['cmd2.Cmd',
|
75
|
+
def _parse_positionals(args: tuple[Any, ...]) -> tuple['cmd2.Cmd', Statement | str]:
|
77
76
|
"""Inspect the positional arguments until the cmd2.Cmd argument is found.
|
78
77
|
|
79
78
|
Assumes that we will find cmd2.Cmd followed by the command statement object or string.
|
@@ -98,7 +97,7 @@ def _parse_positionals(args: tuple[Any, ...]) -> tuple['cmd2.Cmd', Union[Stateme
|
|
98
97
|
raise TypeError('Expected arguments: cmd: cmd2.Cmd, statement: Union[Statement, str] Not found')
|
99
98
|
|
100
99
|
|
101
|
-
def _arg_swap(args:
|
100
|
+
def _arg_swap(args: Sequence[Any], search_arg: Any, *replace_arg: Any) -> list[Any]:
|
102
101
|
"""Swap the Statement parameter with one or more decorator-specific parameters.
|
103
102
|
|
104
103
|
:param args: The original positional arguments
|
@@ -114,7 +113,7 @@ def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) ->
|
|
114
113
|
|
115
114
|
#: Function signature for a command function that accepts a pre-processed argument list from user input
|
116
115
|
#: and optionally returns a boolean
|
117
|
-
ArgListCommandFuncOptionalBoolReturn = Callable[[CommandParent, list[str]],
|
116
|
+
ArgListCommandFuncOptionalBoolReturn = Callable[[CommandParent, list[str]], bool | None]
|
118
117
|
#: Function signature for a command function that accepts a pre-processed argument list from user input
|
119
118
|
#: and returns a boolean
|
120
119
|
ArgListCommandFuncBoolReturn = Callable[[CommandParent, list[str]], bool]
|
@@ -123,21 +122,21 @@ ArgListCommandFuncBoolReturn = Callable[[CommandParent, list[str]], bool]
|
|
123
122
|
ArgListCommandFuncNoneReturn = Callable[[CommandParent, list[str]], None]
|
124
123
|
|
125
124
|
#: Aggregate of all accepted function signatures for command functions that accept a pre-processed argument list
|
126
|
-
ArgListCommandFunc =
|
127
|
-
ArgListCommandFuncOptionalBoolReturn[CommandParent]
|
128
|
-
ArgListCommandFuncBoolReturn[CommandParent]
|
129
|
-
ArgListCommandFuncNoneReturn[CommandParent]
|
130
|
-
|
125
|
+
ArgListCommandFunc = (
|
126
|
+
ArgListCommandFuncOptionalBoolReturn[CommandParent]
|
127
|
+
| ArgListCommandFuncBoolReturn[CommandParent]
|
128
|
+
| ArgListCommandFuncNoneReturn[CommandParent]
|
129
|
+
)
|
131
130
|
|
132
131
|
|
133
132
|
def with_argument_list(
|
134
|
-
func_arg:
|
133
|
+
func_arg: ArgListCommandFunc[CommandParent] | None = None,
|
135
134
|
*,
|
136
135
|
preserve_quotes: bool = False,
|
137
|
-
) ->
|
138
|
-
RawCommandFuncOptionalBoolReturn[CommandParent]
|
139
|
-
Callable[[ArgListCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]
|
140
|
-
|
136
|
+
) -> (
|
137
|
+
RawCommandFuncOptionalBoolReturn[CommandParent]
|
138
|
+
| Callable[[ArgListCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]
|
139
|
+
):
|
141
140
|
"""Decorate a ``do_*`` method to alter the arguments passed to it so it is passed a list[str].
|
142
141
|
|
143
142
|
Default passes a string of whatever the user typed. With this decorator, the
|
@@ -169,7 +168,7 @@ def with_argument_list(
|
|
169
168
|
"""
|
170
169
|
|
171
170
|
@functools.wraps(func)
|
172
|
-
def cmd_wrapper(*args: Any, **kwargs: Any) ->
|
171
|
+
def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None:
|
173
172
|
"""Command function wrapper which translates command line into an argument list and calls actual command function.
|
174
173
|
|
175
174
|
:param args: All positional arguments to this function. We're expecting there to be:
|
@@ -181,7 +180,7 @@ def with_argument_list(
|
|
181
180
|
cmd2_app, statement = _parse_positionals(args)
|
182
181
|
_, parsed_arglist = cmd2_app.statement_parser.get_command_arg_list(command_name, statement, preserve_quotes)
|
183
182
|
args_list = _arg_swap(args, statement, parsed_arglist)
|
184
|
-
return func(*args_list, **kwargs)
|
183
|
+
return func(*args_list, **kwargs)
|
185
184
|
|
186
185
|
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
|
187
186
|
cmd_wrapper.__doc__ = func.__doc__
|
@@ -192,60 +191,10 @@ def with_argument_list(
|
|
192
191
|
return arg_decorator
|
193
192
|
|
194
193
|
|
195
|
-
def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None:
|
196
|
-
"""Recursively set prog attribute of a parser and all of its subparsers.
|
197
|
-
|
198
|
-
Does so that the root command is a command name and not sys.argv[0].
|
199
|
-
|
200
|
-
:param parser: the parser being edited
|
201
|
-
:param prog: new value for the parser's prog attribute
|
202
|
-
"""
|
203
|
-
# Set the prog value for this parser
|
204
|
-
parser.prog = prog
|
205
|
-
req_args: list[str] = []
|
206
|
-
|
207
|
-
# Set the prog value for the parser's subcommands
|
208
|
-
for action in parser._actions:
|
209
|
-
if isinstance(action, argparse._SubParsersAction):
|
210
|
-
# Set the _SubParsersAction's _prog_prefix value. That way if its add_parser() method is called later,
|
211
|
-
# the correct prog value will be set on the parser being added.
|
212
|
-
action._prog_prefix = parser.prog
|
213
|
-
|
214
|
-
# The keys of action.choices are subcommand names as well as subcommand aliases. The aliases point to the
|
215
|
-
# same parser as the actual subcommand. We want to avoid placing an alias into a parser's prog value.
|
216
|
-
# Unfortunately there is nothing about an action.choices entry which tells us it's an alias. In most cases
|
217
|
-
# we can filter out the aliases by checking the contents of action._choices_actions. This list only contains
|
218
|
-
# help information and names for the subcommands and not aliases. However, subcommands without help text
|
219
|
-
# won't show up in that list. Since dictionaries are ordered in Python 3.6 and above and argparse inserts the
|
220
|
-
# subcommand name into choices dictionary before aliases, we should be OK assuming the first time we see a
|
221
|
-
# parser, the dictionary key is a subcommand and not alias.
|
222
|
-
processed_parsers = []
|
223
|
-
|
224
|
-
# Set the prog value for each subcommand's parser
|
225
|
-
for subcmd_name, subcmd_parser in action.choices.items():
|
226
|
-
# Check if we've already edited this parser
|
227
|
-
if subcmd_parser in processed_parsers:
|
228
|
-
continue
|
229
|
-
|
230
|
-
subcmd_prog = parser.prog
|
231
|
-
if req_args:
|
232
|
-
subcmd_prog += " " + " ".join(req_args)
|
233
|
-
subcmd_prog += " " + subcmd_name
|
234
|
-
_set_parser_prog(subcmd_parser, subcmd_prog)
|
235
|
-
processed_parsers.append(subcmd_parser)
|
236
|
-
|
237
|
-
# We can break since argparse only allows 1 group of subcommands per level
|
238
|
-
break
|
239
|
-
|
240
|
-
# Need to save required args so they can be prepended to the subcommand usage
|
241
|
-
if action.required:
|
242
|
-
req_args.append(action.dest)
|
243
|
-
|
244
|
-
|
245
194
|
#: Function signatures for command functions that use an argparse.ArgumentParser to process user input
|
246
195
|
#: and optionally return a boolean
|
247
|
-
ArgparseCommandFuncOptionalBoolReturn = Callable[[CommandParent, argparse.Namespace],
|
248
|
-
ArgparseCommandFuncWithUnknownArgsOptionalBoolReturn = Callable[[CommandParent, argparse.Namespace, list[str]],
|
196
|
+
ArgparseCommandFuncOptionalBoolReturn = Callable[[CommandParent, argparse.Namespace], bool | None]
|
197
|
+
ArgparseCommandFuncWithUnknownArgsOptionalBoolReturn = Callable[[CommandParent, argparse.Namespace, list[str]], bool | None]
|
249
198
|
|
250
199
|
#: Function signatures for command functions that use an argparse.ArgumentParser to process user input
|
251
200
|
#: and return a boolean
|
@@ -258,30 +207,28 @@ ArgparseCommandFuncNoneReturn = Callable[[CommandParent, argparse.Namespace], No
|
|
258
207
|
ArgparseCommandFuncWithUnknownArgsNoneReturn = Callable[[CommandParent, argparse.Namespace, list[str]], None]
|
259
208
|
|
260
209
|
#: Aggregate of all accepted function signatures for an argparse command function
|
261
|
-
ArgparseCommandFunc =
|
262
|
-
ArgparseCommandFuncOptionalBoolReturn[CommandParent]
|
263
|
-
ArgparseCommandFuncWithUnknownArgsOptionalBoolReturn[CommandParent]
|
264
|
-
ArgparseCommandFuncBoolReturn[CommandParent]
|
265
|
-
ArgparseCommandFuncWithUnknownArgsBoolReturn[CommandParent]
|
266
|
-
ArgparseCommandFuncNoneReturn[CommandParent]
|
267
|
-
ArgparseCommandFuncWithUnknownArgsNoneReturn[CommandParent]
|
268
|
-
|
210
|
+
ArgparseCommandFunc = (
|
211
|
+
ArgparseCommandFuncOptionalBoolReturn[CommandParent]
|
212
|
+
| ArgparseCommandFuncWithUnknownArgsOptionalBoolReturn[CommandParent]
|
213
|
+
| ArgparseCommandFuncBoolReturn[CommandParent]
|
214
|
+
| ArgparseCommandFuncWithUnknownArgsBoolReturn[CommandParent]
|
215
|
+
| ArgparseCommandFuncNoneReturn[CommandParent]
|
216
|
+
| ArgparseCommandFuncWithUnknownArgsNoneReturn[CommandParent]
|
217
|
+
)
|
269
218
|
|
270
219
|
|
271
220
|
def with_argparser(
|
272
|
-
parser:
|
273
|
-
|
274
|
-
|
275
|
-
Callable[[CommandParentType], argparse.ArgumentParser], # Cmd or CommandSet classmethod
|
276
|
-
],
|
221
|
+
parser: argparse.ArgumentParser # existing parser
|
222
|
+
| Callable[[], argparse.ArgumentParser] # function or staticmethod
|
223
|
+
| Callable[[CommandParentType], argparse.ArgumentParser], # Cmd or CommandSet classmethod
|
277
224
|
*,
|
278
|
-
ns_provider:
|
225
|
+
ns_provider: Callable[..., argparse.Namespace] | None = None,
|
279
226
|
preserve_quotes: bool = False,
|
280
227
|
with_unknown_args: bool = False,
|
281
228
|
) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]:
|
282
229
|
"""Decorate a ``do_*`` method to populate its ``args`` argument with the given instance of argparse.ArgumentParser.
|
283
230
|
|
284
|
-
:param parser:
|
231
|
+
:param parser: instance of ArgumentParser or a callable that returns an ArgumentParser for this command
|
285
232
|
:param ns_provider: An optional function that accepts a cmd2.Cmd or cmd2.CommandSet object as an argument and returns an
|
286
233
|
argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that
|
287
234
|
affects parsing.
|
@@ -336,7 +283,7 @@ def with_argparser(
|
|
336
283
|
"""
|
337
284
|
|
338
285
|
@functools.wraps(func)
|
339
|
-
def cmd_wrapper(*args: Any, **kwargs: dict[str, Any]) ->
|
286
|
+
def cmd_wrapper(*args: Any, **kwargs: dict[str, Any]) -> bool | None:
|
340
287
|
"""Command function wrapper which translates command line into argparse Namespace and call actual command function.
|
341
288
|
|
342
289
|
:param args: All positional arguments to this function. We're expecting there to be:
|
@@ -367,7 +314,7 @@ def with_argparser(
|
|
367
314
|
namespace = ns_provider(provider_self if provider_self is not None else cmd2_app)
|
368
315
|
|
369
316
|
try:
|
370
|
-
new_args:
|
317
|
+
new_args: tuple[argparse.Namespace] | tuple[argparse.Namespace, list[str]]
|
371
318
|
if with_unknown_args:
|
372
319
|
new_args = arg_parser.parse_known_args(parsed_arglist, namespace)
|
373
320
|
else:
|
@@ -389,18 +336,10 @@ def with_argparser(
|
|
389
336
|
delattr(ns, constants.NS_ATTR_SUBCMD_HANDLER)
|
390
337
|
|
391
338
|
args_list = _arg_swap(args, statement_arg, *new_args)
|
392
|
-
return func(*args_list, **kwargs)
|
339
|
+
return func(*args_list, **kwargs)
|
393
340
|
|
394
341
|
command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
|
395
342
|
|
396
|
-
if isinstance(parser, argparse.ArgumentParser):
|
397
|
-
# Set parser's prog value for backward compatibility within the cmd2 2.0 family.
|
398
|
-
# This will be removed in cmd2 3.0 since we never reference this parser object's prog value.
|
399
|
-
# Since it's possible for the same parser object to be passed into multiple with_argparser()
|
400
|
-
# calls, we only set prog on the deep copies of this parser based on the specific do_xxxx
|
401
|
-
# instance method they are associated with.
|
402
|
-
_set_parser_prog(parser, command_name)
|
403
|
-
|
404
343
|
# Set some custom attributes for this command
|
405
344
|
setattr(cmd_wrapper, constants.CMD_ATTR_ARGPARSER, parser)
|
406
345
|
setattr(cmd_wrapper, constants.CMD_ATTR_PRESERVE_QUOTES, preserve_quotes)
|
@@ -413,20 +352,18 @@ def with_argparser(
|
|
413
352
|
def as_subcommand_to(
|
414
353
|
command: str,
|
415
354
|
subcommand: str,
|
416
|
-
parser:
|
417
|
-
|
418
|
-
|
419
|
-
Callable[[CommandParentType], argparse.ArgumentParser], # Cmd or CommandSet classmethod
|
420
|
-
],
|
355
|
+
parser: argparse.ArgumentParser # existing parser
|
356
|
+
| Callable[[], argparse.ArgumentParser] # function or staticmethod
|
357
|
+
| Callable[[CommandParentType], argparse.ArgumentParser], # Cmd or CommandSet classmethod
|
421
358
|
*,
|
422
|
-
help:
|
423
|
-
aliases:
|
359
|
+
help: str | None = None, # noqa: A002
|
360
|
+
aliases: list[str] | None = None,
|
424
361
|
) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]:
|
425
362
|
"""Tag this method as a subcommand to an existing argparse decorated command.
|
426
363
|
|
427
364
|
:param command: Command Name. Space-delimited subcommands may optionally be specified
|
428
365
|
:param subcommand: Subcommand name
|
429
|
-
:param parser:
|
366
|
+
:param parser: instance of ArgumentParser or a callable that returns an ArgumentParser for this subcommand
|
430
367
|
:param help: Help message for this subcommand which displays in the list of subcommands of the command we are adding to.
|
431
368
|
This is passed as the help argument to subparsers.add_parser().
|
432
369
|
:param aliases: Alternative names for this subcommand. This is passed as the alias argument to
|
cmd2/exceptions.py
CHANGED
@@ -40,7 +40,7 @@ class CompletionError(Exception):
|
|
40
40
|
def __init__(self, *args: Any, apply_style: bool = True) -> None:
|
41
41
|
"""Initialize CompletionError instance.
|
42
42
|
|
43
|
-
:param apply_style: If True, then
|
43
|
+
:param apply_style: If True, then styles.ERROR will be applied to the message text when printed.
|
44
44
|
Set to False in cases where the message text already has the desired style.
|
45
45
|
Defaults to True.
|
46
46
|
"""
|
cmd2/history.py
CHANGED
@@ -11,14 +11,10 @@ from dataclasses import (
|
|
11
11
|
)
|
12
12
|
from typing import (
|
13
13
|
Any,
|
14
|
-
Optional,
|
15
|
-
Union,
|
16
14
|
overload,
|
17
15
|
)
|
18
16
|
|
19
|
-
from . import
|
20
|
-
utils,
|
21
|
-
)
|
17
|
+
from . import string_utils as su
|
22
18
|
from .parsing import (
|
23
19
|
Statement,
|
24
20
|
shlex_split,
|
@@ -164,7 +160,7 @@ class History(list[HistoryItem]):
|
|
164
160
|
"""Start a new session, thereby setting the next index as the first index in the new session."""
|
165
161
|
self.session_start_index = len(self)
|
166
162
|
|
167
|
-
def _zero_based_index(self, onebased:
|
163
|
+
def _zero_based_index(self, onebased: int | str) -> int:
|
168
164
|
"""Convert a one-based index to a zero-based index."""
|
169
165
|
result = int(onebased)
|
170
166
|
if result > 0:
|
@@ -177,7 +173,7 @@ class History(list[HistoryItem]):
|
|
177
173
|
@overload
|
178
174
|
def append(self, new: Statement) -> None: ... # pragma: no cover
|
179
175
|
|
180
|
-
def append(self, new:
|
176
|
+
def append(self, new: Statement | HistoryItem) -> None:
|
181
177
|
"""Append a new statement to the end of the History list.
|
182
178
|
|
183
179
|
:param new: Statement object which will be composed into a HistoryItem
|
@@ -289,9 +285,9 @@ class History(list[HistoryItem]):
|
|
289
285
|
|
290
286
|
def isin(history_item: HistoryItem) -> bool:
|
291
287
|
"""Filter function for string search of history."""
|
292
|
-
sloppy =
|
293
|
-
inraw = sloppy in
|
294
|
-
inexpanded = sloppy in
|
288
|
+
sloppy = su.norm_fold(search)
|
289
|
+
inraw = sloppy in su.norm_fold(history_item.raw)
|
290
|
+
inexpanded = sloppy in su.norm_fold(history_item.expanded)
|
295
291
|
return inraw or inexpanded
|
296
292
|
|
297
293
|
start = 0 if include_persisted else self.session_start_index
|
@@ -332,7 +328,7 @@ class History(list[HistoryItem]):
|
|
332
328
|
del self[0:last_element]
|
333
329
|
|
334
330
|
def _build_result_dictionary(
|
335
|
-
self, start: int, end: int, filter_func:
|
331
|
+
self, start: int, end: int, filter_func: Callable[[HistoryItem], bool] | None = None
|
336
332
|
) -> 'OrderedDict[int, HistoryItem]':
|
337
333
|
"""Build history search results.
|
338
334
|
|