def _make_namespace_with_nested_types(): # type: (...) -> ApiNamespace ns = ApiNamespace('ns_w_nested_types') struct = Struct(name='NestedTypes', namespace=ns, ast_node=None) struct.set_attributes( doc=None, fields=[ StructField( name='NullableList', data_type=Nullable( List(UInt64()) ), doc=None, ast_node=None, ), StructField( name='ListOfNullables', data_type=List( Nullable(UInt64()) ), doc=None, ast_node=None, ) ] ) ns.add_data_type(struct) return ns
def test_struct_with_custom_annotations(self): # type: () -> None ns = ApiNamespace('files') annotation_type = AnnotationType('MyAnnotationType', ns, None, [ AnnotationTypeParam('test_param', Int32(), None, False, None, None) ]) ns.add_annotation_type(annotation_type) annotation = CustomAnnotation('MyAnnotation', ns, None, 'MyAnnotationType', None, [], {'test_param': 42}) annotation.set_attributes(annotation_type) ns.add_annotation(annotation) struct = Struct('MyStruct', ns, None) struct.set_attributes(None, [ StructField('annotated_field', Int32(), None, None), StructField('unannotated_field', Int32(), None, None), ]) struct.fields[0].set_annotations([annotation]) struct.recursive_custom_annotations = set([annotation]) result = self._evaluate_struct(ns, struct) expected = textwrap.dedent('''\ class MyStruct(bb.Struct): __slots__ = [ '_annotated_field_value', '_unannotated_field_value', ] _has_required_fields = True def __init__(self, annotated_field=None, unannotated_field=None): self._annotated_field_value = bb.NOT_SET self._unannotated_field_value = bb.NOT_SET if annotated_field is not None: self.annotated_field = annotated_field if unannotated_field is not None: self.unannotated_field = unannotated_field # Instance attribute type: int (validator is set below) annotated_field = bb.Attribute("annotated_field") # Instance attribute type: int (validator is set below) unannotated_field = bb.Attribute("unannotated_field") def _process_custom_annotations(self, annotation_type, field_path, processor): super(MyStruct, self)._process_custom_annotations(annotation_type, field_path, processor) if annotation_type is MyAnnotationType: self.annotated_field = bb.partially_apply(processor, MyAnnotationType(test_param=42))('{}.annotated_field'.format(field_path), self.annotated_field) MyStruct_validator = bv.Struct(MyStruct) ''') # noqa self.maxDiff = None self.assertEqual(result, expected)
def test_strip_alias(self): first_alias = Alias(None, None, None) second_alias = Alias(None, None, None) third_alias = Alias(None, None, None) first_alias.data_type = second_alias second_alias.data_type = third_alias third_alias.data_type = String() test_struct = Struct('TestStruct', None, None) test_struct.set_attributes( None, [StructField('field1', List(Nullable(first_alias)), None, None)]) curr_type = first_alias while hasattr(curr_type, 'data_type'): curr_type.data_type = resolve_aliases(curr_type.data_type) curr_type = curr_type.data_type self.assertIsInstance(first_alias.data_type, String) self.assertEqual(len(test_struct.fields), 1) field = test_struct.fields[0] strip_alias(field.data_type) list_type = field.data_type self.assertIsInstance(list_type, List) nullable_type = list_type.data_type self.assertIsInstance(nullable_type, Nullable) string_type = nullable_type.data_type self.assertIsInstance(string_type, String)
def test_api_namespace(self): ns = ApiNamespace('files') a1 = Struct('A1', None, ns) a1.set_attributes(None, [StructField('f1', Boolean(), None, None)]) a2 = Struct('A2', None, ns) a2.set_attributes(None, [StructField('f2', Boolean(), None, None)]) l1 = List(a1) s = String() route = ApiRoute('test/route', 1, None) route.set_attributes(None, None, l1, a2, s, None) ns.add_route(route) # Test that only user-defined types are returned. route_io = ns.get_route_io_data_types() self.assertIn(a1, route_io) self.assertIn(a2, route_io) self.assertNotIn(l1, route_io) self.assertNotIn(s, route_io)
def _make_namespace_with_many_structs(): # type: (...) -> ApiNamespace ns = ApiNamespace('ns_with_many_structs') struct1 = Struct(name='Struct1', namespace=ns, ast_node=None) struct1.set_attributes(None, [StructField('f1', Boolean(), None, None)]) ns.add_data_type(struct1) struct2 = Struct(name='Struct2', namespace=ns, ast_node=None) struct2.set_attributes( doc=None, fields=[ StructField('f2', List(UInt64()), None, None), StructField('f3', Timestamp(ISO_8601_FORMAT), None, None), StructField('f4', Map(String(), UInt64()), None, None) ] ) ns.add_data_type(struct2) return ns
def _make_namespace_with_nullable_and_dafault_fields(): # type: (...) -> ApiNamespace ns = ApiNamespace('ns_w_nullable__fields') struct = Struct(name='Struct1', namespace=ns, ast_node=None) default_field = StructField( name='DefaultField', data_type=UInt64(), doc=None, ast_node=None, ) default_field.set_default(1) struct.set_attributes(doc=None, fields=[ StructField( name='NullableField', data_type=Nullable(UInt64()), doc=None, ast_node=None, ), default_field, StructField( name='RequiredField', data_type=UInt64(), doc=None, ast_node=None, ) ]) ns.add_data_type(struct) return ns
def _make_namespace_with_alias(): # type: (...) -> ApiNamespace ns = ApiNamespace('ns_with_alias') struct1 = Struct(name='Struct1', namespace=ns, ast_node=None) struct1.set_attributes(None, [StructField('f1', Boolean(), None, None)]) ns.add_data_type(struct1) alias = Alias(name='AliasToStruct1', namespace=ns, ast_node=None) alias.set_attributes(doc=None, data_type=struct1) ns.add_alias(alias) return ns
def test_union(self): ns = ApiNamespace('files') update_parent_rev = Struct( 'UpdateParentRev', None, ns, ) update_parent_rev.set_attributes( "Overwrite existing file if the parent rev matches.", [ StructField('parent_rev', String(), 'The revision to be updated.', None) ], ) update_parent_rev._add_example( AstExample(path=None, lineno=None, lexpos=None, label='default', text=None, fields={ 'parent_rev': AstExampleField(None, None, None, 'parent_rev', 'xyz123') })) # test variants with only tags, as well as those with structs. conflict = Union( 'WriteConflictPolicy', None, ns, True, ) conflict.set_attributes( 'Policy for managing write conflicts.', [ UnionField('reject', Void(), 'On a write conflict, reject the new file.', None), UnionField( 'overwrite', Void(), 'On a write conflict, overwrite the existing file.', None), UnionField( 'update_if_matching_parent_rev', update_parent_rev, 'On a write conflict, overwrite the existing file.', None), ], ) conflict._add_example( AstExample(path=None, lineno=None, lexpos=None, label='default', text=None, fields={ 'update_if_matching_parent_rev': AstExampleField( None, None, None, 'update_if_matching_parent_rev', AstExampleRef(None, None, None, 'default')) })) conflict._compute_examples() # test that only null value is returned for an example of a Void type self.assertEqual(conflict.get_examples()['reject'].value, {'.tag': 'reject'}) # test that dict is returned for a tagged struct variant self.assertEqual(conflict.get_examples()['default'].value, { '.tag': 'update_if_matching_parent_rev', 'parent_rev': 'xyz123' })
def test_check_example(self): # # Test string # s = String(min_length=1, max_length=5) s.check_example( AstExampleField( path='test.stone', lineno=1, lexpos=0, name='v', value='hello', )) with self.assertRaises(InvalidSpec) as cm: s.check_example( AstExampleField( path='test.stone', lineno=1, lexpos=0, name='v', value='', )) self.assertIn("'' has fewer than 1 character(s)", cm.exception.msg) # # Test list # l1 = List(String(min_length=1), min_items=1, max_items=3) l1.check_example( AstExampleField( path='test.stone', lineno=1, lexpos=0, name='v', value=['asd'], )) with self.assertRaises(InvalidSpec) as cm: l1.check_example( AstExampleField( path='test.stone', lineno=1, lexpos=0, name='v', value=[], )) self.assertIn("has fewer than 1 item(s)", cm.exception.msg) # # Test list of lists # l1 = List(List(String(min_length=1), min_items=1)) l1.check_example( AstExampleField( path='test.stone', lineno=1, lexpos=0, name='v', value=[['asd']], )) with self.assertRaises(InvalidSpec) as cm: l1.check_example( AstExampleField( path='test.stone', lineno=1, lexpos=0, name='v', value=[[]], )) self.assertIn("has fewer than 1 item(s)", cm.exception.msg) # # Test Map type # m = Map(String(), String()) # valid example m.check_example( AstExampleField(path='test.stone', lineno=1, lexpos=0, name='v', value={"foo": "bar"})) # does not conform to declared type with self.assertRaises(InvalidSpec): m.check_example( AstExampleField(path='test.stone', lineno=1, lexpos=0, name='v', value={1: "bar"})) with self.assertRaises(ParameterError): # errors because only string types can be used as keys Map(Int32(), String()) s = Struct('S', None, None) s.set_attributes( "Docstring", [ StructField('a', UInt64(), 'a field', None), StructField('b', List(String()), 'a field', None), ], ) s._add_example( AstExample('test.stone', lineno=1, lexpos=0, label='default', text='Default example', fields={ 'a': AstExampleField( path='test.stone', lineno=2, lexpos=0, name='a', value=132, ), 'b': AstExampleField( path='test.stone', lineno=2, lexpos=0, name='b', value=['a'], ), }))
def test_struct(self): ns = ApiNamespace('test') quota_info = Struct( 'QuotaInfo', None, ns, ) quota_info.set_attributes( "Information about a user's space quota.", [ StructField('quota', UInt64(), 'Total amount of space.', None), ], ) # add an example that doesn't fit the definition of a struct with self.assertRaises(InvalidSpec) as cm: quota_info._add_example( AstExample(path=None, lineno=None, lexpos=None, label='default', text=None, fields={ 'bad_field': AstExampleField(None, None, None, 'bad_field', 'xyz123') })) self.assertIn('has unknown field', cm.exception.msg) quota_info._add_example( AstExample(path=None, lineno=None, lexpos=None, label='default', text=None, fields={ 'quota': AstExampleField(None, None, None, 'quota', 64000) })) # set null for a required field with self.assertRaises(InvalidSpec) as cm: quota_info._add_example( AstExample(path=None, lineno=None, lexpos=None, label='null', text=None, fields={ 'quota': AstExampleField(None, None, None, 'quota', None) })) self.assertEqual( "Bad example for field 'quota': null is not a valid integer", cm.exception.msg) self.assertTrue(quota_info._has_example('default')) quota_info.nullable = True # test for structs within structs account_info = Struct( 'AccountInfo', None, ns, ) account_info.set_attributes( "Information about an account.", [ StructField('account_id', String(), 'Unique identifier for account.', None), StructField('quota_info', quota_info, 'Quota', None) ], ) account_info._add_example( AstExample(path=None, lineno=None, lexpos=None, label='default', text=None, fields={ 'account_id': AstExampleField(None, None, None, 'account_id', 'xyz123'), 'quota_info': AstExampleField( None, None, None, 'quota_info', AstExampleRef(None, None, None, 'default')) })) account_info._compute_examples() # ensure that an example for quota_info is propagated up self.assertIn('quota_info', account_info.get_examples()['default'].value)
def _make_struct(struct_name, struct_field_name, namespace): # type: (typing.Text, typing.Text, ApiNamespace) -> Struct struct = Struct(name=struct_name, namespace=namespace, ast_node=None) struct.set_attributes( None, [StructField(struct_field_name, Boolean(), None, None)]) return struct
def test_struct_with_custom_annotations(self): # type: () -> None ns = ApiNamespace('files') annotation_type = AnnotationType('MyAnnotationType', ns, None, [ AnnotationTypeParam('test_param', Int32(), None, False, None, None) ]) ns.add_annotation_type(annotation_type) annotation = CustomAnnotation('MyAnnotation', ns, None, 'MyAnnotationType', None, [], {'test_param': 42}) annotation.set_attributes(annotation_type) ns.add_annotation(annotation) struct = Struct('MyStruct', ns, None) struct.set_attributes(None, [ StructField('annotated_field', Int32(), None, None), StructField('unannotated_field', Int32(), None, None), ]) struct.fields[0].set_annotations([annotation]) result = self._evaluate_struct(ns, struct) expected = textwrap.dedent('''\ class MyStruct(bb.Struct): __slots__ = [ '_annotated_field_value', '_annotated_field_present', '_unannotated_field_value', '_unannotated_field_present', ] _has_required_fields = True def __init__(self, annotated_field=None, unannotated_field=None): self._annotated_field_value = None self._annotated_field_present = False self._unannotated_field_value = None self._unannotated_field_present = False if annotated_field is not None: self.annotated_field = annotated_field if unannotated_field is not None: self.unannotated_field = unannotated_field @property def annotated_field(self): """ :rtype: long """ if self._annotated_field_present: return self._annotated_field_value else: raise AttributeError("missing required field 'annotated_field'") @annotated_field.setter def annotated_field(self, val): val = self._annotated_field_validator.validate(val) self._annotated_field_value = val self._annotated_field_present = True @annotated_field.deleter def annotated_field(self): self._annotated_field_value = None self._annotated_field_present = False @property def unannotated_field(self): """ :rtype: long """ if self._unannotated_field_present: return self._unannotated_field_value else: raise AttributeError("missing required field 'unannotated_field'") @unannotated_field.setter def unannotated_field(self, val): val = self._unannotated_field_validator.validate(val) self._unannotated_field_value = val self._unannotated_field_present = True @unannotated_field.deleter def unannotated_field(self): self._unannotated_field_value = None self._unannotated_field_present = False def _process_custom_annotations(self, annotation_type, processor): super(MyStruct, self)._process_custom_annotations(annotation_type, processor) if annotation_type is MyAnnotationType: self.annotated_field = bb.partially_apply(processor, MyAnnotationType(test_param=42))(self.annotated_field) def __repr__(self): return 'MyStruct(annotated_field={!r}, unannotated_field={!r})'.format( self._annotated_field_value, self._unannotated_field_value, ) MyStruct_validator = bv.Struct(MyStruct) ''') # noqa self.assertEqual(result, expected)
def test_no_preserve_aliases_from_api(self): api = Api(version=None) api.ensure_namespace('preserve_alias') ns = api.namespaces['preserve_alias'] namespace_id = Alias('NamespaceId', ns, None) namespace_id.data_type = String() shared_folder_id = Alias('SharedFolderId', ns, None) shared_folder_id.set_attributes(None, namespace_id) path_root_id = Alias('PathRootId', ns, None) path_root_id.set_attributes(None, shared_folder_id) ns.add_alias(namespace_id) ns.add_alias(shared_folder_id) ns.add_alias(path_root_id) test_struct = Struct('TestStruct', ns, None) test_struct.set_attributes( None, [StructField('field1', path_root_id, None, None)]) test_union = Union('TestUnion', ns, None, None) test_union.set_attributes( None, [UnionField('test', path_root_id, None, None)]) ns.add_data_type(test_struct) ns.add_data_type(test_union) struct_alias = Alias('StructAlias', ns, None) struct_alias.set_attributes(None, test_struct) ns.add_alias(struct_alias) api = remove_aliases_from_api(api) # Ensure namespace exists self.assertEqual(len(api.namespaces), 1) self.assertTrue('preserve_alias' in api.namespaces) ns = api.namespaces['preserve_alias'] # Ensure aliases are gone self.assertEqual(len(ns.aliases), 0) data_types = { data_type._name: data_type for data_type in ns.data_types } # Ensure struct and union field aliases resolve to proper types test_struct = data_types.get('TestStruct') self.assertIsInstance(test_struct, Struct) self.assertTrue(len(test_struct.fields), 1) field = test_struct.fields[0] self.assertEqual(field.name, 'field1') self.assertIsInstance(field.data_type, String) test_union = data_types['TestUnion'] self.assertTrue(len(test_union.fields), 1) field = test_union.fields[0] self.assertEqual(field.name, 'test') self.assertIsInstance(field.data_type, String)
def test_no_preserve_aliases_from_api(self): api = Api(version=None) # Ensure imports come after 'preserve_alias' lexiographicaly # to catch namespace ordering bugs api.ensure_namespace('preserve_alias') api.ensure_namespace('zzzz') ns = api.namespaces['preserve_alias'] imported = api.namespaces['zzzz'] # Setup aliases namespace_id = Alias('NamespaceId', ns, None) namespace_id.data_type = String() shared_folder_id = Alias('SharedFolderId', ns, None) shared_folder_id.set_attributes(None, namespace_id) path_root_id = Alias('PathRootId', ns, None) path_root_id.set_attributes(None, shared_folder_id) nullable_alias = Alias('NullableAlias', ns, None) nullable_alias.set_attributes(None, Nullable(path_root_id)) foo_alias = Alias('FooAlias', None, None) foo_alias.set_attributes(None, String()) bar_alias = Alias('BarAlias', None, None) bar_alias.set_attributes(None, foo_alias) ns.add_alias(namespace_id) ns.add_alias(shared_folder_id) ns.add_alias(path_root_id) ns.add_alias(nullable_alias) imported.add_alias(foo_alias) imported.add_alias(bar_alias) # Setup composite types test_struct = Struct('TestStruct', ns, None) test_struct.set_attributes(None, [ StructField('field_alias', path_root_id, None, None), StructField('field_nullable_alias', nullable_alias, None, None), StructField('field_list_of_alias', List(path_root_id), None, None) ]) test_union = Union('TestUnion', ns, None, None) test_union.set_attributes( None, [UnionField('test', path_root_id, None, None)]) dependent_struct = Struct('DependentStruct', ns, None) dependent_struct.set_attributes(None, [ StructField('field_alias', imported.alias_by_name['BarAlias'], None, None) ]) ns.add_data_type(test_struct) ns.add_data_type(test_union) ns.add_data_type(dependent_struct) # Setup aliases on composite types struct_alias = Alias('StructAlias', ns, None) struct_alias.set_attributes(None, test_struct) ns.add_alias(struct_alias) api = remove_aliases_from_api(api) # Ensure namespace exists self.assertEqual(len(api.namespaces), 2) self.assertTrue('preserve_alias' in api.namespaces) self.assertTrue('zzzz' in api.namespaces) ns = api.namespaces['preserve_alias'] imported = api.namespaces['zzzz'] # Ensure aliases are gone self.assertEqual(len(ns.aliases), 0) self.assertEqual(len(imported.aliases), 0) data_types = { data_type._name: data_type for data_type in ns.data_types } # Ensure struct and union field aliases resolve to proper types test_struct = data_types.get('TestStruct') self.assertIsInstance(test_struct, Struct) self.assertEqual(len(test_struct.fields), 3) for field in test_struct.fields: if field.name == 'field_list_of_alias': self.assertIsInstance(field.data_type, List) list_type = field.data_type.data_type self.assertIsInstance(list_type, String) elif field.name == 'field_nullable_alias': field_type = field.data_type self.assertIsInstance(field_type, Nullable) self.assertIsInstance(field_type.data_type, String) else: self.assertIsInstance(field.data_type, String) test_union = data_types['TestUnion'] self.assertTrue(len(test_union.fields), 1) field = test_union.fields[0] self.assertEqual(field.name, 'test') self.assertIsInstance(field.data_type, String) # Ensure struct using imported alias resolves properly dependent_struct = data_types.get('DependentStruct') self.assertIsInstance(dependent_struct, Struct) self.assertEqual(len(dependent_struct.fields), 1) field = dependent_struct.fields[0] self.assertIsInstance(field.data_type, String)
def test_preserve_aliases_from_api(self): api = Api(version=None) # Ensure imports come after 'preserve_alias' lexiographicaly # to catch namespace ordering bugs api.ensure_namespace('preserve_alias') api.ensure_namespace('zzzz') ns = api.namespaces['preserve_alias'] imported = api.namespaces['zzzz'] namespace_id = Alias('NamespaceId', ns, None) namespace_id.data_type = String() shared_folder_id = Alias('SharedFolderId', ns, None) shared_folder_id.set_attributes(None, namespace_id) path_root_id = Alias('PathRootId', ns, None) path_root_id.set_attributes(None, shared_folder_id) foo_alias = Alias('FooAlias', None, None) foo_alias.set_attributes(None, String()) bar_alias = Alias('BarAlias', None, None) bar_alias.set_attributes(None, foo_alias) ns.add_alias(namespace_id) ns.add_alias(shared_folder_id) ns.add_alias(path_root_id) imported.add_alias(foo_alias) imported.add_alias(bar_alias) test_struct = Struct('TestStruct', ns, None) test_struct.set_attributes( None, [StructField('field1', path_root_id, None, None)]) test_union = Union('TestUnion', ns, None, None) test_union.set_attributes( None, [UnionField('test', path_root_id, None, None)]) dependent_struct = Struct('DependentStruct', ns, None) dependent_struct.set_attributes(None, [ StructField('field_alias', imported.alias_by_name['BarAlias'], None, None) ]) ns.add_data_type(test_struct) ns.add_data_type(test_union) ns.add_data_type(dependent_struct) struct_alias = Alias('StructAlias', ns, None) struct_alias.set_attributes(None, test_struct) ns.add_alias(struct_alias) # Ensure namespace exists self.assertEqual(len(api.namespaces), 2) self.assertTrue('preserve_alias' in api.namespaces) self.assertTrue('zzzz' in api.namespaces) ns = api.namespaces['preserve_alias'] imported = api.namespaces['zzzz'] # Ensure aliases exist self.assertEqual(len(ns.aliases), 4) self.assertEqual(len(imported.aliases), 2) aliases = {alias._name: alias for alias in ns.aliases} imported_aliases = {alias.name: alias for alias in imported.aliases} data_types = { data_type._name: data_type for data_type in ns.data_types } # Ensure aliases are in the namespace self.assertTrue('NamespaceId' in aliases) self.assertTrue('SharedFolderId' in aliases) self.assertTrue('PathRootId' in aliases) self.assertTrue('StructAlias' in aliases) self.assertTrue('FooAlias' in imported_aliases) self.assertTrue('BarAlias' in imported_aliases) # Ensure aliases resolve to proper types self.assertIsInstance(aliases['NamespaceId'].data_type, String) self.assertIsInstance(aliases['SharedFolderId'].data_type, Alias) self.assertIsInstance(aliases['PathRootId'].data_type, Alias) self.assertIsInstance(aliases['StructAlias'].data_type, Struct) self.assertIsInstance(imported_aliases['FooAlias'].data_type, String) self.assertIsInstance(imported_aliases['BarAlias'].data_type, Alias) # Ensure struct and union field aliases resolve to proper types self.assertIsInstance(data_types['TestStruct'], Struct) test_struct = data_types.get('TestStruct') dependent_struct = data_types.get('DependentStruct') self.assertTrue(len(test_struct.fields), 1) self.assertTrue(len(dependent_struct.fields), 1) field = test_struct.fields[0] self.assertEqual(field.name, 'field1') self.assertIsInstance(field.data_type, Alias) field = dependent_struct.fields[0] self.assertEqual(field.name, 'field_alias') self.assertIsInstance(field.data_type, Alias) test_union = data_types['TestUnion'] self.assertTrue(len(test_union.fields), 1) field = test_union.fields[0] self.assertEqual(field.name, 'test') self.assertIsInstance(field.data_type, Alias)