diff --git a/knack/parser.py b/knack/parser.py index 4013b77..e79b744 100644 --- a/knack/parser.py +++ b/knack/parser.py @@ -30,6 +30,20 @@ ] +def _get_action_name(argument): + # copied from argparse in order not to use a protected method + if argument is None: + return None + if argument.option_strings: + return '/'.join(argument.option_strings) + if argument.metavar not in (None, argparse.SUPPRESS): + return argument.metavar + if argument.dest not in (None, argparse.SUPPRESS): + return argument.dest + + return None + + class CLICommandParser(argparse.ArgumentParser): @staticmethod @@ -266,19 +280,34 @@ def _check_value(self, action, value): import sys if action.choices is not None and value not in action.choices: - # parser has no `command_source`, value is part of command itself - error_msg = "{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'.".format( - prog=self.prog, value=value) - logger.error(error_msg) - candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) + is_command = action.dest in ["_command", "_subcommand"] + candidates = action.choices + if is_command: + candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) + if candidates: print_args = { 's': 's' if len(candidates) > 1 else '', 'verb': 'are' if len(candidates) > 1 else 'is', - 'value': value + 'value': value, + 'action': _get_action_name(action), + 'choices': ', '.join(map(repr, candidates)) } - suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format(**print_args) + + if is_command: + # parser has no `command_source`, value is part of command itself + error_msg = "{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'.".format( + prog=self.prog, value=value) + logger.error(error_msg) + if candidates: + suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format(**print_args) + suggestion_msg += '\n'.join(['\t' + candidate for candidate in candidates]) + print(suggestion_msg, file=sys.stderr) + else: + error_msg = "{prog}: invalid choice '{value}'. See '{prog} --help'.".format( + prog=self.prog, value=value) + logger.error(error_msg) + suggestion_msg = "\nPossible choice{s} for argument '{action}' {verb}:\n".format(**print_args) suggestion_msg += '\n'.join(['\t' + candidate for candidate in candidates]) print(suggestion_msg, file=sys.stderr) - self.exit(2)