Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert breaking change of TypeMap.get_container_cls function header #590

Merged
merged 3 commits into from
Apr 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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