cmd2 2.5.11__py3-none-any.whl → 2.6.0__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/history.py CHANGED
@@ -1,22 +1,16 @@
1
- # coding=utf-8
2
- """
3
- History management classes
4
- """
1
+ """History management classes."""
5
2
 
6
3
  import json
7
4
  import re
8
5
  from collections import (
9
6
  OrderedDict,
10
7
  )
8
+ from collections.abc import Callable, Iterable
11
9
  from dataclasses import (
12
10
  dataclass,
13
11
  )
14
12
  from typing import (
15
13
  Any,
16
- Callable,
17
- Dict,
18
- Iterable,
19
- List,
20
14
  Optional,
21
15
  Union,
22
16
  overload,
@@ -32,8 +26,7 @@ from .parsing import (
32
26
 
33
27
 
34
28
  def single_line_format(statement: Statement) -> str:
35
- """
36
- Format a command line to display on a single line.
29
+ """Format a command line to display on a single line.
37
30
 
38
31
  Spaces and newlines in quotes are preserved so those strings will span multiple lines.
39
32
 
@@ -71,7 +64,7 @@ def single_line_format(statement: Statement) -> str:
71
64
 
72
65
  @dataclass(frozen=True)
73
66
  class HistoryItem:
74
- """Class used to represent one command in the history list"""
67
+ """Class used to represent one command in the history list."""
75
68
 
76
69
  _listformat = ' {:>4} {}'
77
70
  _ex_listformat = ' {:>4}x {}'
@@ -82,7 +75,7 @@ class HistoryItem:
82
75
  statement: Statement
83
76
 
84
77
  def __str__(self) -> str:
85
- """A convenient human-readable representation of the history item"""
78
+ """Human-readable representation of the history item."""
86
79
  return self.statement.raw
87
80
 
88
81
  @property
@@ -95,8 +88,7 @@ class HistoryItem:
95
88
 
96
89
  @property
97
90
  def expanded(self) -> str:
98
- """Return the command as run which includes shortcuts and aliases resolved
99
- plus any changes made in hooks
91
+ """Return the command as run which includes shortcuts and aliases resolved plus any changes made in hooks.
100
92
 
101
93
  Proxy property for ``self.statement.expanded_command_line``
102
94
  """
@@ -121,10 +113,7 @@ class HistoryItem:
121
113
  if raw != expanded_command:
122
114
  ret_str += '\n' + self._ex_listformat.format(idx, expanded_command)
123
115
  else:
124
- if expanded:
125
- ret_str = self.expanded
126
- else:
127
- ret_str = single_line_format(self.statement).rstrip()
116
+ ret_str = self.expanded if expanded else single_line_format(self.statement).rstrip()
128
117
 
129
118
  # Display a numbered list if not writing to a script
130
119
  if not script:
@@ -132,14 +121,13 @@ class HistoryItem:
132
121
 
133
122
  return ret_str
134
123
 
135
- def to_dict(self) -> Dict[str, Any]:
136
- """Utility method to convert this HistoryItem into a dictionary for use in persistent JSON history files"""
124
+ def to_dict(self) -> dict[str, Any]:
125
+ """Convert this HistoryItem into a dictionary for use in persistent JSON history files."""
137
126
  return {HistoryItem._statement_field: self.statement.to_dict()}
138
127
 
139
128
  @staticmethod
140
- def from_dict(source_dict: Dict[str, Any]) -> 'HistoryItem':
141
- """
142
- Utility method to restore a HistoryItem from a dictionary
129
+ def from_dict(source_dict: dict[str, Any]) -> 'HistoryItem':
130
+ """Restore a HistoryItem from a dictionary.
143
131
 
144
132
  :param source_dict: source data dictionary (generated using to_dict())
145
133
  :return: HistoryItem object
@@ -149,9 +137,8 @@ class HistoryItem:
149
137
  return HistoryItem(Statement.from_dict(statement_dict))
150
138
 
151
139
 
152
- class History(List[HistoryItem]):
153
- """A list of [HistoryItem][cmd2.history.HistoryItem] objects with additional methods
154
- for searching and managing the list.
140
+ class History(list[HistoryItem]):
141
+ """A list of [HistoryItem][cmd2.history.HistoryItem] objects with additional methods for searching and managing the list.
155
142
 
156
143
  [cmd2.Cmd][] instantiates this class into the `cmd2.Cmd.history`
157
144
  attribute, and adds commands to it as a user enters them.
@@ -169,7 +156,8 @@ class History(List[HistoryItem]):
169
156
  _history_items_field = 'history_items'
170
157
 
171
158
  def __init__(self, seq: Iterable[HistoryItem] = ()) -> None:
172
- super(History, self).__init__(seq)
159
+ """Initialize History instances."""
160
+ super().__init__(seq)
173
161
  self.session_start_index = 0
174
162
 
175
163
  def start_session(self) -> None:
@@ -196,7 +184,7 @@ class History(List[HistoryItem]):
196
184
  and added to the end of the list
197
185
  """
198
186
  history_item = HistoryItem(new) if isinstance(new, Statement) else new
199
- super(History, self).append(history_item)
187
+ super().append(history_item)
200
188
 
201
189
  def clear(self) -> None:
202
190
  """Remove all items from the History list."""
@@ -211,10 +199,9 @@ class History(List[HistoryItem]):
211
199
  """
212
200
  if index == 0:
213
201
  raise IndexError('The first command in history is command 1.')
214
- elif index < 0:
202
+ if index < 0:
215
203
  return self[index]
216
- else:
217
- return self[index - 1]
204
+ return self[index - 1]
218
205
 
219
206
  # This regular expression parses input for the span() method. There are five parts:
220
207
  #
@@ -243,7 +230,7 @@ class History(List[HistoryItem]):
243
230
  spanpattern = re.compile(r'^\s*(?P<start>-?[1-9]\d*)?(?P<separator>:|(\.{2,}))(?P<end>-?[1-9]\d*)?\s*$')
244
231
 
245
232
  def span(self, span: str, include_persisted: bool = False) -> 'OrderedDict[int, HistoryItem]':
246
- """Return a slice of the History list
233
+ """Return a slice of the History list.
247
234
 
248
235
  :param span: string containing an index or a slice
249
236
  :param include_persisted: if True, then retrieve full results including from persisted history
@@ -292,7 +279,7 @@ class History(List[HistoryItem]):
292
279
  return self._build_result_dictionary(start, end)
293
280
 
294
281
  def str_search(self, search: str, include_persisted: bool = False) -> 'OrderedDict[int, HistoryItem]':
295
- """Find history items which contain a given string
282
+ """Find history items which contain a given string.
296
283
 
297
284
  :param search: the string to search for
298
285
  :param include_persisted: if True, then search full history including persisted history
@@ -301,7 +288,7 @@ class History(List[HistoryItem]):
301
288
  """
302
289
 
303
290
  def isin(history_item: HistoryItem) -> bool:
304
- """filter function for string search of history"""
291
+ """Filter function for string search of history."""
305
292
  sloppy = utils.norm_fold(search)
306
293
  inraw = sloppy in utils.norm_fold(history_item.raw)
307
294
  inexpanded = sloppy in utils.norm_fold(history_item.expanded)
@@ -311,7 +298,7 @@ class History(List[HistoryItem]):
311
298
  return self._build_result_dictionary(start, len(self), isin)
312
299
 
313
300
  def regex_search(self, regex: str, include_persisted: bool = False) -> 'OrderedDict[int, HistoryItem]':
314
- """Find history items which match a given regular expression
301
+ """Find history items which match a given regular expression.
315
302
 
316
303
  :param regex: the regular expression to search for.
317
304
  :param include_persisted: if True, then search full history including persisted history
@@ -324,14 +311,14 @@ class History(List[HistoryItem]):
324
311
  finder = re.compile(regex, re.DOTALL | re.MULTILINE)
325
312
 
326
313
  def isin(hi: HistoryItem) -> bool:
327
- """filter function for doing a regular expression search of history"""
314
+ """Filter function for doing a regular expression search of history."""
328
315
  return bool(finder.search(hi.raw) or finder.search(hi.expanded))
329
316
 
330
317
  start = 0 if include_persisted else self.session_start_index
331
318
  return self._build_result_dictionary(start, len(self), isin)
332
319
 
333
320
  def truncate(self, max_length: int) -> None:
334
- """Truncate the length of the history, dropping the oldest items if necessary
321
+ """Truncate the length of the history, dropping the oldest items if necessary.
335
322
 
336
323
  :param max_length: the maximum length of the history, if negative, all history
337
324
  items will be deleted
@@ -347,10 +334,10 @@ class History(List[HistoryItem]):
347
334
  def _build_result_dictionary(
348
335
  self, start: int, end: int, filter_func: Optional[Callable[[HistoryItem], bool]] = None
349
336
  ) -> 'OrderedDict[int, HistoryItem]':
350
- """
351
- Build history search results
337
+ """Build history search results.
338
+
352
339
  :param start: start index to search from
353
- :param end: end index to stop searching (exclusive)
340
+ :param end: end index to stop searching (exclusive).
354
341
  """
355
342
  results: OrderedDict[int, HistoryItem] = OrderedDict()
356
343
  for index in range(start, end):
@@ -359,7 +346,7 @@ class History(List[HistoryItem]):
359
346
  return results
360
347
 
361
348
  def to_json(self) -> str:
362
- """Utility method to convert this History into a JSON string for use in persistent history files"""
349
+ """Convert this History into a JSON string for use in persistent history files."""
363
350
  json_dict = {
364
351
  History._history_version_field: History._history_version,
365
352
  History._history_items_field: [hi.to_dict() for hi in self],
@@ -368,8 +355,7 @@ class History(List[HistoryItem]):
368
355
 
369
356
  @staticmethod
370
357
  def from_json(history_json: str) -> 'History':
371
- """
372
- Utility method to restore History from a JSON string
358
+ """Restore History from a JSON string.
373
359
 
374
360
  :param history_json: history data as JSON string (generated using to_json())
375
361
  :return: History object
cmd2/parsing.py CHANGED
@@ -1,20 +1,15 @@
1
- #
2
- # -*- coding: utf-8 -*-
3
- """Statement parsing classes for cmd2"""
1
+ """Statement parsing classes for cmd2."""
4
2
 
5
3
  import re
6
4
  import shlex
5
+ from collections.abc import Iterable
7
6
  from dataclasses import (
8
7
  dataclass,
9
8
  field,
10
9
  )
11
10
  from typing import (
12
11
  Any,
13
- Dict,
14
- Iterable,
15
- List,
16
12
  Optional,
17
- Tuple,
18
13
  Union,
19
14
  )
20
15
 
@@ -27,9 +22,11 @@ from .exceptions import (
27
22
  )
28
23
 
29
24
 
30
- def shlex_split(str_to_split: str) -> List[str]:
31
- """
25
+ def shlex_split(str_to_split: str) -> list[str]:
26
+ """Split the string *str_to_split* using shell-like syntax.
27
+
32
28
  A wrapper around shlex.split() that uses cmd2's preferred arguments.
29
+
33
30
  This allows other classes to easily call split() the same way StatementParser does.
34
31
 
35
32
  :param str_to_split: the string being split
@@ -40,10 +37,10 @@ def shlex_split(str_to_split: str) -> List[str]:
40
37
 
41
38
  @dataclass(frozen=True)
42
39
  class MacroArg:
43
- """
44
- Information used to replace or unescape arguments in a macro value when the macro is resolved
40
+ """Information used to replace or unescape arguments in a macro value when the macro is resolved.
41
+
45
42
  Normal argument syntax: {5}
46
- Escaped argument syntax: {{5}}
43
+ Escaped argument syntax: {{5}}.
47
44
  """
48
45
 
49
46
  # The starting index of this argument in the macro value
@@ -73,7 +70,7 @@ class MacroArg:
73
70
 
74
71
  @dataclass(frozen=True)
75
72
  class Macro:
76
- """Defines a cmd2 macro"""
73
+ """Defines a cmd2 macro."""
77
74
 
78
75
  # Name of the macro
79
76
  name: str
@@ -85,11 +82,11 @@ class Macro:
85
82
  minimum_arg_count: int
86
83
 
87
84
  # Used to fill in argument placeholders in the macro
88
- arg_list: List[MacroArg] = field(default_factory=list)
85
+ arg_list: list[MacroArg] = field(default_factory=list)
89
86
 
90
87
 
91
88
  @dataclass(frozen=True)
92
- class Statement(str): # type: ignore[override]
89
+ class Statement(str): # type: ignore[override] # noqa: SLOT000
93
90
  """String subclass with additional attributes to store the results of parsing.
94
91
 
95
92
  The ``cmd`` module in the standard library passes commands around as a
@@ -129,7 +126,7 @@ class Statement(str): # type: ignore[override]
129
126
  command: str = ''
130
127
 
131
128
  # list of arguments to the command, not including any output redirection or terminators; quoted args remain quoted
132
- arg_list: List[str] = field(default_factory=list)
129
+ arg_list: list[str] = field(default_factory=list)
133
130
 
134
131
  # if the command is a multiline command, the name of the command, otherwise empty
135
132
  multiline_command: str = ''
@@ -152,7 +149,7 @@ class Statement(str): # type: ignore[override]
152
149
  # Used in JSON dictionaries
153
150
  _args_field = 'args'
154
151
 
155
- def __new__(cls, value: object, *pos_args: Any, **kw_args: Any) -> 'Statement':
152
+ def __new__(cls, value: object, *_pos_args: Any, **_kw_args: Any) -> 'Statement':
156
153
  """Create a new instance of Statement.
157
154
 
158
155
  We must override __new__ because we are subclassing `str` which is
@@ -161,8 +158,7 @@ class Statement(str): # type: ignore[override]
161
158
  NOTE: @dataclass takes care of initializing other members in the __init__ it
162
159
  generates.
163
160
  """
164
- stmt = super().__new__(cls, value)
165
- return stmt
161
+ return super().__new__(cls, value)
166
162
 
167
163
  @property
168
164
  def command_and_args(self) -> str:
@@ -182,7 +178,7 @@ class Statement(str): # type: ignore[override]
182
178
 
183
179
  @property
184
180
  def post_command(self) -> str:
185
- """A string containing any ending terminator, suffix, and redirection chars"""
181
+ """A string containing any ending terminator, suffix, and redirection chars."""
186
182
  rtn = ''
187
183
  if self.terminator:
188
184
  rtn += self.terminator
@@ -202,12 +198,12 @@ class Statement(str): # type: ignore[override]
202
198
 
203
199
  @property
204
200
  def expanded_command_line(self) -> str:
205
- """Concatenate [command_and_args][cmd2.Statement.command_and_args] and [post_command][cmd2.Statement.post_command]"""
201
+ """Concatenate [command_and_args][cmd2.Statement.command_and_args] and [post_command][cmd2.Statement.post_command]."""
206
202
  return self.command_and_args + self.post_command
207
203
 
208
204
  @property
209
- def argv(self) -> List[str]:
210
- """a list of arguments a-la ``sys.argv``.
205
+ def argv(self) -> list[str]:
206
+ """A list of arguments a-la ``sys.argv``.
211
207
 
212
208
  The first element of the list is the command after shortcut and macro
213
209
  expansion. Subsequent elements of the list contain any additional
@@ -218,21 +214,19 @@ class Statement(str): # type: ignore[override]
218
214
  """
219
215
  if self.command:
220
216
  rtn = [utils.strip_quotes(self.command)]
221
- for cur_token in self.arg_list:
222
- rtn.append(utils.strip_quotes(cur_token))
217
+ rtn.extend(utils.strip_quotes(cur_token) for cur_token in self.arg_list)
223
218
  else:
224
219
  rtn = []
225
220
 
226
221
  return rtn
227
222
 
228
- def to_dict(self) -> Dict[str, Any]:
229
- """Utility method to convert this Statement into a dictionary for use in persistent JSON history files"""
223
+ def to_dict(self) -> dict[str, Any]:
224
+ """Convert this Statement into a dictionary for use in persistent JSON history files."""
230
225
  return self.__dict__.copy()
231
226
 
232
227
  @staticmethod
233
- def from_dict(source_dict: Dict[str, Any]) -> 'Statement':
234
- """
235
- Utility method to restore a Statement from a dictionary
228
+ def from_dict(source_dict: dict[str, Any]) -> 'Statement':
229
+ """Restore a Statement from a dictionary.
236
230
 
237
231
  :param source_dict: source data dictionary (generated using to_dict())
238
232
  :return: Statement object
@@ -242,7 +236,7 @@ class Statement(str): # type: ignore[override]
242
236
  try:
243
237
  value = source_dict[Statement._args_field]
244
238
  except KeyError as ex:
245
- raise KeyError(f"Statement dictionary is missing {ex} field")
239
+ raise KeyError(f"Statement dictionary is missing {ex} field") from None
246
240
 
247
241
  # Pass the rest at kwargs (minus args)
248
242
  kwargs = source_dict.copy()
@@ -258,8 +252,8 @@ class StatementParser:
258
252
  self,
259
253
  terminators: Optional[Iterable[str]] = None,
260
254
  multiline_commands: Optional[Iterable[str]] = None,
261
- aliases: Optional[Dict[str, str]] = None,
262
- shortcuts: Optional[Dict[str, str]] = None,
255
+ aliases: Optional[dict[str, str]] = None,
256
+ shortcuts: Optional[dict[str, str]] = None,
263
257
  ) -> None:
264
258
  """Initialize an instance of StatementParser.
265
259
 
@@ -271,13 +265,13 @@ class StatementParser:
271
265
  :param aliases: dictionary containing aliases
272
266
  :param shortcuts: dictionary containing shortcuts
273
267
  """
274
- self.terminators: Tuple[str, ...]
268
+ self.terminators: tuple[str, ...]
275
269
  if terminators is None:
276
270
  self.terminators = (constants.MULTILINE_TERMINATOR,)
277
271
  else:
278
272
  self.terminators = tuple(terminators)
279
- self.multiline_commands: Tuple[str, ...] = tuple(multiline_commands) if multiline_commands is not None else ()
280
- self.aliases: Dict[str, str] = aliases if aliases is not None else {}
273
+ self.multiline_commands: tuple[str, ...] = tuple(multiline_commands) if multiline_commands is not None else ()
274
+ self.aliases: dict[str, str] = aliases if aliases is not None else {}
281
275
 
282
276
  if shortcuts is None:
283
277
  shortcuts = constants.DEFAULT_SHORTCUTS
@@ -318,7 +312,7 @@ class StatementParser:
318
312
  expr = rf'\A\s*(\S*?)({second_group})'
319
313
  self._command_pattern = re.compile(expr)
320
314
 
321
- def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> Tuple[bool, str]:
315
+ def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> tuple[bool, str]:
322
316
  """Determine whether a word is a valid name for a command.
323
317
 
324
318
  Commands cannot include redirection characters, whitespace,
@@ -340,7 +334,7 @@ class StatementParser:
340
334
  valid = False
341
335
 
342
336
  if not isinstance(word, str):
343
- return False, f'must be a string. Received {str(type(word))} instead' # type: ignore[unreachable]
337
+ return False, f'must be a string. Received {type(word)!s} instead' # type: ignore[unreachable]
344
338
 
345
339
  if not word:
346
340
  return False, 'cannot be an empty string'
@@ -363,22 +357,18 @@ class StatementParser:
363
357
  errmsg += ', '.join([shlex.quote(x) for x in errchars])
364
358
 
365
359
  match = self._command_pattern.search(word)
366
- if match:
367
- if word == match.group(1):
368
- valid = True
369
- errmsg = ''
360
+ if match and word == match.group(1):
361
+ valid = True
362
+ errmsg = ''
370
363
  return valid, errmsg
371
364
 
372
- def tokenize(self, line: str) -> List[str]:
373
- """
374
- Lex a string into a list of tokens. Shortcuts and aliases are expanded and
375
- comments are removed.
365
+ def tokenize(self, line: str) -> list[str]:
366
+ """Lex a string into a list of tokens. Shortcuts and aliases are expanded and comments are removed.
376
367
 
377
368
  :param line: the command line being lexed
378
369
  :return: A list of tokens
379
370
  :raises Cmd2ShlexError: if a shlex error occurs (e.g. No closing quotation)
380
371
  """
381
-
382
372
  # expand shortcuts and aliases
383
373
  line = self._expand(line)
384
374
 
@@ -390,23 +380,20 @@ class StatementParser:
390
380
  try:
391
381
  tokens = shlex_split(line)
392
382
  except ValueError as ex:
393
- raise Cmd2ShlexError(ex)
383
+ raise Cmd2ShlexError(ex) from None
394
384
 
395
385
  # custom lexing
396
- tokens = self.split_on_punctuation(tokens)
397
- return tokens
386
+ return self.split_on_punctuation(tokens)
398
387
 
399
388
  def parse(self, line: str) -> Statement:
400
- """
401
- Tokenize the input and parse it into a [cmd2.parsing.Statement][] object,
402
- stripping comments, expanding aliases and shortcuts, and extracting output
403
- redirection directives.
389
+ """Tokenize the input and parse it into a [cmd2.parsing.Statement][] object.
390
+
391
+ Stripping comments, expanding aliases and shortcuts, and extracting output redirection directives.
404
392
 
405
393
  :param line: the command line being parsed
406
394
  :return: a new [cmd2.parsing.Statement][] object
407
395
  :raises Cmd2ShlexError: if a shlex error occurs (e.g. No closing quotation)
408
396
  """
409
-
410
397
  # handle the special case/hardcoded terminator of a blank line
411
398
  # we have to do this before we tokenize because tokenizing
412
399
  # destroys all unquoted whitespace in the input
@@ -522,13 +509,10 @@ class StatementParser:
522
509
  arg_list = tokens[1:]
523
510
 
524
511
  # set multiline
525
- if command in self.multiline_commands:
526
- multiline_command = command
527
- else:
528
- multiline_command = ''
512
+ multiline_command = command if command in self.multiline_commands else ''
529
513
 
530
514
  # build the statement
531
- statement = Statement(
515
+ return Statement(
532
516
  args,
533
517
  raw=line,
534
518
  command=command,
@@ -540,10 +524,9 @@ class StatementParser:
540
524
  output=output,
541
525
  output_to=output_to,
542
526
  )
543
- return statement
544
527
 
545
528
  def parse_command_only(self, rawinput: str) -> Statement:
546
- """Partially parse input into a [cmd2.Statement][] object.
529
+ """Parse input into a [cmd2.Statement][] object (partially).
547
530
 
548
531
  The command is identified, and shortcuts and aliases are expanded.
549
532
  Multiline commands are identified, but terminators and output
@@ -596,22 +579,17 @@ class StatementParser:
596
579
  args = ''
597
580
 
598
581
  # set multiline
599
- if command in self.multiline_commands:
600
- multiline_command = command
601
- else:
602
- multiline_command = ''
582
+ multiline_command = command if command in self.multiline_commands else ''
603
583
 
604
584
  # build the statement
605
- statement = Statement(args, raw=rawinput, command=command, multiline_command=multiline_command)
606
- return statement
585
+ return Statement(args, raw=rawinput, command=command, multiline_command=multiline_command)
607
586
 
608
587
  def get_command_arg_list(
609
588
  self, command_name: str, to_parse: Union[Statement, str], preserve_quotes: bool
610
- ) -> Tuple[Statement, List[str]]:
611
- """
612
- Convenience method used by the argument parsing decorators.
589
+ ) -> tuple[Statement, list[str]]:
590
+ """Retrieve just the arguments being passed to their ``do_*`` methods as a list.
613
591
 
614
- Retrieves just the arguments being passed to their ``do_*`` methods as a list.
592
+ Convenience method used by the argument parsing decorators.
615
593
 
616
594
  :param command_name: name of the command being run
617
595
  :param to_parse: what is being passed to the ``do_*`` method. It can be one of two types:
@@ -636,12 +614,10 @@ class StatementParser:
636
614
 
637
615
  if preserve_quotes:
638
616
  return to_parse, to_parse.arg_list
639
- else:
640
- return to_parse, to_parse.argv[1:]
617
+ return to_parse, to_parse.argv[1:]
641
618
 
642
619
  def _expand(self, line: str) -> str:
643
- """Expand aliases and shortcuts"""
644
-
620
+ """Expand aliases and shortcuts."""
645
621
  # Make a copy of aliases so we can keep track of what aliases have been resolved to avoid an infinite loop
646
622
  remaining_aliases = list(self.aliases.keys())
647
623
  keep_expanding = bool(remaining_aliases)
@@ -667,19 +643,18 @@ class StatementParser:
667
643
  if line.startswith(shortcut):
668
644
  # If the next character after the shortcut isn't a space, then insert one
669
645
  shortcut_len = len(shortcut)
646
+ effective_expansion = expansion
670
647
  if len(line) == shortcut_len or line[shortcut_len] != ' ':
671
- expansion += ' '
648
+ effective_expansion += ' '
672
649
 
673
650
  # Expand the shortcut
674
- line = line.replace(shortcut, expansion, 1)
651
+ line = line.replace(shortcut, effective_expansion, 1)
675
652
  break
676
653
  return line
677
654
 
678
655
  @staticmethod
679
- def _command_and_args(tokens: List[str]) -> Tuple[str, str]:
680
- """Given a list of tokens, return a tuple of the command
681
- and the args as a string.
682
- """
656
+ def _command_and_args(tokens: list[str]) -> tuple[str, str]:
657
+ """Given a list of tokens, return a tuple of the command and the args as a string."""
683
658
  command = ''
684
659
  args = ''
685
660
 
@@ -691,7 +666,7 @@ class StatementParser:
691
666
 
692
667
  return command, args
693
668
 
694
- def split_on_punctuation(self, tokens: List[str]) -> List[str]:
669
+ def split_on_punctuation(self, tokens: list[str]) -> list[str]:
695
670
  """Further splits tokens from a command line using punctuation characters.
696
671
 
697
672
  Punctuation characters are treated as word breaks when they are in
@@ -701,7 +676,7 @@ class StatementParser:
701
676
  :param tokens: the tokens as parsed by shlex
702
677
  :return: a new list of tokens, further split using punctuation
703
678
  """
704
- punctuation: List[str] = []
679
+ punctuation: list[str] = []
705
680
  punctuation.extend(self.terminators)
706
681
  punctuation.extend(constants.REDIRECTION_CHARS)
707
682
 
cmd2/plugin.py CHANGED
@@ -1,13 +1,9 @@
1
- #
2
- # coding=utf-8
3
- """Classes for the cmd2 plugin system"""
1
+ """Classes for the cmd2 plugin system."""
4
2
 
5
3
  from dataclasses import (
6
4
  dataclass,
7
5
  )
8
- from typing import (
9
- Optional,
10
- )
6
+ from typing import Optional
11
7
 
12
8
  from .parsing import (
13
9
  Statement,
@@ -16,7 +12,7 @@ from .parsing import (
16
12
 
17
13
  @dataclass
18
14
  class PostparsingData:
19
- """Data class containing information passed to postparsing hook methods"""
15
+ """Data class containing information passed to postparsing hook methods."""
20
16
 
21
17
  stop: bool
22
18
  statement: Statement
@@ -24,14 +20,14 @@ class PostparsingData:
24
20
 
25
21
  @dataclass
26
22
  class PrecommandData:
27
- """Data class containing information passed to precommand hook methods"""
23
+ """Data class containing information passed to precommand hook methods."""
28
24
 
29
25
  statement: Statement
30
26
 
31
27
 
32
28
  @dataclass
33
29
  class PostcommandData:
34
- """Data class containing information passed to postcommand hook methods"""
30
+ """Data class containing information passed to postcommand hook methods."""
35
31
 
36
32
  stop: bool
37
33
  statement: Statement
@@ -39,7 +35,7 @@ class PostcommandData:
39
35
 
40
36
  @dataclass
41
37
  class CommandFinalizationData:
42
- """Data class containing information passed to command finalization hook methods"""
38
+ """Data class containing information passed to command finalization hook methods."""
43
39
 
44
40
  stop: bool
45
41
  statement: Optional[Statement]