Ejemplo n.º 1
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)
    def FromData(cls, data):
        """Gets the arg definition from the spec data.

    Args:
      data: The spec data.

    Returns:
      Argument, the parsed argument.

    Raises:
      InvalidSchemaError: if the YAML command is malformed.
    """
        group = data.get('group')
        if group:
            return ArgumentGroup.FromData(group)

        api_field = data.get('api_field')
        disable_unused_arg_check = data.get('disable_unused_arg_check')
        arg_name = data.get('arg_name', api_field)
        if not arg_name:
            raise util.InvalidSchemaError(
                'An argument must have at least one of [api_field, arg_name].')
        is_positional = data.get('is_positional')
        flag_name = arg_name if is_positional else '--' + arg_name

        if data.get('default') and data.get('fallback'):
            raise util.InvalidSchemaError(
                'An argument may have at most one of [default, fallback].')

        try:
            help_text = data['help_text']
        except KeyError:
            raise util.InvalidSchemaError('An argument must have help_text.')

        choices = data.get('choices')

        return cls(
            api_field=api_field,
            arg_name=arg_name,
            help_text=help_text,
            metavar=data.get('metavar'),
            completer=util.Hook.FromData(data, 'completer'),
            is_positional=is_positional,
            type=util.ParseType(data.get('type')),
            choices=[util.Choice(d) for d in choices] if choices else None,
            default=data.get('default', arg_utils.UNSPECIFIED),
            fallback=util.Hook.FromData(data, 'fallback'),
            processor=util.Hook.FromData(data, 'processor'),
            required=data.get('required', False),
            hidden=data.get('hidden', False),
            action=util.ParseAction(data.get('action'), flag_name),
            repeated=data.get('repeated'),
            disable_unused_arg_check=disable_unused_arg_check,
        )
class CommandSchemaUtilTests(test_case.TestCase, parameterized.TestCase):
  """Tests of the command schema utils."""

  def testHook(self):
    self.assertIsNone(util.Hook.FromData({}, 'completer'))
    h = util.Hook.FromData(
        {'completer': CompleterStub.__module__ + ':CompleterStub'}, 'completer')
    self.assertEqual(h, CompleterStub)
    h = util.Hook.FromData(
        {'completer':
             CompleterStub.__module__ + ':CompleterStub.SubCompleter'},
        'completer')
    self.assertEqual(h, CompleterStub.SubCompleter)

    h = util.ImportPythonHook(
        CompleterStub.__module__ + ':CompleterStub')
    self.assertEqual(h.attribute, CompleterStub)
    self.assertFalse(h.kwargs)

    h = util.ImportPythonHook(
        CompleterStub.__module__ + ':CompleterGen:')
    self.assertEqual(h.attribute, CompleterGen)
    self.assertFalse(h.kwargs)
    self.assertEqual(h.GetHook(), CompleterStub)

    h = util.ImportPythonHook(
        CompleterStub.__module__ + ':CompleterGen:foo=a,bar=b')
    self.assertEqual(h.attribute, CompleterGen)
    self.assertEqual(h.kwargs, {'foo': 'a', 'bar': 'b'})
    self.assertEqual(h.GetHook(), CompleterStub)

    with self.assertRaisesRegex(util.InvalidSchemaError,
                                r'Invalid Python hook: \[foo\].'):
      util.ImportPythonHook('foo')
    with self.assertRaisesRegex(util.InvalidSchemaError,
                                r'Invalid Python hook: \[a:b:c:d\].'):
      util.ImportPythonHook('a:b:c:d')
    with self.assertRaisesRegex(
        util.InvalidSchemaError,
        r'Invalid Python hook: \[{}:CompleterStub:asdf\]'
        r'. Args must be in the form'.format(CompleterStub.__module__)):
      util.ImportPythonHook(
          CompleterStub.__module__ + ':CompleterStub:asdf')

    with self.assertRaisesRegex(
        util.InvalidSchemaError,
        r'Could not import Python hook: \[foo:bar\]. Module path \[foo:bar\] '
        r'not found: No module named \'?foo\'?.'):
      util.ImportPythonHook('foo:bar')

    with self.assertRaisesRegex(
        util.InvalidSchemaError,
        r"Could not import Python hook: \[googlecloudsdk:bar\]. Module path "
        r"\[googlecloudsdk:bar\] not found: "
        r"('module' object|module 'googlecloudsdk') has no attribute 'bar'."):
      util.ImportPythonHook('googlecloudsdk:bar')

  @parameterized.parameters(
      (None, None),
      ('store', 'store'),
      ('store_true', 'store_true'),
      (CompleterStub.__module__ + ':CompleterStub', CompleterStub),
  )
  def testParseAction(self, action, output):
    self.assertEqual(util.ParseAction(action, 'foo'), output)

  def testParseActionDeprecation(self):
    result = util.ParseAction({'deprecated': {'warn': 'warn'}}, 'foo')
    self.assertTrue(issubclass(result, argparse.Action))

  @parameterized.parameters(
      (None, None),
      ('str', str),
      ('int', int),
      ('long', long if six.PY2 else int),
      ('float', float),
      ('bool', bool),
      (CompleterStub.__module__ + ':CompleterStub', CompleterStub),
  )
  def testParseType(self, t, output):
    self.assertEqual(util.ParseType(t), output)

  @parameterized.named_parameters(
      ('ArgDict', util.ArgDict.FromData),
      ('Type', lambda x: util.ParseType({'arg_dict': x})),
  )
  def testArgDict(self, loader):
    data = {'spec': [
        {'api_field': 'string1', 'arg_name': 'a'},
        {'api_field': 'enum1', 'arg_name': 'b'},
        {'api_field': 'bool1', 'arg_name': 'c'},
        {'api_field': 'int1', 'arg_name': 'd'},
        {'api_field': 'float1', 'arg_name': 'e'}
    ]}
    arg_dict = loader(data)
    dict_type = arg_dict.GenerateType(fm.FakeMessage)
    result = dict_type('a=foo,b=thing-one,c=True,d=1,e=2.0')
    self.assertEqual(
        result,
        fm.FakeMessage(string1='foo', enum1=fm.FakeMessage.FakeEnum.THING_ONE,
                       bool1=True, int1=1, float1=2.0))

  @parameterized.named_parameters(
      ('ArgDict', util.ArgDict.FromData),
      ('Type', lambda x: util.ParseType({'arg_dict': x})),
  )
  def testArgDictOptionalKeys(self, loader):
    data = {'spec': [
        {'api_field': 'string1', 'arg_name': 'a', 'required': False},
        {'api_field': 'enum1', 'arg_name': 'b', 'required': False,
         'choices': [
             {'arg_value': 'thing_one',
              'enum_value': fm.FakeMessage.FakeEnum.THING_ONE},
             {'arg_value': 'thing_two',
              'enum_value': fm.FakeMessage.FakeEnum.THING_TWO}
         ]},
        {'api_field': 'bool1', 'arg_name': 'c', 'required': False},
        {'api_field': 'int1', 'arg_name': 'd', 'required': False},
        {'api_field': 'float1', 'arg_name': 'e', 'required': False}
    ]}
    arg_dict = loader(data)
    dict_type = arg_dict.GenerateType(fm.FakeMessage)
    result = dict_type('')
    self.assertEqual(
        result,
        fm.FakeMessage(string1=None, enum1=None, bool1=None, int1=None,
                       float1=None))

  def testErrors(self):
    data = {'spec': [{'api_field': 'message1', 'arg_name': 'a'}]}
    arg_dict = util.ArgDict.FromData(data)
    with self.assertRaisesRegex(util.InvalidSchemaError,
                                'Unknown type for field: message1'):
      arg_dict.GenerateType(fm.FakeMessage)

    data['flatten'] = True
    with self.assertRaisesRegex(
        util.InvalidSchemaError,
        'Flattened ArgDicts must have exactly two items in the spec.'):
      util.ArgDict.FromData(data)

    data['spec'].append({'api_field': 'message2', 'arg_name': 'b'})
    arg_dict = util.ArgDict.FromData(data)
    with self.assertRaisesRegex(
        util.InvalidSchemaError,
        'Unknown type for field: message1'):
      arg_dict.GenerateType(fm.FakeMessage)

  @parameterized.named_parameters(
      ('ArgDict', util.ArgDict.FromData),
      ('Type', lambda x: util.ParseType({'arg_dict': x})),
  )
  def testArgDictFlattened(self, loader):
    data = {'flatten': True,
            'spec': [{'api_field': 'string1'}, {'api_field': 'string2'}]}
    arg_dict = loader(data)
    dict_type = arg_dict.GenerateType(fm.FakeMessage.InnerMessage)
    result = dict_type('a=b,c=d')
    self.assertEqual(
        result,
        [fm.FakeMessage.InnerMessage(string1='a', string2='b'),
         fm.FakeMessage.InnerMessage(string1='c', string2='d')])

  def testChoices(self):
    data = {'arg_value': 'thing-one',
            'enum_value': fm.FakeMessage.FakeEnum.THING_ONE}
    choice = util.Choice(data)
    self.assertEqual(choice.arg_value, 'thing-one')
    self.assertEqual(choice.enum_value, fm.FakeMessage.FakeEnum.THING_ONE)

  def testChoice_DefaultEnumValue(self):
    data = {'arg_value': 'thing-one'}
    choice = util.Choice(data)
    self.assertEqual(choice.arg_value, 'thing-one')
    self.assertEqual(choice.enum_value, 'THING_ONE')
 def testParseType(self, t, output):
   self.assertEqual(util.ParseType(t), output)