def _GetArgAttributes(self,
                          field,
                          field_helps=None,
                          default_help=None,
                          required=False):
        """Gets attributes of the argument that should be generated.

    Args:
      field: str, The name of the field.
      field_helps: {str: str}, A mapping of field name to help strings.
      default_help: str, The help string to use if there is no specific help
        for this flag.
      required: bool, True if we must generate an arg for this field (if it is
        part of the resource arg) or false if it is ok to not generate a flag
        for it (and it will just be None in the request).

    Raises:
      MissingArgInformation: If arg info is provided but the given field was
      not registered, and it has been marked as required=True.

    Returns:
      yaml_command_schema.Argument, The attributes to use to generate the arg,
      or None if it should not be generated.
    """
        if not field_helps:
            field_helps = {}
        if self.arg_info is None:
            # No info was given, we are just auto generating everything.
            return yaml_command_schema.Argument(
                arg_name=self.auto_rename_fields.get(field, field),
                help_text=field_helps.get(field.split('.')[-1], default_help))

        # Arg information was explicitly provided, so only generate fields that
        # are registered.
        data = self.arg_info.get(field)
        if data:
            return data
        if required:
            renamed = self.auto_rename_fields.get(field, field)
            if renamed not in self.builtin_args:
                # This is a resource arg and it must be registered.
                raise MissingArgInformation(field)
            # A required arg that is not registered, but is a builtin, so we don't
            # care because it won't be used. We do need to return a name here so
            # that it will end up being parsed though.
            return yaml_command_schema.Argument(
                arg_name=renamed,
                help_text=None,
            )

        # No info was provided for this arg and it is not required, don't generate.
        return None
Beispiel #2
0
 def testGenerateFlagBoolean(self):
   a = yaml_command_schema.Argument('asdf', 'foo', 'foo help')
   arg = arg_utils.GenerateFlag(fm.FakeMessage.bool1, a)
   self.assertEqual(arg.name, '--foo')
   self.assertEqual(
       {'category': None, 'action': 'store_true', 'completer': None,
        'help': 'foo help', 'hidden': False, 'required': False}, arg.kwargs)
Beispiel #3
0
    def testCreateRequestForUpdate(self, is_atomic):
        self.MockCRUDMethods(('foo.projects.instances', is_atomic))
        method = registry.GetMethod('foo.projects.instances', 'patch')
        arg_info = [
            yaml_command_schema.Argument('description', 'display-name', 'dn')
        ]

        gen = arg_marshalling.DeclarativeArgumentGenerator(
            method, arg_info, MakeResource())
        (parser, args) = CheckArgs(gen.GenerateArgs())
        self.assertEqual(['--display-name', 'instance'], sorted(args.keys()))
        self.assertEqual(
            args['instance'].kwargs['help'],
            'ID of the instance or fully qualified identifier for the instance.'
        )
        self.assertEqual(args['--display-name'].kwargs['help'], 'dn')

        namespace = parser.parse_args(['i', '--display-name', 'dis'])
        properties.VALUES.core.project.Set('p')
        req = gen.CreateRequest(namespace)
        self.assertEqual('dis', req.description)

        if is_atomic:
            self.assertEqual('projects/p/instances/i', req.name)
        else:
            self.assertEqual('p', req.projectsId)
            self.assertEqual('i', req.instancesId)
Beispiel #4
0
    def testCommandFallbackResourceArg(self, is_atomic):
        self.MockCRUDMethods(('foo.projects.parents.instances', is_atomic))
        method = registry.GetMethod('foo.projects.parents.instances', 'get')
        resource_arg = MakeResource(
            collection='foo.projects.parents.instances',
            attributes=['parent', 'instance'])
        resource_arg.command_level_fallthroughs = {'parent': ['--just-a-flag']}
        gen = arg_marshalling.DeclarativeArgumentGenerator(
            method,
            [yaml_command_schema.Argument(None, 'just-a-flag', 'help!')],
            resource_arg)
        (parser, _) = CheckArgs(gen.GenerateArgs())
        properties.VALUES.core.project.Set('p')
        namespace = parser.parse_args(['i', '--just-a-flag', '!'])
        req = gen.CreateRequest(namespace)
        if is_atomic:
            self.assertEqual('projects/p/parents/!/instances/i', req.name)
        else:
            self.assertEqual('i', req.instancesId)
            self.assertEqual('!', req.parentsId)

        # Make sure fallback doesn't get called if the information is provided in
        # the resource name
        (parser, _) = CheckArgs(gen.GenerateArgs())
        namespace = parser.parse_args(
            ['projects/p/parents/parent/instances/i', '--just-a-flag', '!'])
        req = gen.CreateRequest(namespace)
        if is_atomic:
            self.assertEqual('projects/p/parents/parent/instances/i', req.name)
        else:
            self.assertEqual('i', req.instancesId)
            self.assertEqual('parent', req.parentsId)
    def _FlagAttributesForField(self, prefix, field, is_root):
        """Compute the flag name to generate for the given message field.

    Args:
      prefix: str, A prefix to put on the flag (when generating flags for
        sub-messages).
      field: MessageField, The apitools field to generate the flag for.
      is_root: bool, True if this is the request message itself (not a
        sub-field).

    Returns:
      yaml_command_schema.Argument, The attributes to use to generate the arg,
      or None if it should not be generated.
    """
        if self._ShouldSkipAtomicField(field, is_root):
            return None
        attributes = self._GetArgAttributes(prefix + field.name)
        if not attributes:
            if field.variant == messages.Variant.MESSAGE:
                # If we are not generating this field, but it is a message, we might
                # want to generate flags for specific parts of the sub message.
                return yaml_command_schema.Argument(prefix + field.name, None)
            # Only stop processing if this is a scalar.
            return None
        if attributes.arg_name in self.ignored_fields:
            return None
        if field.variant == messages.Variant.MESSAGE:
            if (attributes.arg_name == self.method.request_field
                    and attributes.arg_name.lower().endswith('request')):
                attributes.arg_name = 'request'
        return attributes
Beispiel #6
0
 def testGenerateGroups(self, is_atomic, is_required):
     self.MockCRUDMethods(('foo.projects.instances', is_atomic))
     method = registry.GetMethod('foo.projects.instances', 'list')
     group_args = [
         yaml_command_schema.Argument('pageSize', 'page-size', 'ph'),
         yaml_command_schema.Argument('pageToken', 'page-token', 'th')
     ]
     group = yaml_command_schema.ArgumentGroup(arguments=group_args,
                                               mutex=True,
                                               required=is_required)
     gen = arg_marshalling.DeclarativeArgumentGenerator(
         method, [group],
         MakeResource(collection='foo.projects', attributes=[]))
     (_, args) = CheckArgs(gen.GenerateArgs())
     self.assertEqual(2, len(args))
     self.assertEqual(['--page-size', '--page-token'], sorted(args))
  def _GenerateArguments(self, prefix, message):
    """Gets the arguments to add to the parser that appear in the method body.

    Args:
      prefix: str, A string to prepend to the name of the flag. This is used
        for flags representing fields of a submessage.
      message: The apitools message to generate the flags for.

    Returns:
      {str, calliope.base.Argument}, A map of field name to argument.
    """
    args = []
    field_helps = arg_utils.FieldHelpDocs(message)
    for field in message.all_fields():
      field_help = field_helps.get(field.name, None)
      name = self._GetArgName(field.name, field_help)
      if not name:
        continue
      name = prefix + name
      if field.variant == messages.Variant.MESSAGE:
        sub_args = self._GenerateArguments(name + '.', field.type)
        if sub_args:
          help_text = (name + ': ' + field_help) if field_help else ''
          group = base.ArgumentGroup(help=help_text)
          args.append(group)
          for arg in sub_args:
            group.AddArgument(arg)
      else:
        attributes = yaml_command_schema.Argument(name, name, field_help)
        arg = arg_utils.GenerateFlag(field, attributes, fix_bools=False,
                                     category='MESSAGE')
        if not arg.kwargs.get('help'):
          arg.kwargs['help'] = 'API doc needs help for field [{}].'.format(name)
        args.append(arg)
    return args
Beispiel #8
0
 def testGenerateFlagEnumChoices(self):
   a = yaml_command_schema.Argument('asdf', 'foo', 'foo help')
   arg = arg_utils.GenerateFlag(fm.FakeMessage.enum1, a)
   self.assertEqual(arg.name, '--foo')
   self.assertEqual(
       self._MakeKwargs(choices=['thing-one', 'thing-two'],
                        type=arg_utils.EnumNameToChoice),
       arg.kwargs)
Beispiel #9
0
 def testGenerateFlagPositional(self):
   a = yaml_command_schema.Argument('asdf', 'foo', 'foo help',
                                    is_positional=True)
   arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a)
   self.assertEqual(arg.name, 'foo')
   kwargs = self._MakeKwargs()
   del kwargs['required']
   self.assertEqual(kwargs, arg.kwargs)
Beispiel #10
0
 def testArgDictFlattened(self, second_field, cmdline_args, field_value):
     arg_spec = yaml_command_schema.Argument(
         'repeated_message',
         'foo',
         'help',
         type=util.FlattenedArgDict(
             util.ArgDictField.FromData({'api_field': 'string1'}),
             util.ArgDictField.FromData({'api_field': second_field})))
     self.Check(arg_spec, cmdline_args, 'repeated_message', field_value)
Beispiel #11
0
    def testGenerateFields(self, is_atomic):
        self.MockCRUDMethods(('foo.projects.instances', is_atomic))
        method = registry.GetMethod('foo.projects.instances', 'list')
        arg_info = [
            yaml_command_schema.Argument('pageSize', 'page-size', 'ph'),
            yaml_command_schema.Argument(None, 'just-a-flag', 'help!')
        ]
        gen = arg_marshalling.DeclarativeArgumentGenerator(
            method, arg_info,
            MakeResource(collection='foo.projects', attributes=[]))
        (parser, args) = CheckArgs(gen.GenerateArgs())
        self.assertEqual(['--just-a-flag', '--page-size'], sorted(args.keys()))
        self.assertEqual(args['--page-size'].kwargs['help'], 'ph')
        self.assertEqual(args['--just-a-flag'].kwargs['help'], 'help!')

        namespace = parser.parse_args(['--page-size', '1'])
        properties.VALUES.core.project.Set('p')
        req = gen.CreateRequest(namespace)
        self.assertEqual('1', req.pageSize)
        if is_atomic:
            self.assertEqual('projects/p', req.parent)
        else:
            self.assertEqual('p', req.projectsId)
Beispiel #12
0
  def testRepeated(self):
    # Repeated simple type gets wrapped.
    a = yaml_command_schema.Argument('foo', 'foo', 'foo help')
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string2, a)
    self.assertTrue(isinstance(arg.kwargs['type'], arg_parsers.ArgList))
    self.assertEqual(arg.kwargs['type'].element_type, six.text_type)

    # Repeated complex type doesn't get re-wrapped.
    a = yaml_command_schema.Argument('foo', 'foo', 'foo help',
                                     type=arg_parsers.ArgList())
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string2, a)
    self.assertIsNone(arg.kwargs['type'].element_type)

    # Repeated with flattened ArgDict.
    t = yaml_command_schema_util.ParseType(
        {'arg_dict': {'flatten': True, 'spec': [
            {'api_field': 'string1'}, {'api_field': 'string2'}]}})
    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', type=t)
    arg = arg_utils.GenerateFlag(fm.FakeMessage.repeated_message, a)
    result = arg.kwargs['type']('a=b,c=d')
    self.assertEqual(len(result), 2)
    self.assertEqual(result[0],
                     fm.FakeMessage.InnerMessage(string1='a', string2='b'))
    self.assertEqual(result[1],
                     fm.FakeMessage.InnerMessage(string1='c', string2='d'))

    # Repeated with ArgDict.
    t = yaml_command_schema_util.ParseType(
        {'arg_dict': {'spec': [
            {'api_field': 'string1', 'arg_name': 'a'},
            {'api_field': 'string2', 'arg_name': 'b'}]}})
    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', type=t)
    arg = arg_utils.GenerateFlag(fm.FakeMessage.repeated_message, a)
    result = arg.kwargs['type']('a=foo,b=bar')
    self.assertEqual(result,
                     fm.FakeMessage.InnerMessage(string1='foo', string2='bar'))

    # Not allowed to use ArgDict with non-repeated field.
    with self.assertRaisesRegex(
        arg_utils.ArgumentGenerationError,
        r'Failed to generate argument for field \[string1\]: The given type '
        r'can only be used on repeated fields.'):
      arg_utils.GenerateFlag(fm.FakeMessage.string1, a)

    # Force repeated arg to be singular.
    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', repeated=False)
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string2, a)
    self.assertEqual(arg.kwargs['type'], six.text_type)

    # Repeated with custom action is an error.
    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', action='foo')
    with self.assertRaisesRegex(
        arg_utils.ArgumentGenerationError,
        r'Failed to generate argument for field \[string2\]: The field is '
        r'repeated but is using a custom action. You might want to set '
        r'repeated: False in your arg spec.'):
      arg_utils.GenerateFlag(fm.FakeMessage.string2, a)
Beispiel #13
0
  def testGenerateFlagBasics(self):
    a = yaml_command_schema.Argument('asdf', 'foo', 'foo help')
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a)
    self.assertEqual(arg.name, '--foo')
    self.assertEqual(self._MakeKwargs(), arg.kwargs)

    arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a, category='ASDF')
    self.assertDictContainsSubset(self._MakeKwargs(category='ASDF'), arg.kwargs)

    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', default='junk')
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a)
    self.assertEqual(self._MakeKwargs(default='junk'), arg.kwargs)

    a = yaml_command_schema.Argument(
        'foo', 'foo', 'foo help',
        choices=[yaml_command_schema_util.Choice({'arg_value': 'a',
                                                  'enum_value': 'b'}),
                 yaml_command_schema_util.Choice({'arg_value': 'c',
                                                  'enum_value': 'd'})])
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a)
    self.assertEqual(self._MakeKwargs(choices=['a', 'c']), arg.kwargs)

    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', completer='junk')
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a)
    self.assertEqual(self._MakeKwargs(completer='junk'), arg.kwargs)

    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', hidden=True)
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a)
    self.assertEqual(self._MakeKwargs(hidden=True), arg.kwargs)

    a = yaml_command_schema.Argument('foo', 'foo', 'foo help', required=True)
    arg = arg_utils.GenerateFlag(fm.FakeMessage.string1, a)
    self.assertEqual(self._MakeKwargs(required=True), arg.kwargs)

    # No api_field
    a = yaml_command_schema.Argument(None, 'foo', 'foo help', repeated=False)
    arg = arg_utils.GenerateFlag(None, a)
    self.assertEqual(arg.kwargs['type'], None)

    # Unknown type
    a = yaml_command_schema.Argument('foo', 'foo', 'foo help')
    with self.assertRaisesRegex(
        arg_utils.ArgumentGenerationError,
        r'Failed to generate argument for field \[message1\]: The field is of '
        r'an unknown type.'):
      arg_utils.GenerateFlag(fm.FakeMessage.message1, a)
Beispiel #14
0
    def testFieldProcessing(self, is_atomic):
        def P(value):
            return '!' + value

        self.MockCRUDMethods(('foo.projects.instances', is_atomic))
        method = registry.GetMethod('foo.projects.instances', 'list')
        arg_info = [
            yaml_command_schema.Argument('pageSize',
                                         'page-size',
                                         'ph',
                                         processor=P)
        ]
        gen = arg_marshalling.DeclarativeArgumentGenerator(
            method, arg_info,
            MakeResource(collection='foo.projects', attributes=[]))
        req = gen.CreateRequest(mock.MagicMock(project='p', page_size='a'))
        self.assertEqual('!a', req.pageSize)
Beispiel #15
0
    def testCreateRequestWithStaticFields(self, is_atomic):
        self.MockCRUDMethods(('foo.projects.instances', is_atomic))
        method = registry.GetMethod('foo.projects.instances', 'list')
        arg_info = [
            yaml_command_schema.Argument('pageSize', 'page-size', 'ph')
        ]
        gen = arg_marshalling.DeclarativeArgumentGenerator(
            method, arg_info,
            MakeResource(collection='foo.projects', attributes=[]))
        (_, args) = CheckArgs(gen.GenerateArgs())
        self.assertEqual(['--page-size'], sorted(args.keys()))
        self.assertEqual(args['--page-size'].kwargs['help'], 'ph')

        req = gen.CreateRequest(mock.MagicMock(project='p', page_size=None),
                                static_fields={'pageSize': 1})
        self.assertEqual(1, req.pageSize)
        req = gen.CreateRequest(mock.MagicMock(project='p', page_size=2),
                                static_fields={'pageSize': 1})
        self.assertEqual(2, req.pageSize)
Beispiel #16
0
    def testFieldFallback(self, is_atomic):
        def Fallback():
            return '!'

        self.MockCRUDMethods(('foo.projects.instances', is_atomic))
        method = registry.GetMethod('foo.projects.instances', 'list')
        arg_info = [
            yaml_command_schema.Argument('pageSize',
                                         'page-size',
                                         'ph',
                                         fallback=Fallback)
        ]
        gen = arg_marshalling.DeclarativeArgumentGenerator(
            method, arg_info,
            MakeResource(collection='foo.projects', attributes=[]))
        (parser, _) = CheckArgs(gen.GenerateArgs())
        properties.VALUES.core.project.Set('p')
        namespace = parser.parse_args([])
        req = gen.CreateRequest(namespace)
        self.assertEqual('!', req.pageSize)
Beispiel #17
0
    def testCreateRequestExistingMessage(self):
        self.MockCRUDMethods(('foo.projects.instances', True))
        method = registry.GetMethod('foo.projects.instances', 'patch')
        arg_info = [
            yaml_command_schema.Argument('description', 'display-name', 'dn')
        ]
        gen = arg_marshalling.DeclarativeArgumentGenerator(
            method, arg_info, MakeResource())
        (parser, _) = CheckArgs(gen.GenerateArgs())
        namespace = parser.parse_args(['i', '--display-name', 'dis'])
        properties.VALUES.core.project.Set('p')
        original_message = list(
            method.GetRequestType().all_fields.return_value)
        mock_field = mock.MagicMock()
        mock_field.name = 'foo'
        mock_field.repeated = False
        existing_message = original_message.append(mock_field)

        req = gen.CreateRequest(namespace, existing_message=existing_message)
        self.assertEqual('projects/p/instances/i', req.name)
        self.assertEqual('dis', req.description)
        self.assertIsNotNone(req.foo)
Beispiel #18
0
 def testArgErrors(self, api_field, arg_name, cmdline_args):
   arg_spec = yaml_command_schema.Argument(api_field, arg_name, 'help')
   with self.assertRaises(SystemExit):
     self.Check(arg_spec, cmdline_args, api_field, None)
Beispiel #19
0
 def testArg(self, api_field, arg_name, cmdline_args, field_value):
   arg_spec = yaml_command_schema.Argument(api_field, arg_name, 'help')
   self.Check(arg_spec, cmdline_args, api_field, field_value)
Beispiel #20
0
 def Add(field, name):
   a = yaml_command_schema.Argument('asdf', name, 'help')
   arg_utils.GenerateFlag(field, a).AddToParser(parser)
Beispiel #21
0
    def _NormalizeResourceArgInfo(self, resource_arg_info):
        """Make sure the registered args match required args for the resource.

    For normal methods, this simply ensures that all API parameters have
    registered resource arguments and that there are no extra registered
    arguments. You are allowed to omit arguments that are "ignored", meaning
    arguments are not generated for them (like project).

    In this case where the method's params don't match the resources params and
    it is not a list method (basically just create), we will want to validate
    that you supply arguments for all parts of the resource because that is how
    it surfaces on the command line. The difference is that the last arg should
    have an api_field that points to the message body of the request because it
    cannot be included as a direct API parameter.

    Args:
      resource_arg_info: [yaml_command_yaml_command_schema.Argument], The
        registered resource arguments.

    Raises:
      InvalidResourceArguments: If something is wong with the registered
        arguments.
      InvalidResourceArgumentLists: If the lists of actual and expected
        arguments don't match.

    Returns:
      The modified list of resource args. Placeholder arguments will be created
      for any missing "ignored" args. The end result is that this list is the
      exact length and order of the API parameters.
    """
        actual_field_names = list(
            self.method.resource_argument_collection.detailed_params)
        resource_args = list(resource_arg_info)

        if (self.method.resource_argument_collection.detailed_params !=
                self.method.request_collection.detailed_params):
            # This only happens for create (not list)
            if resource_args:
                request_field_name = resource_args[-1].api_field
                # Ensure that the api_field for the last param of create methods points
                # to the message body and not to a normal API param.
                if not request_field_name.startswith(
                        self.method.request_field + '.'):
                    raise InvalidResourceArguments(
                        'The API field for the final resource arguments of create '
                        'commands must point to a request field, not a resource reference'
                        ' parameter. It must start with: [{}]'.format(
                            self.method.request_field + '.'))
                # Update this so validation doesn't fail down below.
                actual_field_names[-1] = request_field_name

        full_resource_args = []
        for field_name in actual_field_names:
            if resource_args and field_name == resource_args[0].api_field:
                # Argument matches expected, just add it to the list.
                full_resource_args.append(resource_args.pop(0))
            elif field_name in DeclarativeArgumentGenerator.IGNORED_FIELDS:
                # It doesn't match, but the field is ignored. Assume it was omitted
                # and generate a placeholder.
                full_resource_args.append(
                    yaml_command_schema.Argument(field_name,
                                                 DeclarativeArgumentGenerator.
                                                 IGNORED_FIELDS[field_name],
                                                 None,
                                                 generate=False))
            else:
                # The lists just don't match.
                raise InvalidResourceArgumentLists(actual_field_names,
                                                   resource_arg_info)

        if resource_args:
            # All actual fields were processed but there are still registered
            # arguments remaining, they must be extra.
            raise InvalidResourceArgumentLists(actual_field_names,
                                               resource_arg_info)

        return full_resource_args