cmd2 2.5.11__py3-none-any.whl → 2.6.1__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.
@@ -1,8 +1,5 @@
1
- # coding=utf-8
2
- # flake8: noqa C901
3
- # NOTE: Ignoring flake8 cyclomatic complexity in this file
4
- """
5
- This module defines the ArgparseCompleter class which provides argparse-based tab completion to cmd2 apps.
1
+ """Module efines the ArgparseCompleter class which provides argparse-based tab completion to cmd2 apps.
2
+
6
3
  See the header of argparse_custom.py for instructions on how to use these features.
7
4
  """
8
5
 
@@ -14,10 +11,7 @@ from collections import (
14
11
  )
15
12
  from typing import (
16
13
  TYPE_CHECKING,
17
- Dict,
18
- List,
19
14
  Optional,
20
- Type,
21
15
  Union,
22
16
  cast,
23
17
  )
@@ -62,29 +56,31 @@ ARG_TOKENS = 'arg_tokens'
62
56
 
63
57
 
64
58
  def _build_hint(parser: argparse.ArgumentParser, arg_action: argparse.Action) -> str:
65
- """Build tab completion hint for a given argument"""
59
+ """Build tab completion hint for a given argument."""
66
60
  # Check if hinting is disabled for this argument
67
61
  suppress_hint = arg_action.get_suppress_tab_hint() # type: ignore[attr-defined]
68
62
  if suppress_hint or arg_action.help == argparse.SUPPRESS:
69
63
  return ''
70
- else:
71
- # Use the parser's help formatter to display just this action's help text
72
- formatter = parser._get_formatter()
73
- formatter.start_section("Hint")
74
- formatter.add_argument(arg_action)
75
- formatter.end_section()
76
- return formatter.format_help()
64
+
65
+ # Use the parser's help formatter to display just this action's help text
66
+ formatter = parser._get_formatter()
67
+ formatter.start_section("Hint")
68
+ formatter.add_argument(arg_action)
69
+ formatter.end_section()
70
+ return formatter.format_help()
77
71
 
78
72
 
79
73
  def _single_prefix_char(token: str, parser: argparse.ArgumentParser) -> bool:
80
- """Returns if a token is just a single flag prefix character"""
74
+ """Is a token just a single flag prefix character."""
81
75
  return len(token) == 1 and token[0] in parser.prefix_chars
82
76
 
83
77
 
84
78
  def _looks_like_flag(token: str, parser: argparse.ArgumentParser) -> bool:
85
- """
86
- Determine if a token looks like a flag. Unless an argument has nargs set to argparse.REMAINDER,
87
- then anything that looks like a flag can't be consumed as a value for it.
79
+ """Determine if a token looks like a flag.
80
+
81
+ Unless an argument has nargs set to argparse.REMAINDER, then anything that looks like a flag
82
+ can't be consumed as a value for it.
83
+
88
84
  Based on argparse._parse_optional().
89
85
  """
90
86
  # Flags have to be at least characters
@@ -96,20 +92,15 @@ def _looks_like_flag(token: str, parser: argparse.ArgumentParser) -> bool:
96
92
  return False
97
93
 
98
94
  # If it looks like a negative number, it is not a flag unless there are negative-number-like flags
99
- if parser._negative_number_matcher.match(token):
100
- if not parser._has_negative_number_optionals:
101
- return False
102
-
103
- # Flags can't have a space
104
- if ' ' in token:
95
+ if parser._negative_number_matcher.match(token) and not parser._has_negative_number_optionals:
105
96
  return False
106
97
 
107
- # Starts like a flag
108
- return True
98
+ # Flags can't have a space
99
+ return ' ' not in token
109
100
 
110
101
 
111
102
  class _ArgumentState:
112
- """Keeps state of an argument being parsed"""
103
+ """Keeps state of an argument being parsed."""
113
104
 
114
105
  def __init__(self, arg_action: argparse.Action) -> None:
115
106
  self.action = arg_action
@@ -131,7 +122,7 @@ class _ArgumentState:
131
122
  elif self.action.nargs == argparse.OPTIONAL:
132
123
  self.min = 0
133
124
  self.max = 1
134
- elif self.action.nargs == argparse.ZERO_OR_MORE or self.action.nargs == argparse.REMAINDER:
125
+ elif self.action.nargs in (argparse.ZERO_OR_MORE, argparse.REMAINDER):
135
126
  self.min = 0
136
127
  self.max = INFINITY
137
128
  elif self.action.nargs == argparse.ONE_OR_MORE:
@@ -144,38 +135,36 @@ class _ArgumentState:
144
135
 
145
136
  class _UnfinishedFlagError(CompletionError):
146
137
  def __init__(self, flag_arg_state: _ArgumentState) -> None:
138
+ """CompletionError which occurs when the user has not finished the current flag.
139
+
140
+ :param flag_arg_state: information about the unfinished flag action.
147
141
  """
148
- CompletionError which occurs when the user has not finished the current flag
149
- :param flag_arg_state: information about the unfinished flag action
150
- """
151
- error = "Error: argument {}: {} ({} entered)".format(
152
- argparse._get_action_name(flag_arg_state.action),
153
- generate_range_error(cast(int, flag_arg_state.min), cast(Union[int, float], flag_arg_state.max)),
154
- flag_arg_state.count,
155
- )
142
+ arg = f'{argparse._get_action_name(flag_arg_state.action)}'
143
+ err = f'{generate_range_error(cast(int, flag_arg_state.min), cast(Union[int, float], flag_arg_state.max))}'
144
+ error = f"Error: argument {arg}: {err} ({flag_arg_state.count} entered)"
156
145
  super().__init__(error)
157
146
 
158
147
 
159
148
  class _NoResultsError(CompletionError):
160
149
  def __init__(self, parser: argparse.ArgumentParser, arg_action: argparse.Action) -> None:
161
- """
162
- CompletionError which occurs when there are no results. If hinting is allowed, then its message will
163
- be a hint about the argument being tab completed.
150
+ """CompletionError which occurs when there are no results.
151
+
152
+ If hinting is allowed, then its message will be a hint about the argument being tab completed.
153
+
164
154
  :param parser: ArgumentParser instance which owns the action being tab completed
165
- :param arg_action: action being tab completed
155
+ :param arg_action: action being tab completed.
166
156
  """
167
157
  # Set apply_style to False because we don't want hints to look like errors
168
158
  super().__init__(_build_hint(parser, arg_action), apply_style=False)
169
159
 
170
160
 
171
161
  class ArgparseCompleter:
172
- """Automatic command line tab completion based on argparse parameters"""
162
+ """Automatic command line tab completion based on argparse parameters."""
173
163
 
174
164
  def __init__(
175
- self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens: Optional[Dict[str, List[str]]] = None
165
+ self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens: Optional[dict[str, list[str]]] = None
176
166
  ) -> None:
177
- """
178
- Create an ArgparseCompleter
167
+ """Create an ArgparseCompleter.
179
168
 
180
169
  :param parser: ArgumentParser instance
181
170
  :param cmd2_app: reference to the Cmd2 application that owns this ArgparseCompleter
@@ -187,7 +176,7 @@ class ArgparseCompleter:
187
176
  self._cmd2_app = cmd2_app
188
177
 
189
178
  if parent_tokens is None:
190
- parent_tokens = dict()
179
+ parent_tokens = {}
191
180
  self._parent_tokens = parent_tokens
192
181
 
193
182
  self._flags = [] # all flags in this command
@@ -213,10 +202,9 @@ class ArgparseCompleter:
213
202
  self._subcommand_action = action
214
203
 
215
204
  def complete(
216
- self, text: str, line: str, begidx: int, endidx: int, tokens: List[str], *, cmd_set: Optional[CommandSet] = None
217
- ) -> List[str]:
218
- """
219
- Complete text using argparse metadata
205
+ self, text: str, line: str, begidx: int, endidx: int, tokens: list[str], *, cmd_set: Optional[CommandSet] = None
206
+ ) -> list[str]:
207
+ """Complete text using argparse metadata.
220
208
 
221
209
  :param text: the string prefix we are attempting to match (all matches must begin with it)
222
210
  :param line: the current input line with leading whitespace removed
@@ -245,26 +233,27 @@ class ArgparseCompleter:
245
233
  flag_arg_state: Optional[_ArgumentState] = None
246
234
 
247
235
  # Non-reusable flags that we've parsed
248
- matched_flags: List[str] = []
236
+ matched_flags: list[str] = []
249
237
 
250
238
  # Keeps track of arguments we've seen and any tokens they consumed
251
- consumed_arg_values: Dict[str, List[str]] = dict() # dict(arg_name -> List[tokens])
239
+ consumed_arg_values: dict[str, list[str]] = {} # dict(arg_name -> list[tokens])
252
240
 
253
241
  # Completed mutually exclusive groups
254
- completed_mutex_groups: Dict[argparse._MutuallyExclusiveGroup, argparse.Action] = dict()
242
+ completed_mutex_groups: dict[argparse._MutuallyExclusiveGroup, argparse.Action] = {}
255
243
 
256
244
  def consume_argument(arg_state: _ArgumentState) -> None:
257
- """Consuming token as an argument"""
245
+ """Consuming token as an argument."""
258
246
  arg_state.count += 1
259
247
  consumed_arg_values.setdefault(arg_state.action.dest, [])
260
248
  consumed_arg_values[arg_state.action.dest].append(token)
261
249
 
262
250
  def update_mutex_groups(arg_action: argparse.Action) -> None:
263
- """
264
- Check if an argument belongs to a mutually exclusive group and either mark that group
265
- as complete or print an error if the group has already been completed
251
+ """Check if an argument belongs to a mutually exclusive group potenitally mark that group complete.
252
+
253
+ Either mark the group as complete or print an error if the group has already been completed.
254
+
266
255
  :param arg_action: the action of the argument
267
- :raises CompletionError: if the group is already completed
256
+ :raises CompletionError: if the group is already completed.
268
257
  """
269
258
  # Check if this action is in a mutually exclusive group
270
259
  for group in self._parser._mutually_exclusive_groups:
@@ -277,9 +266,9 @@ class ArgparseCompleter:
277
266
  if arg_action == completer_action:
278
267
  return
279
268
 
280
- error = "Error: argument {}: not allowed with argument {}".format(
281
- argparse._get_action_name(arg_action), argparse._get_action_name(completer_action)
282
- )
269
+ arg_str = f'{argparse._get_action_name(arg_action)}'
270
+ completer_str = f'{argparse._get_action_name(completer_action)}'
271
+ error = f"Error: argument {arg_str}: not allowed with argument {completer_str}"
283
272
  raise CompletionError(error)
284
273
 
285
274
  # Mark that this action completed the group
@@ -289,7 +278,7 @@ class ArgparseCompleter:
289
278
  for group_action in group._group_actions:
290
279
  if group_action == arg_action:
291
280
  continue
292
- elif group_action in self._flag_to_action.values():
281
+ if group_action in self._flag_to_action.values():
293
282
  matched_flags.extend(group_action.option_strings)
294
283
  elif group_action in remaining_positionals:
295
284
  remaining_positionals.remove(group_action)
@@ -307,15 +296,15 @@ class ArgparseCompleter:
307
296
  continue
308
297
 
309
298
  # If we're in a flag REMAINDER arg, force all future tokens to go to that until a double dash is hit
310
- elif flag_arg_state is not None and flag_arg_state.is_remainder:
311
- if token == '--':
299
+ if flag_arg_state is not None and flag_arg_state.is_remainder:
300
+ if token == '--': # noqa: S105
312
301
  flag_arg_state = None
313
302
  else:
314
303
  consume_argument(flag_arg_state)
315
304
  continue
316
305
 
317
306
  # Handle '--' which tells argparse all remaining arguments are non-flags
318
- elif token == '--' and not skip_remaining_flags:
307
+ if token == '--' and not skip_remaining_flags: # noqa: S105
319
308
  # Check if there is an unfinished flag
320
309
  if (
321
310
  flag_arg_state is not None
@@ -325,10 +314,9 @@ class ArgparseCompleter:
325
314
  raise _UnfinishedFlagError(flag_arg_state)
326
315
 
327
316
  # Otherwise end the current flag
328
- else:
329
- flag_arg_state = None
330
- skip_remaining_flags = True
331
- continue
317
+ flag_arg_state = None
318
+ skip_remaining_flags = True
319
+ continue
332
320
 
333
321
  # Check the format of the current token to see if it can be an argument's value
334
322
  if _looks_like_flag(token, self._parser) and not skip_remaining_flags:
@@ -385,36 +373,30 @@ class ArgparseCompleter:
385
373
  # Otherwise treat as a positional argument
386
374
  else:
387
375
  # If we aren't current tracking a positional, then get the next positional arg to handle this token
388
- if pos_arg_state is None:
389
- # Make sure we are still have positional arguments to parse
390
- if remaining_positionals:
391
- action = remaining_positionals.popleft()
376
+ if pos_arg_state is None and remaining_positionals:
377
+ action = remaining_positionals.popleft()
392
378
 
393
- # Are we at a subcommand? If so, forward to the matching completer
394
- if action == self._subcommand_action:
395
- if token in self._subcommand_action.choices:
396
- # Merge self._parent_tokens and consumed_arg_values
397
- parent_tokens = {**self._parent_tokens, **consumed_arg_values}
379
+ # Are we at a subcommand? If so, forward to the matching completer
380
+ if action == self._subcommand_action:
381
+ if token in self._subcommand_action.choices:
382
+ # Merge self._parent_tokens and consumed_arg_values
383
+ parent_tokens = {**self._parent_tokens, **consumed_arg_values}
398
384
 
399
- # Include the subcommand name if its destination was set
400
- if action.dest != argparse.SUPPRESS:
401
- parent_tokens[action.dest] = [token]
385
+ # Include the subcommand name if its destination was set
386
+ if action.dest != argparse.SUPPRESS:
387
+ parent_tokens[action.dest] = [token]
402
388
 
403
- parser: argparse.ArgumentParser = self._subcommand_action.choices[token]
404
- completer_type = self._cmd2_app._determine_ap_completer_type(parser)
389
+ parser: argparse.ArgumentParser = self._subcommand_action.choices[token]
390
+ completer_type = self._cmd2_app._determine_ap_completer_type(parser)
405
391
 
406
- completer = completer_type(parser, self._cmd2_app, parent_tokens=parent_tokens)
392
+ completer = completer_type(parser, self._cmd2_app, parent_tokens=parent_tokens)
407
393
 
408
- return completer.complete(
409
- text, line, begidx, endidx, tokens[token_index + 1 :], cmd_set=cmd_set
410
- )
411
- else:
412
- # Invalid subcommand entered, so no way to complete remaining tokens
413
- return []
394
+ return completer.complete(text, line, begidx, endidx, tokens[token_index + 1 :], cmd_set=cmd_set)
395
+ # Invalid subcommand entered, so no way to complete remaining tokens
396
+ return []
414
397
 
415
- # Otherwise keep track of the argument
416
- else:
417
- pos_arg_state = _ArgumentState(action)
398
+ # Otherwise keep track of the argument
399
+ pos_arg_state = _ArgumentState(action)
418
400
 
419
401
  # Check if we have a positional to consume this token
420
402
  if pos_arg_state is not None:
@@ -467,7 +449,7 @@ class ArgparseCompleter:
467
449
  return completion_results
468
450
 
469
451
  # Otherwise, print a hint if the flag isn't finished or text isn't possibly the start of a flag
470
- elif (
452
+ if (
471
453
  (isinstance(flag_arg_state.min, int) and flag_arg_state.count < flag_arg_state.min)
472
454
  or not _single_prefix_char(text, self._parser)
473
455
  or skip_remaining_flags
@@ -493,7 +475,7 @@ class ArgparseCompleter:
493
475
  return completion_results
494
476
 
495
477
  # Otherwise, print a hint if text isn't possibly the start of a flag
496
- elif not _single_prefix_char(text, self._parser) or skip_remaining_flags:
478
+ if not _single_prefix_char(text, self._parser) or skip_remaining_flags:
497
479
  raise _NoResultsError(self._parser, pos_arg_state.action)
498
480
 
499
481
  # If we aren't skipping remaining flags, then complete flag names if either is True:
@@ -507,9 +489,8 @@ class ArgparseCompleter:
507
489
 
508
490
  return completion_results
509
491
 
510
- def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matched_flags: List[str]) -> List[str]:
511
- """Tab completion routine for a parsers unused flags"""
512
-
492
+ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matched_flags: list[str]) -> list[str]:
493
+ """Tab completion routine for a parsers unused flags."""
513
494
  # Build a list of flags that can be tab completed
514
495
  match_against = []
515
496
 
@@ -524,7 +505,7 @@ class ArgparseCompleter:
524
505
  matches = self._cmd2_app.basic_complete(text, line, begidx, endidx, match_against)
525
506
 
526
507
  # Build a dictionary linking actions with their matched flag names
527
- matched_actions: Dict[argparse.Action, List[str]] = dict()
508
+ matched_actions: dict[argparse.Action, list[str]] = {}
528
509
  for flag in matches:
529
510
  action = self._flag_to_action[flag]
530
511
  matched_actions.setdefault(action, [])
@@ -541,14 +522,13 @@ class ArgparseCompleter:
541
522
 
542
523
  return matches
543
524
 
544
- def _format_completions(self, arg_state: _ArgumentState, completions: Union[List[str], List[CompletionItem]]) -> List[str]:
545
- """Format CompletionItems into hint table"""
546
-
525
+ def _format_completions(self, arg_state: _ArgumentState, completions: Union[list[str], list[CompletionItem]]) -> list[str]:
526
+ """Format CompletionItems into hint table."""
547
527
  # Nothing to do if we don't have at least 2 completions which are all CompletionItems
548
528
  if len(completions) < 2 or not all(isinstance(c, CompletionItem) for c in completions):
549
- return cast(List[str], completions)
529
+ return cast(list[str], completions)
550
530
 
551
- completion_items = cast(List[CompletionItem], completions)
531
+ completion_items = cast(list[CompletionItem], completions)
552
532
 
553
533
  # Check if the data being completed have a numerical type
554
534
  all_nums = all(isinstance(c.orig_value, numbers.Number) for c in completion_items)
@@ -599,7 +579,7 @@ class ArgparseCompleter:
599
579
  item.description = item.description.replace('\t', four_spaces)
600
580
  desc_width = max(widest_line(item.description), desc_width)
601
581
 
602
- cols = list()
582
+ cols = []
603
583
  dest_alignment = HorizontalAlignment.RIGHT if all_nums else HorizontalAlignment.LEFT
604
584
  cols.append(
605
585
  Column(
@@ -616,17 +596,17 @@ class ArgparseCompleter:
616
596
  self._cmd2_app.formatted_completions = hint_table.generate_table(table_data, row_spacing=0)
617
597
 
618
598
  # Return sorted list of completions
619
- return cast(List[str], completions)
599
+ return cast(list[str], completions)
600
+
601
+ def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: int, tokens: list[str]) -> list[str]:
602
+ """Supports cmd2's help command in the completion of subcommand names.
620
603
 
621
- def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: int, tokens: List[str]) -> List[str]:
622
- """
623
- Supports cmd2's help command in the completion of subcommand names
624
604
  :param text: the string prefix we are attempting to match (all matches must begin with it)
625
605
  :param line: the current input line with leading whitespace removed
626
606
  :param begidx: the beginning index of the prefix text
627
607
  :param endidx: the ending index of the prefix text
628
608
  :param tokens: arguments passed to command/subcommand
629
- :return: List of subcommand completions
609
+ :return: list of subcommand completions.
630
610
  """
631
611
  # If our parser has subcommands, we must examine the tokens and check if they are subcommands
632
612
  # If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter.
@@ -638,18 +618,17 @@ class ArgparseCompleter:
638
618
 
639
619
  completer = completer_type(parser, self._cmd2_app)
640
620
  return completer.complete_subcommand_help(text, line, begidx, endidx, tokens[token_index + 1 :])
641
- elif token_index == len(tokens) - 1:
621
+ if token_index == len(tokens) - 1:
642
622
  # Since this is the last token, we will attempt to complete it
643
623
  return self._cmd2_app.basic_complete(text, line, begidx, endidx, self._subcommand_action.choices)
644
- else:
645
- break
624
+ break
646
625
  return []
647
626
 
648
- def format_help(self, tokens: List[str]) -> str:
649
- """
650
- Supports cmd2's help command in the retrieval of help text
627
+ def format_help(self, tokens: list[str]) -> str:
628
+ """Supports cmd2's help command in the retrieval of help text.
629
+
651
630
  :param tokens: arguments passed to help command
652
- :return: help text of the command being queried
631
+ :return: help text of the command being queried.
653
632
  """
654
633
  # If our parser has subcommands, we must examine the tokens and check if they are subcommands
655
634
  # If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter.
@@ -661,8 +640,7 @@ class ArgparseCompleter:
661
640
 
662
641
  completer = completer_type(parser, self._cmd2_app)
663
642
  return completer.format_help(tokens[token_index + 1 :])
664
- else:
665
- break
643
+ break
666
644
  return self._parser.format_help()
667
645
 
668
646
  def _complete_arg(
@@ -672,17 +650,17 @@ class ArgparseCompleter:
672
650
  begidx: int,
673
651
  endidx: int,
674
652
  arg_state: _ArgumentState,
675
- consumed_arg_values: Dict[str, List[str]],
653
+ consumed_arg_values: dict[str, list[str]],
676
654
  *,
677
655
  cmd_set: Optional[CommandSet] = None,
678
- ) -> List[str]:
679
- """
680
- Tab completion routine for an argparse argument
656
+ ) -> list[str]:
657
+ """Tab completion routine for an argparse argument.
658
+
681
659
  :return: list of completions
682
- :raises CompletionError: if the completer or choices function this calls raises one
660
+ :raises CompletionError: if the completer or choices function this calls raises one.
683
661
  """
684
662
  # Check if the arg provides choices to the user
685
- arg_choices: Union[List[str], ChoicesCallable]
663
+ arg_choices: Union[list[str], ChoicesCallable]
686
664
  if arg_state.action.choices is not None:
687
665
  arg_choices = list(arg_state.action.choices)
688
666
  if not arg_choices:
@@ -739,7 +717,7 @@ class ArgparseCompleter:
739
717
  # Otherwise use basic_complete on the choices
740
718
  else:
741
719
  # Check if the choices come from a function
742
- completion_items: List[str] = []
720
+ completion_items: list[str] = []
743
721
  if isinstance(arg_choices, ChoicesCallable):
744
722
  if not arg_choices.is_completer:
745
723
  choices_func = arg_choices.choices_provider
@@ -771,14 +749,13 @@ class ArgparseCompleter:
771
749
 
772
750
 
773
751
  # The default ArgparseCompleter class for a cmd2 app
774
- DEFAULT_AP_COMPLETER: Type[ArgparseCompleter] = ArgparseCompleter
752
+ DEFAULT_AP_COMPLETER: type[ArgparseCompleter] = ArgparseCompleter
775
753
 
776
754
 
777
- def set_default_ap_completer_type(completer_type: Type[ArgparseCompleter]) -> None:
778
- """
779
- Set the default ArgparseCompleter class for a cmd2 app.
755
+ def set_default_ap_completer_type(completer_type: type[ArgparseCompleter]) -> None:
756
+ """Set the default ArgparseCompleter class for a cmd2 app.
780
757
 
781
758
  :param completer_type: Type that is a subclass of ArgparseCompleter.
782
759
  """
783
- global DEFAULT_AP_COMPLETER
760
+ global DEFAULT_AP_COMPLETER # noqa: PLW0603
784
761
  DEFAULT_AP_COMPLETER = completer_type