Skip to content

Commit bf099c8

Browse files
committed
Finished implementation of CompletionError
1 parent 4eff58c commit bf099c8

File tree

1 file changed

+62
-30
lines changed

1 file changed

+62
-30
lines changed

cmd2/argparse_completer.py

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
from . import cmd2
1616
from . import utils
1717
from .ansi import ansi_safe_wcswidth, style_error
18+
from .argparse_custom import ATTR_CHOICES_CALLABLE, INFINITY, generate_range_error
1819
from .argparse_custom import ATTR_SUPPRESS_TAB_HINT, ATTR_DESCRIPTIVE_COMPLETION_HEADER, ATTR_NARGS_RANGE
19-
from .argparse_custom import ChoicesCallable, CompletionItem, ATTR_CHOICES_CALLABLE, INFINITY, generate_range_error
20+
from .argparse_custom import ChoicesCallable, CompletionError, CompletionItem
2021
from .rl_utils import rl_force_redisplay
2122

2223
# If no descriptive header is supplied, then this will be used instead
@@ -319,8 +320,12 @@ def consume_argument(arg_state: AutoCompleter._ArgumentState) -> None:
319320

320321
# Check if we are completing a flag's argument
321322
if flag_arg_state is not None:
322-
completion_results = self._complete_for_arg(flag_arg_state.action, text, line,
323-
begidx, endidx, consumed_arg_values)
323+
try:
324+
completion_results = self._complete_for_arg(flag_arg_state.action, text, line,
325+
begidx, endidx, consumed_arg_values)
326+
except CompletionError as ex:
327+
self._print_completion_error(flag_arg_state.action, ex)
328+
return []
324329

325330
# If we have results, then return them
326331
if completion_results:
@@ -341,8 +346,12 @@ def consume_argument(arg_state: AutoCompleter._ArgumentState) -> None:
341346
action = self._positional_actions[pos_index]
342347
pos_arg_state = AutoCompleter._ArgumentState(action)
343348

344-
completion_results = self._complete_for_arg(pos_arg_state.action, text, line,
345-
begidx, endidx, consumed_arg_values)
349+
try:
350+
completion_results = self._complete_for_arg(pos_arg_state.action, text, line,
351+
begidx, endidx, consumed_arg_values)
352+
except CompletionError as ex:
353+
self._print_completion_error(pos_arg_state.action, ex)
354+
return []
346355

347356
# If we have results, then return them
348357
if completion_results:
@@ -456,7 +465,11 @@ def format_help(self, tokens: List[str]) -> str:
456465
def _complete_for_arg(self, arg_action: argparse.Action,
457466
text: str, line: str, begidx: int, endidx: int,
458467
consumed_arg_values: Dict[str, List[str]]) -> List[str]:
459-
"""Tab completion routine for an argparse argument"""
468+
"""
469+
Tab completion routine for an argparse argument
470+
:return: list of completions
471+
:raises CompletionError if the completer or choices function this calls raises one
472+
"""
460473
# Check if the arg provides choices to the user
461474
if arg_action.choices is not None:
462475
arg_choices = arg_action.choices
@@ -520,53 +533,72 @@ def _complete_for_arg(self, arg_action: argparse.Action,
520533
return self._format_completions(arg_action, results)
521534

522535
@staticmethod
523-
def _print_arg_hint(arg_action: argparse.Action) -> None:
524-
"""Print argument hint to the terminal when tab completion results in no results"""
525-
526-
# Check if hinting is disabled
527-
suppress_hint = getattr(arg_action, ATTR_SUPPRESS_TAB_HINT, False)
528-
if suppress_hint or arg_action.help == argparse.SUPPRESS or arg_action.dest == argparse.SUPPRESS:
529-
return
530-
536+
def _format_message_prefix(arg_action: argparse.Action) -> str:
537+
"""Format the arg prefix text that appears before messages printed to the user"""
531538
# Check if this is a flag
532539
if arg_action.option_strings:
533540
flags = ', '.join(arg_action.option_strings)
534541
param = ' ' + str(arg_action.dest).upper()
535-
prefix = '{}{}'.format(flags, param)
542+
return '{}{}'.format(flags, param)
536543

537544
# Otherwise this is a positional
538545
else:
539-
prefix = '{}'.format(str(arg_action.dest).upper())
546+
return '{}'.format(str(arg_action.dest).upper())
547+
548+
@staticmethod
549+
def _print_message(msg: str) -> None:
550+
"""Print a message instead of tab completions and redraw the prompt and input line"""
551+
print(msg)
552+
rl_force_redisplay()
553+
554+
def _print_arg_hint(self, arg_action: argparse.Action) -> None:
555+
"""
556+
Print argument hint to the terminal when tab completion results in no results
557+
:param arg_action: action being tab completed
558+
"""
559+
# Check if hinting is disabled
560+
suppress_hint = getattr(arg_action, ATTR_SUPPRESS_TAB_HINT, False)
561+
if suppress_hint or arg_action.help == argparse.SUPPRESS or arg_action.dest == argparse.SUPPRESS:
562+
return
540563

564+
prefix = self._format_message_prefix(arg_action)
541565
prefix = ' {0: <{width}} '.format(prefix, width=20)
542566
pref_len = len(prefix)
543567

544568
help_text = '' if arg_action.help is None else arg_action.help
545569
help_lines = help_text.splitlines()
546570

547571
if len(help_lines) == 1:
548-
print('\nHint:\n{}{}\n'.format(prefix, help_lines[0]))
572+
self._print_message('\nHint:\n{}{}\n'.format(prefix, help_lines[0]))
549573
else:
550574
out_str = '\n{}'.format(prefix)
551575
out_str += '\n{0: <{width}}'.format('', width=pref_len).join(help_lines)
552-
print('\nHint:' + out_str + '\n')
576+
self._print_message('\nHint:' + out_str + '\n')
553577

554-
# Redraw prompt and input line
555-
rl_force_redisplay()
556-
557-
@staticmethod
558-
def _print_unfinished_flag_error(flag_arg_state: _ArgumentState) -> None:
559-
"""Print an error during tab completion when the user has not finished the current flag"""
560-
flags = ', '.join(flag_arg_state.action.option_strings)
561-
param = ' ' + str(flag_arg_state.action.dest).upper()
562-
prefix = '{}{}'.format(flags, param)
578+
def _print_unfinished_flag_error(self, flag_arg_state: _ArgumentState) -> None:
579+
"""
580+
Print an error during tab completion when the user has not finished the current flag
581+
:param flag_arg_state: information about the unfinished flag action
582+
"""
583+
prefix = self._format_message_prefix(flag_arg_state.action)
563584

564585
out_str = "\nError:\n"
565586
out_str += ' {0: <{width}} '.format(prefix, width=20)
566587
out_str += generate_range_error(flag_arg_state.min, flag_arg_state.max)
567588

568589
out_str += ' ({} entered)'.format(flag_arg_state.count)
569-
print(style_error('{}\n'.format(out_str)))
590+
self._print_message(style_error('{}\n'.format(out_str)))
570591

571-
# Redraw prompt and input line
572-
rl_force_redisplay()
592+
def _print_completion_error(self, arg_action: argparse.Action, completion_error: CompletionError) -> None:
593+
"""
594+
Print a CompletionError to the user
595+
:param arg_action: action being tab completed
596+
:param completion_error: error that occurred
597+
"""
598+
prefix = self._format_message_prefix(arg_action)
599+
600+
out_str = "\nError:\n"
601+
out_str += ' {0: <{width}} '.format(prefix, width=20)
602+
out_str += str(completion_error)
603+
604+
self._print_message(style_error('{}\n'.format(out_str)))

0 commit comments

Comments
 (0)