Exemple #1
0
 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())
Exemple #2
0
  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()
Exemple #3
0
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')
Exemple #5
0
  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)
Exemple #6
0
 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()