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)