Skip to content

Commit

Permalink
Revert breaking change of TypeMap.get_container_cls function header (
Browse files Browse the repository at this point in the history
…#590)

* Revert breaking change of get_container_cls function header

* Update changelog

* Fix flake8
  • Loading branch information
rly authored Apr 23, 2021
1 parent 1365149 commit 73d9336
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 81 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# HDMF Changelog

## HDMF 2.5.1 (April 23, 2021)

### Bug fix
- Revert breaking change in `TypeMap.get_container_cls`. While this function is returned to its original behavior,
it will be modified at the next major release. Please use the new `TypeMap.get_dt_container_cls` instead. @rly (#590)

## HDMF 2.5.0 (April 22, 2021)

### New features
Expand Down
2 changes: 1 addition & 1 deletion src/hdmf/build/classgenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def _get_container_type(cls, type_name, type_map):
"""Search all namespaces for the container class associated with the given data type.
Raises TypeDoesNotExistError if type is not found in any namespace.
"""
container_type = type_map.get_container_cls(type_name)
container_type = type_map.get_dt_container_cls(type_name)
if container_type is None: # pragma: no cover
# this should never happen after hdmf#322
raise TypeDoesNotExistError("Type '%s' does not exist." % type_name)
Expand Down
38 changes: 27 additions & 11 deletions src/hdmf/build/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,30 +475,46 @@ def load_namespaces(self, **kwargs):
for new_ns, ns_deps in deps.items():
for src_ns, types in ns_deps.items():
for dt in types:
container_cls = self.get_container_cls(dt, src_ns, autogen=False)
container_cls = self.get_dt_container_cls(dt, src_ns, autogen=False)
if container_cls is None:
container_cls = TypeSource(src_ns, dt)
self.register_container_type(new_ns, dt, container_cls)
return deps

@docval({"name": "namespace", "type": str, "doc": "the namespace containing the data_type"},
{"name": "data_type", "type": str, "doc": "the data type to create a AbstractContainer class for"},
{"name": "autogen", "type": bool, "doc": "autogenerate class if one does not exist", "default": True},
returns='the class for the given namespace and data_type', rtype=type)
def get_container_cls(self, **kwargs):
"""Get the container class from data type specification.
If no class has been associated with the ``data_type`` from ``namespace``, a class will be dynamically
created and returned.
"""
# NOTE: this internally used function get_container_cls will be removed in favor of get_dt_container_cls
namespace, data_type, autogen = getargs('namespace', 'data_type', 'autogen', kwargs)
return self.get_dt_container_cls(data_type, namespace, autogen)

@docval({"name": "data_type", "type": str, "doc": "the data type to create a AbstractContainer class for"},
{"name": "namespace", "type": str, "doc": "the namespace containing the data_type", "default": None},
{"name": "autogen", "type": bool, "doc": "autogenerate class if one does not exist", "default": True},
returns='the class for the given namespace and data_type', rtype=type)
def get_container_cls(self, **kwargs):
def get_dt_container_cls(self, **kwargs):
"""Get the container class from data type specification.
If no class has been associated with the ``data_type`` from ``namespace``, a class will be dynamically
created and returned.
Replaces get_container_cls but namespace is optional. If namespace is unknown, it will be looked up from
all namespaces.
"""
namespace, data_type, autogen = getargs('namespace', 'data_type', 'autogen', kwargs)

# namespace is unknown, so look it up
if namespace is None:
for key, val in self.__container_types.items():
for ns_key, ns_data_types in self.__container_types.items():
# NOTE that the type_name may appear in multiple namespaces based on how they were resolved
# but the same type_name should point to the same class
if data_type in val:
namespace = key
if data_type in ns_data_types:
namespace = ns_key
break

cls = self.__get_container_cls(namespace, data_type)
Expand All @@ -517,18 +533,18 @@ def __check_dependent_types(self, spec, namespace):
def __check_dependent_types_helper(spec, namespace):
if isinstance(spec, (GroupSpec, DatasetSpec)):
if spec.data_type_inc is not None:
self.get_container_cls(spec.data_type_inc, namespace) # TODO handle recursive definitions
self.get_dt_container_cls(spec.data_type_inc, namespace) # TODO handle recursive definitions
if spec.data_type_def is not None:
self.get_container_cls(spec.data_type_def, namespace)
self.get_dt_container_cls(spec.data_type_def, namespace)
elif isinstance(spec, LinkSpec):
if spec.target_type is not None:
self.get_container_cls(spec.target_type, namespace)
self.get_dt_container_cls(spec.target_type, namespace)
if isinstance(spec, GroupSpec):
for child_spec in (spec.groups + spec.datasets + spec.links):
__check_dependent_types_helper(child_spec, namespace)

if spec.data_type_inc is not None:
self.get_container_cls(spec.data_type_inc, namespace)
self.get_dt_container_cls(spec.data_type_inc, namespace)
if isinstance(spec, GroupSpec):
for child_spec in (spec.groups + spec.datasets + spec.links):
__check_dependent_types_helper(child_spec, namespace)
Expand Down Expand Up @@ -560,7 +576,7 @@ def __get_container_cls(self, namespace, data_type):
return None
ret = self.__container_types[namespace][data_type]
if isinstance(ret, TypeSource): # data_type is a dependency from ret.namespace
cls = self.get_container_cls(ret.data_type, ret.namespace) # get class / generate class
cls = self.get_dt_container_cls(ret.data_type, ret.namespace) # get class / generate class
# register the same class into this namespace (replaces TypeSource)
self.register_container_type(namespace, data_type, cls)
ret = cls
Expand Down Expand Up @@ -619,7 +635,7 @@ def get_cls(self, **kwargs):
namespace = self.get_builder_ns(builder)
if namespace is None:
raise ValueError("No namespace found for builder %s" % builder.path)
return self.get_container_cls(data_type, namespace)
return self.get_dt_container_cls(data_type, namespace)

@docval({'name': 'spec', 'type': (DatasetSpec, GroupSpec), 'doc': 'the parent spec to search'},
{'name': 'builder', 'type': (DatasetBuilder, GroupBuilder, LinkBuilder),
Expand Down
44 changes: 22 additions & 22 deletions src/hdmf/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ def available_namespaces():
return __TYPE_MAP.namespace_catalog.namespaces


# a function to get the container class for a give type
@docval({'name': 'data_type', 'type': str,
'doc': 'the data_type to get the Container class for'},
{'name': 'namespace', 'type': str, 'doc': 'the namespace the data_type is defined in'},
is_method=False)
def get_class(**kwargs):
"""Get the class object of the Container subclass corresponding to a given neurdata_type.
"""
data_type, namespace = getargs('data_type', 'namespace', kwargs)
return __TYPE_MAP.get_dt_container_cls(data_type, namespace)


# load the hdmf-common namespace
__resources = __get_resources()
if os.path.exists(__resources['namespace_path']):
Expand Down Expand Up @@ -129,16 +141,16 @@ def available_namespaces():
raise RuntimeError("Unable to load a TypeMap - no namespace file found")


DynamicTable = __TYPE_MAP.get_container_cls('DynamicTable', CORE_NAMESPACE)
VectorData = __TYPE_MAP.get_container_cls('VectorData', CORE_NAMESPACE)
VectorIndex = __TYPE_MAP.get_container_cls('VectorIndex', CORE_NAMESPACE)
ElementIdentifiers = __TYPE_MAP.get_container_cls('ElementIdentifiers', CORE_NAMESPACE)
DynamicTableRegion = __TYPE_MAP.get_container_cls('DynamicTableRegion', CORE_NAMESPACE)
EnumData = __TYPE_MAP.get_container_cls('EnumData', EXP_NAMESPACE)
CSRMatrix = __TYPE_MAP.get_container_cls('CSRMatrix', CORE_NAMESPACE)
ExternalResources = __TYPE_MAP.get_container_cls('ExternalResources', EXP_NAMESPACE)
SimpleMultiContainer = __TYPE_MAP.get_container_cls('SimpleMultiContainer', CORE_NAMESPACE)
AlignedDynamicTable = __TYPE_MAP.get_container_cls('AlignedDynamicTable', CORE_NAMESPACE)
DynamicTable = get_class('DynamicTable', CORE_NAMESPACE)
VectorData = get_class('VectorData', CORE_NAMESPACE)
VectorIndex = get_class('VectorIndex', CORE_NAMESPACE)
ElementIdentifiers = get_class('ElementIdentifiers', CORE_NAMESPACE)
DynamicTableRegion = get_class('DynamicTableRegion', CORE_NAMESPACE)
EnumData = get_class('EnumData', EXP_NAMESPACE)
CSRMatrix = get_class('CSRMatrix', CORE_NAMESPACE)
ExternalResources = get_class('ExternalResources', EXP_NAMESPACE)
SimpleMultiContainer = get_class('SimpleMultiContainer', CORE_NAMESPACE)
AlignedDynamicTable = get_class('AlignedDynamicTable', CORE_NAMESPACE)


@docval({'name': 'extensions', 'type': (str, TypeMap, list),
Expand Down Expand Up @@ -190,18 +202,6 @@ def get_manager(**kwargs):
return BuildManager(type_map)


# a function to get the container class for a give type
@docval({'name': 'data_type', 'type': str,
'doc': 'the data_type to get the Container class for'},
{'name': 'namespace', 'type': str, 'doc': 'the namespace the data_type is defined in'},
is_method=False)
def get_class(**kwargs):
"""Get the class object of the Container subclass corresponding to a given neurdata_type.
"""
data_type, namespace = getargs('data_type', 'namespace', kwargs)
return __TYPE_MAP.get_container_cls(data_type, namespace)


@docval({'name': 'io', 'type': HDMFIO,
'doc': 'the HDMFIO object to read from'},
{'name': 'namespace', 'type': str,
Expand Down
Loading

0 comments on commit 73d9336

Please sign in to comment.