1313 deque ,
1414)
1515from typing import (
16+ Any ,
17+ cast ,
1618 Dict ,
1719 List ,
1820 Optional ,
@@ -106,8 +108,8 @@ class _ArgumentState:
106108
107109 def __init__ (self , arg_action : argparse .Action ) -> None :
108110 self .action = arg_action
109- self .min = None
110- self .max = None
111+ self .min : Union [ int , str ]
112+ self .max : Union [ float , int , str ]
111113 self .count = 0
112114 self .is_remainder = self .action .nargs == argparse .REMAINDER
113115
@@ -144,7 +146,7 @@ def __init__(self, flag_arg_state: _ArgumentState) -> None:
144146 """
145147 error = "Error: argument {}: {} ({} entered)" .format (
146148 argparse ._get_action_name (flag_arg_state .action ),
147- generate_range_error (flag_arg_state .min , flag_arg_state .max ),
149+ generate_range_error (cast ( int , flag_arg_state .min ), cast ( Union [ int , float ], flag_arg_state .max ) ),
148150 flag_arg_state .count ,
149151 )
150152 super ().__init__ (error )
@@ -234,19 +236,19 @@ def complete(
234236 skip_remaining_flags = False
235237
236238 # _ArgumentState of the current positional
237- pos_arg_state = None
239+ pos_arg_state : Optional [ _ArgumentState ] = None
238240
239241 # _ArgumentState of the current flag
240- flag_arg_state = None
242+ flag_arg_state : Optional [ _ArgumentState ] = None
241243
242244 # Non-reusable flags that we've parsed
243- matched_flags = []
245+ matched_flags : List [ str ] = []
244246
245247 # Keeps track of arguments we've seen and any tokens they consumed
246- consumed_arg_values = dict () # dict(arg_name -> List[tokens])
248+ consumed_arg_values : Dict [ str , List [ str ]] = dict () # dict(arg_name -> List[tokens])
247249
248250 # Completed mutually exclusive groups
249- completed_mutex_groups = dict () # dict( argparse._MutuallyExclusiveGroup -> Action which completed group )
251+ completed_mutex_groups : Dict [ argparse ._MutuallyExclusiveGroup , argparse . Action ] = dict ( )
250252
251253 def consume_argument (arg_state : _ArgumentState ) -> None :
252254 """Consuming token as an argument"""
@@ -315,7 +317,9 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
315317 # Handle '--' which tells argparse all remaining arguments are non-flags
316318 elif token == '--' and not skip_remaining_flags :
317319 # Check if there is an unfinished flag
318- if flag_arg_state is not None and flag_arg_state .count < flag_arg_state .min :
320+ if flag_arg_state is not None \
321+ and isinstance (flag_arg_state .min , int ) \
322+ and flag_arg_state .count < flag_arg_state .min :
319323 raise _UnfinishedFlagError (flag_arg_state )
320324
321325 # Otherwise end the current flag
@@ -328,7 +332,9 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
328332 if _looks_like_flag (token , self ._parser ) and not skip_remaining_flags :
329333
330334 # Check if there is an unfinished flag
331- if flag_arg_state is not None and flag_arg_state .count < flag_arg_state .min :
335+ if flag_arg_state is not None \
336+ and isinstance (flag_arg_state .min , int ) \
337+ and flag_arg_state .count < flag_arg_state .min :
332338 raise _UnfinishedFlagError (flag_arg_state )
333339
334340 # Reset flag arg state but not positional tracking because flags can be
@@ -361,7 +367,7 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
361367 new_arg_state = _ArgumentState (action )
362368
363369 # Keep track of this flag if it can receive arguments
364- if new_arg_state .max > 0 :
370+ if new_arg_state .max > 0 : # type: ignore[operator]
365371 flag_arg_state = new_arg_state
366372 skip_remaining_flags = flag_arg_state .is_remainder
367373
@@ -370,7 +376,7 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
370376 consume_argument (flag_arg_state )
371377
372378 # Check if we have finished with this flag
373- if flag_arg_state .count >= flag_arg_state .max :
379+ if isinstance ( flag_arg_state . max , ( float , int )) and flag_arg_state .count >= flag_arg_state .max :
374380 flag_arg_state = None
375381
376382 # Otherwise treat as a positional argument
@@ -415,7 +421,7 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
415421 skip_remaining_flags = True
416422
417423 # Check if we have finished with this positional
418- elif pos_arg_state .count >= pos_arg_state .max :
424+ elif isinstance ( pos_arg_state . max , ( float , int )) and pos_arg_state .count >= pos_arg_state .max :
419425 pos_arg_state = None
420426
421427 # Check if the next positional has nargs set to argparse.REMAINDER.
@@ -432,7 +438,9 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
432438 # the current argument. We will handle the completion of flags that start with only one prefix
433439 # character (-f) at the end.
434440 if _looks_like_flag (text , self ._parser ) and not skip_remaining_flags :
435- if flag_arg_state is not None and flag_arg_state .count < flag_arg_state .min :
441+ if flag_arg_state is not None \
442+ and isinstance (flag_arg_state .min , int ) \
443+ and flag_arg_state .count < flag_arg_state .min :
436444 raise _UnfinishedFlagError (flag_arg_state )
437445 return self ._complete_flags (text , line , begidx , endidx , matched_flags )
438446
@@ -453,7 +461,7 @@ def update_mutex_groups(arg_action: argparse.Action) -> None:
453461
454462 # Otherwise, print a hint if the flag isn't finished or text isn't possibly the start of a flag
455463 elif (
456- flag_arg_state .count < flag_arg_state .min
464+ ( isinstance ( flag_arg_state .min , int ) and flag_arg_state . count < flag_arg_state .min )
457465 or not _single_prefix_char (text , self ._parser )
458466 or skip_remaining_flags
459467 ):
@@ -523,14 +531,15 @@ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matche
523531
524532 return matches
525533
526- def _format_completions (self , arg_state : _ArgumentState , completions : List [ Union [str , CompletionItem ]]) -> List [str ]:
534+ def _format_completions (self , arg_state : _ArgumentState , completions : Union [List [ str ], List [ CompletionItem ]]) -> List [str ]:
527535 # Check if the results are CompletionItems and that there aren't too many to display
528536 if 1 < len (completions ) <= self ._cmd2_app .max_completion_items and isinstance (completions [0 ], CompletionItem ):
537+ completion_items = cast (List [CompletionItem ], completions )
529538 four_spaces = 4 * ' '
530539
531540 # If the user has not already sorted the CompletionItems, then sort them before appending the descriptions
532541 if not self ._cmd2_app .matches_sorted :
533- completions .sort (key = self ._cmd2_app .default_sort_key )
542+ completion_items .sort (key = self ._cmd2_app .default_sort_key )
534543 self ._cmd2_app .matches_sorted = True
535544
536545 # If a metavar was defined, use that instead of the dest field
@@ -556,7 +565,7 @@ def _format_completions(self, arg_state: _ArgumentState, completions: List[Union
556565 token_width = ansi .style_aware_wcswidth (destination )
557566 desc_width = ansi .widest_line (desc_header )
558567
559- for item in completions :
568+ for item in completion_items :
560569 token_width = max (ansi .style_aware_wcswidth (item ), token_width )
561570
562571 # Replace tabs with 4 spaces so we can calculate width
@@ -568,10 +577,10 @@ def _format_completions(self, arg_state: _ArgumentState, completions: List[Union
568577 cols .append (Column (desc_header , width = desc_width ))
569578
570579 hint_table = SimpleTable (cols , divider_char = None )
571- table_data = [[item , item .description ] for item in completions ]
580+ table_data = [[item , item .description ] for item in completion_items ]
572581 self ._cmd2_app .formatted_completions = hint_table .generate_table (table_data , row_spacing = 0 )
573582
574- return completions
583+ return cast ( List [ str ], completions )
575584
576585 def complete_subcommand_help (self , text : str , line : str , begidx : int , endidx : int , tokens : List [str ]) -> List [str ]:
577586 """
@@ -623,14 +632,15 @@ def _complete_arg(
623632 arg_state : _ArgumentState ,
624633 consumed_arg_values : Dict [str , List [str ]],
625634 * ,
626- cmd_set : Optional [CommandSet ] = None
635+ cmd_set : Optional [CommandSet ] = None ,
627636 ) -> List [str ]:
628637 """
629638 Tab completion routine for an argparse argument
630639 :return: list of completions
631640 :raises: CompletionError if the completer or choices function this calls raises one
632641 """
633642 # Check if the arg provides choices to the user
643+ arg_choices : Union [List [str ], ChoicesCallable ]
634644 if arg_state .action .choices is not None :
635645 arg_choices = list (arg_state .action .choices )
636646 if not arg_choices :
@@ -645,11 +655,12 @@ def _complete_arg(
645655 for index , choice in enumerate (arg_choices ):
646656 # Prevent converting anything that is already a str (i.e. CompletionItem)
647657 if not isinstance (choice , str ):
648- arg_choices [index ] = str (choice )
658+ arg_choices [index ] = str (choice ) # type: ignore[unreachable]
649659 else :
650- arg_choices = getattr (arg_state .action , ATTR_CHOICES_CALLABLE , None )
651- if arg_choices is None :
660+ choices_attr = getattr (arg_state .action , ATTR_CHOICES_CALLABLE , None )
661+ if choices_attr is None :
652662 return []
663+ arg_choices = choices_attr
653664
654665 # If we are going to call a completer/choices function, then set up the common arguments
655666 args = []
@@ -681,20 +692,26 @@ def _complete_arg(
681692 # Check if the argument uses a specific tab completion function to provide its choices
682693 if isinstance (arg_choices , ChoicesCallable ) and arg_choices .is_completer :
683694 args .extend ([text , line , begidx , endidx ])
684- results = arg_choices .to_call (* args , ** kwargs )
695+ results = arg_choices .to_call (* args , ** kwargs ) # type: ignore[arg-type]
685696
686697 # Otherwise use basic_complete on the choices
687698 else :
688699 # Check if the choices come from a function
689- if isinstance (arg_choices , ChoicesCallable ) and not arg_choices .is_completer :
690- arg_choices = arg_choices .to_call (* args , ** kwargs )
700+ completion_items : List [str ]
701+ if isinstance (arg_choices , ChoicesCallable ):
702+ if not arg_choices .is_completer :
703+ completion_items = arg_choices .to_call (* args , ** kwargs ) # type: ignore[arg-type]
704+ else :
705+ completion_items = []
706+ else :
707+ completion_items = arg_choices
691708
692709 # Filter out arguments we already used
693710 used_values = consumed_arg_values .get (arg_state .action .dest , [])
694- arg_choices = [choice for choice in arg_choices if choice not in used_values ]
711+ completion_items = [choice for choice in completion_items if choice not in used_values ]
695712
696713 # Do tab completion on the choices
697- results = self ._cmd2_app .basic_complete (text , line , begidx , endidx , arg_choices )
714+ results = self ._cmd2_app .basic_complete (text , line , begidx , endidx , completion_items )
698715
699716 if not results :
700717 # Reset the value for matches_sorted. This is because completion of flag names
0 commit comments