diff --git a/src/hdmf/build/map.py b/src/hdmf/build/map.py index de254ca5b..6006cc383 100644 --- a/src/hdmf/build/map.py +++ b/src/hdmf/build/map.py @@ -598,12 +598,12 @@ def __get_fields(cls, name_stack, all_names, spec): if spec.name is None: name = cls.convert_dt_name(spec) name_stack.append(name) - if name in all_names: - name = "_".join(name_stack) + name = '__'.join(name_stack) all_names[name] = spec if isinstance(spec, BaseStorageSpec): if not (spec.data_type_def is None and spec.data_type_inc is None): # don't get names for components in data_types + name_stack.pop() return for subspec in spec.attributes: cls.__get_fields(name_stack, all_names, subspec) @@ -643,10 +643,6 @@ def __map_spec(self, spec): def map_attr(self, **kwargs): """ Map an attribute to spec. Use this to override default behavior """ attr_name, spec = getargs('attr_name', 'spec', kwargs) - if hasattr(spec, 'name') and spec.name is not None: - n = spec.name - elif hasattr(spec, 'data_type_def') and spec.data_type_def is not None: - n = spec.data_type_def # noqa: F841 self.__spec2attr[spec] = attr_name self.__attr2spec[attr_name] = spec diff --git a/tests/unit/build_tests/test_io_manager.py b/tests/unit/build_tests/test_io_manager.py index c92b1301c..06a542b43 100644 --- a/tests/unit/build_tests/test_io_manager.py +++ b/tests/unit/build_tests/test_io_manager.py @@ -11,6 +11,16 @@ from tests.unit.test_utils import Foo, FooBucket, CORE_NAMESPACE +class FooMapper(ObjectMapper): + """Maps nested 'attr2' attribute on dataset 'my_data' to Foo.attr2 in constructor and attribute map + """ + + def __init__(self, spec): + super(FooMapper, self).__init__(spec) + my_data_spec = spec.get_dataset('my_data') + self.map_spec('attr2', my_data_spec.get_attribute('attr2')) + + class TestBase(unittest.TestCase): def setUp(self): @@ -37,7 +47,7 @@ def setUp(self): self.namespace_catalog.add_namespace(CORE_NAMESPACE, self.namespace) self.type_map = TypeMap(self.namespace_catalog) self.type_map.register_container_type(CORE_NAMESPACE, 'Foo', Foo) - self.type_map.register_map(Foo, ObjectMapper) + self.type_map.register_map(Foo, FooMapper) self.manager = BuildManager(self.type_map) @@ -130,7 +140,7 @@ def setUp(self): self.spec_catalog.register_spec(self.bucket_spec, 'test.yaml') self.type_map.register_container_type(CORE_NAMESPACE, 'FooBucket', FooBucket) - self.type_map.register_map(FooBucket, ObjectMapper) + self.type_map.register_map(FooBucket, self.setUpBucketMapper()) self.manager = BuildManager(self.type_map) def setUpBucketBuilder(self): @@ -139,6 +149,9 @@ def setUpBucketBuilder(self): def setUpBucketSpec(self): raise unittest.SkipTest('Abstract Base Class') + def setUpBucketMapper(self): + raise unittest.SkipTest('Abstract Base Class') + def test_build(self): ''' Test default mapping for an Container that has an Container as an attribute value ''' builder = self.manager.build(self.foo_bucket) @@ -171,6 +184,9 @@ def setUpBucketSpec(self): data_type_inc='Foo', quantity=ZERO_OR_MANY)]) + def setUpBucketMapper(self): + return ObjectMapper + class TestNestedContainersSubgroup(TestNestedBase): ''' @@ -197,6 +213,14 @@ def setUpBucketSpec(self): data_type_def='FooBucket', groups=[tmp_spec]) + def setUpBucketMapper(self): + class BucketMapper(ObjectMapper): + def __init__(self, spec): + super(BucketMapper, self).__init__(spec) + self.map_spec('foos', spec.get_group('foo_holder').get_data_type('Foo')) + + return BucketMapper + class TestNestedContainersSubgroupSubgroup(TestNestedBase): ''' @@ -225,6 +249,14 @@ def setUpBucketSpec(self): data_type_def='FooBucket', groups=[tmp_spec]) + def setUpBucketMapper(self): + class BucketMapper(ObjectMapper): + def __init__(self, spec): + super(BucketMapper, self).__init__(spec) + self.map_spec('foos', spec.get_group('foo_holder_holder').get_group('foo_holder').get_data_type('Foo')) + + return BucketMapper + class TestTypeMap(TestBase): diff --git a/tests/unit/build_tests/test_io_map.py b/tests/unit/build_tests/test_io_map.py index c563c7ebb..b5fc1ff78 100644 --- a/tests/unit/build_tests/test_io_map.py +++ b/tests/unit/build_tests/test_io_map.py @@ -348,6 +348,7 @@ def test_build(self): expected = GroupBuilder('my_bar', datasets={'data': DatasetBuilder( 'data', list(range(10)), attributes={'attr2': 10})}, attributes={'attr1': 'value1'}) + self._remap_nested_attr() builder = self.mapper.build(container_inst, self.manager) self.assertDictEqual(builder, expected) @@ -359,15 +360,27 @@ def test_construct(self): 'data', list(range(10)), attributes={'attr2': 10})}, attributes={'attr1': 'value1', 'data_type': 'Bar', 'namespace': CORE_NAMESPACE, 'object_id': expected.object_id}) + self._remap_nested_attr() container = self.mapper.construct(builder, self.manager) self.assertEqual(container, expected) def test_default_mapping_keys(self): attr_map = self.mapper.get_attr_names(self.bar_spec) keys = set(attr_map.keys()) - expected = {'attr1', 'data', 'attr2'} + expected = {'attr1', 'data', 'data__attr2'} self.assertSetEqual(keys, expected) + def test_remap_keys(self): + self._remap_nested_attr() + self.assertEqual(self.mapper.get_attr_spec('attr2'), + self.mapper.spec.get_dataset('data').get_attribute('attr2')) + self.assertEqual(self.mapper.get_attr_spec('attr1'), self.mapper.spec.get_attribute('attr1')) + self.assertEqual(self.mapper.get_attr_spec('data'), self.mapper.spec.get_dataset('data')) + + def _remap_nested_attr(self): + data_spec = self.mapper.spec.get_dataset('data') + self.mapper.map_spec('attr2', data_spec.get_attribute('attr2')) + class TestObjectMapperNoNesting(TestObjectMapper): diff --git a/tests/unit/test_io_hdf5_h5tools.py b/tests/unit/test_io_hdf5_h5tools.py index 514278e68..2b021e23d 100644 --- a/tests/unit/test_io_hdf5_h5tools.py +++ b/tests/unit/test_io_hdf5_h5tools.py @@ -424,6 +424,12 @@ def _get_manager(): data_type_def='FooBucket', groups=[tmp_spec]) + class FooMapper(ObjectMapper): + def __init__(self, spec): + super(FooMapper, self).__init__(spec) + my_data_spec = spec.get_dataset('my_data') + self.map_spec('attr2', my_data_spec.get_attribute('attr2')) + class BucketMapper(ObjectMapper): def __init__(self, spec): super(BucketMapper, self).__init__(spec) @@ -461,6 +467,7 @@ def __init__(self, spec): type_map.register_container_type(CORE_NAMESPACE, 'FooBucket', FooBucket) type_map.register_container_type(CORE_NAMESPACE, 'FooFile', FooFile) + type_map.register_map(Foo, FooMapper) type_map.register_map(FooBucket, BucketMapper) type_map.register_map(FooFile, FileMapper)