def _Suggest(self, unknown_args): """Error out with a suggestion based on text distance for each unknown.""" messages = [] suggester = usage_text.TextChoiceSuggester() # pylint:disable=protected-access, This is an instance of this class. for flag in self._calliope_command.GetAllAvailableFlags(): options = flag.option_strings if options: # This is a flag, add all its names as choices. suggester.AddChoices(options) # Add any aliases as choices as well, but suggest the primary name. aliases = getattr(flag, 'suggestion_aliases', None) if aliases: suggester.AddAliases(aliases, options[0]) suggestions = {} for arg in unknown_args: # Only do this for flag names. if arg.startswith('--'): # Strip the flag value if any from the suggestion. flag = arg.split('=')[0] suggestion = suggester.GetSuggestion(flag) else: suggestion = None if suggestion: suggestions[arg] = suggestion messages.append(arg + " (did you mean '{0}'?)".format(suggestion)) else: messages.append(arg) # If there is a single arg, put it on the same line. If there are multiple # add each on it's own line for better clarity. separator = u'\n ' if len(messages) > 1 else u' ' # This try-except models the real parse_args() pathway to self.error(). try: raise parser_errors.UnrecognizedArgumentsError( u'unrecognized arguments:{0}{1}'.format( separator, separator.join(messages)), parser=self, total_unrecognized=len(unknown_args), total_suggestions=len(suggestions), suggestions=suggestions, ) except argparse.ArgumentError as e: self.error(e.message)
def ParseRemainingArgs(self, remaining_args, namespace, original_args): """Parses the unrecognized args from the end of the remaining_args. This method identifies all unrecognized arguments after the last argument recognized by a parser (but before --). It then either logs a warning and binds them to the namespace or raises an error, depending on strictness. Args: remaining_args: A list of arguments that the parsers did not recognize. namespace: The Namespace to bind to. original_args: The full list of arguments given to the top parser, Raises: ArgumentError: If there were remaining arguments after the last recognized argument and this action is strict. Returns: A tuple of the updated namespace and unrecognized arguments (before the last recognized argument). """ # Only parse consecutive unknown args from the end of the original args. # Strip out everything after '--' if '--' in original_args: original_args, _ = self._SplitOnDash(original_args) # Find common suffix between remaining_args and original_args split_index = 0 for i, (arg1, arg2) in enumerate( zip(reversed(remaining_args), reversed(original_args))): if arg1 != arg2: split_index = len(remaining_args) - i break pass_through_args = remaining_args[split_index:] remaining_args = remaining_args[:split_index] if pass_through_args: msg = (u'unrecognized args: {args}\n' + self.explanation).format(args=u' '.join(pass_through_args)) raise parser_errors.UnrecognizedArgumentsError(msg) self(None, namespace, pass_through_args) return namespace, remaining_args
def parse_args(self, args=None, namespace=None): """Overrides argparse.ArgumentParser's .parse_args method.""" namespace, unknown_args = self.parse_known_args(args, namespace) if not unknown_args: return namespace # Content of these lines differs from argparser's parse_args(). # pylint:disable=protected-access deepest_parser = namespace._deepest_parser or self deepest_parser._specified_args = namespace._specified_args if deepest_parser._remainder_action: # Assume the user wanted to pass all arguments after last recognized # arguments into _remainder_action. Either do this with a warning or # fail depending on strictness. # pylint:disable=protected-access try: namespace, unknown_args = ( deepest_parser._remainder_action.ParseRemainingArgs( unknown_args, namespace, args)) # There still may be unknown_args that came before the last known arg. if not unknown_args: return namespace except parser_errors.UnrecognizedArgumentsError as e: # In the case of UnrecognizedArgumentsError, we want to just let it # continue so that we can get the nicer error handling. pass # There is at least one parsing error. Add a message for each unknown # argument. For each, try to come up with a suggestion based on text # distance. If one is close enough, print a 'did you mean' message along # with that argument. messages = [] suggester = usage_text.TextChoiceSuggester() # pylint:disable=protected-access, This is an instance of this class. for flag in deepest_parser._calliope_command.GetAllAvailableFlags(): options = flag.option_strings if options: # This is a flag, add all its names as choices. suggester.AddChoices(options) # Add any aliases as choices as well, but suggest the primary name. aliases = getattr(flag, 'suggestion_aliases', None) if aliases: suggester.AddAliases(aliases, options[0]) suggestions = {} for arg in unknown_args: # Only do this for flag names. if arg.startswith('--'): # Strip the flag value if any from the suggestion. flag = arg.split('=')[0] suggestion = suggester.GetSuggestion(flag) else: suggestion = None if suggestion: suggestions[arg] = suggestion messages.append(arg + " (did you mean '{0}'?)".format(suggestion)) else: messages.append(arg) # If there is a single arg, put it on the same line. If there are multiple # add each on it's own line for better clarity. separator = u'\n ' if len(messages) > 1 else u' ' # This try-except models the real parse_args() pathway to self.error(). try: raise parser_errors.UnrecognizedArgumentsError( u'unrecognized arguments:{0}{1}'.format( separator, separator.join(messages)), parser=deepest_parser, total_unrecognized=len(unknown_args), total_suggestions=len(suggestions), suggestions=suggestions, ) except argparse.ArgumentError as e: deepest_parser.error(e.message)