def getDummyArgumentInterceptor(parser): # Don't ask, it's magic. return parser_arguments.ArgumentInterceptor( parser=parser, cli_generator=surface_config_set.Set.GetCLIGenerator(), allow_positional=True, data=IAMUtilTest.DummyData())
def _AssignParser(self, parser_group, allow_positional_args): """Assign a parser group to model this Command or CommandGroup. Args: parser_group: argparse._ArgumentGroup, the group that will model this command or group's arguments. allow_positional_args: bool, Whether to allow positional args for this group or not. """ if not parser_group: # This is the root of the command tree, so we create the first parser. self._parser = parser_extensions.ArgumentParser( description=self.long_help, add_help=False, prog=self.dotted_name, calliope_command=self) else: # This is a normal sub group, so just add a new subparser to the existing # one. self._parser = parser_group.add_parser( self.cli_name, help=self.short_help, description=self.long_help, add_help=False, prog=self.dotted_name, calliope_command=self) self._sub_parser = None self.ai = parser_arguments.ArgumentInterceptor( parser=self._parser, is_global=not parser_group, cli_generator=self._cli_generator, allow_positional=allow_positional_args) self.ai.add_argument( '-h', action=actions.ShortHelpAction(self), is_replicated=True, category=base.COMMONLY_USED_FLAGS, help='Print a summary help and exit.') self.ai.add_argument( '--help', action=actions.RenderDocumentAction(self, '--help'), is_replicated=True, category=base.COMMONLY_USED_FLAGS, help='Display detailed help.') self.ai.add_argument( '--document', action=actions.RenderDocumentAction(self), is_replicated=True, nargs=1, metavar='ATTRIBUTES', type=arg_parsers.ArgDict(), hidden=True, help='THIS TEXT SHOULD BE HIDDEN') self._AcquireArgs()
def ArgumentParser(cli_name='test'): """Gets a calliope argument parser with all intercepts in place.""" command = MockCommand(cli_name) wrapped_parser = parser_extensions.ArgumentParser(calliope_command=command) parser = parser_arguments.ArgumentInterceptor(parser=wrapped_parser, cli_generator=None, allow_positional=True) command.ai = parser parser.parse_args = wrapped_parser.parse_args # For test use only. return parser
def SetUp(self): # Set up a sample argparse parser. command = calliope_util.MockCommand('command') argparser = parser_extensions.ArgumentParser(calliope_command=command) self.parser = parser_arguments.ArgumentInterceptor( parser=argparser, cli_generator=None, allow_positional=True) command.ai = self.parser # Set up a default fallthrough. def Fallthrough(): return '!' self.fallthrough = deps.Fallthrough(Fallthrough, hint='h')
def __call__(self, parser, namespace, values, option_string=None): choice = values[0] args = self.GenerateArgs(namespace, choice) sub_parser = self._name_parser_map[choice] # This is tricky. When we create a new parser above, that parser does not # have any of the flags from the parent command. We need to propagate them # all down to this parser like we do in calliope. We also want to add new # flags. In order for those to show up in the help, they need to be # registered with an ArgumentInterceptor. Here, we create one and seed it # with the data of the parent. This actually means that every flag we add # to our new parser will show up in the help of the parent parser, even # though those flags are not actually on that parser. This is ok because # help is always run on the parent ArgumentInterceptor and we want it to # show the full set of args. ai = parser_arguments.ArgumentInterceptor( sub_parser, is_root=False, cli_generator=None, allow_positional=True, data=self._parent_ai.data) for flag in itertools.chain(self._parent_ai.flag_args, self._parent_ai.ancestor_flag_args): # Propagate the flags down except the ones we are not supposed to. Note # that we *do* copy the help action unlike we usually do because this # subparser is going to share the help action of the parent. if flag.do_not_propagate or flag.required: continue # We add the flags directly to the parser instead of the # ArgumentInterceptor because if we didn't the flags would be duplicated # in the help, since we reused the data object from the parent. sub_parser._add_action(flag) # Update parent display_info in children, children take precedence. ai.display_info.AddLowerDisplayInfo(self._parent_ai.display_info) # Add args to the parser and remove any collisions if arguments are # already registered with the same name. for _, arg in args.iteritems(): arg.RemoveFromParser(ai) added_arg = arg.AddToParser(ai) # Argcomplete patches parsers and actions before call() is called. Since # we generate these args at call() time, they have not been patched and # causes completion to fail. Since we know that we are not going to be # adding any subparsers (the only thing that actually needs to be patched) # we fake it here to make argcomplete think it did the patching so it # doesn't crash. if '_ARGCOMPLETE' in os.environ and not hasattr(added_arg, '_orig_class'): added_arg._orig_class = added_arg.__class__ super(DynamicPositionalAction, self).__call__( parser, namespace, values, option_string=option_string)
def __init__(self, args=None, command_name='test', command_resources=None, cli=None, command_only=False, handler_info=None, **kwargs): self._calliope_command = MockCommand(command_name) self._cli = cli self._command_only = command_only parser = parser_extensions.ArgumentParser( add_help=False, prog=command_name, calliope_command=self._calliope_command) self._calliope_command.ai = parser_arguments.ArgumentInterceptor(parser) self._calls = {} self._commands = [] self._command_resources = command_resources or {} self._handler_info = handler_info or {} dests = {} if args: for name, value in six.iteritems(args): self._calliope_command.ai.add_argument(name, help='Auxilio aliis.') dests[name.replace('-', '_').strip('_')] = value super(MockNamespace, self).__init__(**dests)
def validate_specified_args(self, ai, specified_args, top=True): """Validate specified args against the arg group constraints. Each group may be mutually exclusive and/or required. Each argument may be required. Args: ai: ArgumentInterceptor, The argument interceptor containing the ai.arguments argument group. specified_args: set, The dests of the specified args. top: bool, True if ai.arguments is the top level group. Raises: ModalGroupError: If modal arg not specified. OptionalMutexError: On optional mutex group conflict. RequiredError: If required arg not specified. RequiredMutexError: On required mutex group conflict. Returns: True if the subgroup was specified. """ also_optional = [] # The optional args in group that were not specified. have_optional = [] # The specified optional (not required) args. have_required = [] # The specified required args. need_required = [] # The required args in group that must be specified. for arg in sorted(ai.arguments, key=usage_text.GetArgSortKey): if arg.is_group: arg_was_specified = self.validate_specified_args(arg, specified_args, top=False) else: arg_was_specified = arg.dest in specified_args if arg_was_specified: if arg.is_required: have_required.append(arg) else: have_optional.append(arg) elif arg.is_required: if not isinstance(arg, DynamicPositionalAction): need_required.append(arg) else: also_optional.append(arg) if need_required: if top or have_required and not (have_optional or also_optional): ai = parser_arguments.ArgumentInterceptor(self, arguments=need_required) self._Error(parser_errors.RequiredError( parser=self, argument=usage_text.GetArgUsage( ai, value=False, hidden=True, top=top))) if have_optional or have_required: have_ai = parser_arguments.ArgumentInterceptor( self, arguments=have_optional + have_required) need_ai = parser_arguments.ArgumentInterceptor( self, arguments=need_required) self._Error(parser_errors.ModalGroupError( parser=self, argument=usage_text.GetArgUsage( have_ai, value=False, hidden=True, top=top), conflict=usage_text.GetArgUsage( need_ai, value=False, hidden=True, top=top))) # Multiple args with the same dest are counted as 1 arg. count = (len(self.GetDestinations(have_required)) + len(self.GetDestinations(have_optional))) if ai.is_mutex: conflict = usage_text.GetArgUsage(ai, value=False, hidden=True, top=top) if ai.is_required: if count != 1: if count: argument = usage_text.GetArgUsage( sorted(have_required + have_optional, key=usage_text.GetArgSortKey)[0], value=False, hidden=True, top=top) else: argument = None self._Error(parser_errors.RequiredMutexError( parser=self, argument=argument, conflict=conflict)) elif count > 1: argument = usage_text.GetArgUsage( sorted(have_required + have_optional, key=usage_text.GetArgSortKey)[0], value=False, hidden=True, top=top) self._Error(parser_errors.OptionalMutexError( parser=self, argument=argument, conflict=conflict)) return bool(count)
def __call__(self, parser, namespace, values, option_string=None): choice = values[0] args = self.GenerateArgs(namespace, choice) # type: list[base.Argument] sub_parser = self._name_parser_map[choice] # This is tricky. When we create a new parser above, that parser does not # have any of the flags from the parent command. We need to propagate them # all down to this parser like we do in calliope. We also want to add new # flags. In order for those to show up in the help, they need to be # registered with an ArgumentInterceptor. Here, we create one and seed it # with the data of the parent. This actually means that every flag we add # to our new parser will show up in the help of the parent parser, even # though those flags are not actually on that parser. This is ok because # help is always run on the parent ArgumentInterceptor and we want it to # show the full set of args. ai = parser_arguments.ArgumentInterceptor( sub_parser, is_global=False, cli_generator=None, allow_positional=True, data=self._parent_ai.data) for flag in itertools.chain(self._parent_ai.flag_args, self._parent_ai.ancestor_flag_args): # Propagate the flags down except the ones we are not supposed to. Note # that we *do* copy the help action unlike we usually do because this # subparser is going to share the help action of the parent. if flag.do_not_propagate or flag.is_required: continue # We add the flags directly to the parser instead of the # ArgumentInterceptor because if we didn't the flags would be duplicated # in the help, since we reused the data object from the parent. sub_parser._add_action(flag) # Update parent display_info in children, children take precedence. ai.display_info.AddLowerDisplayInfo(self._parent_ai.display_info) # Add args to the parser and remove any collisions if arguments are # already registered with the same name. for arg in args: arg.RemoveFromParser(ai) added_arg = arg.AddToParser(ai) # Argcomplete patches parsers and actions before call() is called. Since # we generate these args at call() time, they have not been patched and # causes completion to fail. Since we know that we are not going to be # adding any subparsers (the only thing that actually needs to be patched) # we fake it here to make argcomplete think it did the patching so it # doesn't crash. if '_ARGCOMPLETE' in os.environ and not hasattr(added_arg, '_orig_class'): added_arg._orig_class = added_arg.__class__ super(DynamicPositionalAction, self).__call__( parser, namespace, values, option_string=option_string) # Running two dynamic commands in a row using the same CLI object is a # problem because the argparse parsers are saved in between invocations. # This is usually fine because everything is static, but in this case two # invocations could actually have different dynamic args generated. We # have to do two things to get this to work. First we need to clear the # parser from the map. If we don't do this, this class doesn't even get # called again because the choices are already defined. Second, we need # to remove the arguments we added from the ArgumentInterceptor. The # parser itself is thrown out, but because we are sharing an # ArgumentInterceptor with our parent, it remembers the args that we # added. Later, they are propagated back down to us even though they no # longer actually exist. When completing, we know we will only be running # a single invocation and we need to leave the choices around so that the # completer can read them after the command fails to run. if '_ARGCOMPLETE' not in os.environ: self._name_parser_map.clear()