cmd2 2.7.0__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/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"
@@ -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[..., Optional[bool]]
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: Optional[cmd2.Cmd] = None
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 if you need to change its return type to a
107
- child class of Cmd.
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=Union[type['cmd2.Cmd'], type[CommandSet]])
64
+ CommandParentType = TypeVar('CommandParentType', bound=type['cmd2.Cmd'] | type[CommandSet])
66
65
 
67
66
 
68
- RawCommandFuncOptionalBoolReturn = Callable[[CommandParent, Union[Statement, str]], Optional[bool]]
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', Union[Statement, str]]:
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: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) -> list[Any]:
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]], Optional[bool]]
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 = Union[
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: Optional[ArgListCommandFunc[CommandParent]] = None,
133
+ func_arg: ArgListCommandFunc[CommandParent] | None = None,
135
134
  *,
136
135
  preserve_quotes: bool = False,
137
- ) -> Union[
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) -> Optional[bool]:
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) # type: ignore[call-arg]
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], Optional[bool]]
248
- ArgparseCommandFuncWithUnknownArgsOptionalBoolReturn = Callable[[CommandParent, argparse.Namespace, list[str]], Optional[bool]]
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 = Union[
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: Union[
273
- argparse.ArgumentParser, # existing parser
274
- Callable[[], argparse.ArgumentParser], # function or staticmethod
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: Optional[Callable[..., argparse.Namespace]] = None,
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: unique instance of ArgumentParser or a callable that returns an ArgumentParser
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]) -> Optional[bool]:
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: Union[tuple[argparse.Namespace], tuple[argparse.Namespace, list[str]]]
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) # type: ignore[call-arg]
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: Union[
417
- argparse.ArgumentParser, # existing parser
418
- Callable[[], argparse.ArgumentParser], # function or staticmethod
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: Optional[str] = None, # noqa: A002
423
- aliases: Optional[list[str]] = None,
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: argparse Parser for this subcommand
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 ansi.style_error will be applied to the message text when printed.
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: Union[int, str]) -> int:
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: Union[Statement, HistoryItem]) -> None:
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 = utils.norm_fold(search)
293
- inraw = sloppy in utils.norm_fold(history_item.raw)
294
- inexpanded = sloppy in utils.norm_fold(history_item.expanded)
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: Optional[Callable[[HistoryItem], bool]] = None
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