Coverage for src/hdmf/common/__init__.py: 75%
123 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-04 02:57 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-04 02:57 +0000
1'''This package will contain functions, classes, and objects
2for reading and writing data in according to the HDMF-common specification
3'''
4import os.path
5from copy import deepcopy
7CORE_NAMESPACE = 'hdmf-common'
8EXP_NAMESPACE = 'hdmf-experimental'
11from ..spec import NamespaceCatalog # noqa: E402
12from ..utils import docval, getargs, get_docval # noqa: E402
13from ..backends.io import HDMFIO # noqa: E402
14from ..backends.hdf5 import HDF5IO # noqa: E402
15from ..validate import ValidatorMap # noqa: E402
16from ..build import BuildManager, TypeMap # noqa: E402
17from ..container import _set_exp # noqa: E402
20# a global type map
21global __TYPE_MAP
24# a function to register a container classes with the global map
25@docval({'name': 'data_type', 'type': str, 'doc': 'the data_type to get the spec for'},
26 {'name': 'namespace', 'type': str, 'doc': 'the name of the namespace', 'default': CORE_NAMESPACE},
27 {"name": "container_cls", "type": type,
28 "doc": "the class to map to the specified data_type", 'default': None},
29 is_method=False)
30def register_class(**kwargs):
31 """Register an Container class to use for reading and writing a data_type from a specification
32 If container_cls is not specified, returns a decorator for registering an Container subclass
33 as the class for data_type in namespace.
34 """
35 data_type, namespace, container_cls = getargs('data_type', 'namespace', 'container_cls', kwargs)
36 if namespace == EXP_NAMESPACE:
37 def _dec(cls):
38 _set_exp(cls)
39 __TYPE_MAP.register_container_type(namespace, data_type, cls)
40 return cls
41 else:
42 def _dec(cls):
43 __TYPE_MAP.register_container_type(namespace, data_type, cls)
44 return cls
46 if container_cls is None: 46 ↛ 49line 46 didn't jump to line 49, because the condition on line 46 was never false
47 return _dec
48 else:
49 _dec(container_cls)
52# a function to register an object mapper for a container class
53@docval({"name": "container_cls", "type": type,
54 "doc": "the Container class for which the given ObjectMapper class gets used for"},
55 {"name": "mapper_cls", "type": type, "doc": "the ObjectMapper class to use to map", 'default': None},
56 is_method=False)
57def register_map(**kwargs):
58 """Register an ObjectMapper to use for a Container class type
59 If mapper_cls is not specified, returns a decorator for registering an ObjectMapper class
60 as the mapper for container_cls. If mapper_cls specified, register the class as the mapper for container_cls
61 """
62 container_cls, mapper_cls = getargs('container_cls', 'mapper_cls', kwargs)
64 def _dec(cls):
65 __TYPE_MAP.register_map(container_cls, cls)
66 return cls
67 if mapper_cls is None: 67 ↛ 70line 67 didn't jump to line 70, because the condition on line 67 was never false
68 return _dec
69 else:
70 _dec(mapper_cls)
73def __get_resources():
74 try:
75 from importlib.resources import files
76 except ImportError:
77 # TODO: Remove when python 3.9 becomes the new minimum
78 from importlib_resources import files
80 __location_of_this_file = files(__name__)
81 __core_ns_file_name = 'namespace.yaml'
82 __schema_dir = 'hdmf-common-schema/common'
84 ret = dict()
85 ret['namespace_path'] = str(__location_of_this_file / __schema_dir / __core_ns_file_name)
86 return ret
89def _get_resources():
90 # LEGACY: Needed to support legacy implementation.
91 return __get_resources()
94@docval({'name': 'namespace_path', 'type': str,
95 'doc': 'the path to the YAML with the namespace definition'},
96 returns="the namespaces loaded from the given file", rtype=tuple,
97 is_method=False)
98def load_namespaces(**kwargs):
99 '''
100 Load namespaces from file
101 '''
102 namespace_path = getargs('namespace_path', kwargs)
103 return __TYPE_MAP.load_namespaces(namespace_path)
106def available_namespaces():
107 return __TYPE_MAP.namespace_catalog.namespaces
110# a function to get the container class for a give type
111@docval({'name': 'data_type', 'type': str,
112 'doc': 'the data_type to get the Container class for'},
113 {'name': 'namespace', 'type': str, 'doc': 'the namespace the data_type is defined in'},
114 is_method=False)
115def get_class(**kwargs):
116 """Get the class object of the Container subclass corresponding to a given neurdata_type.
117 """
118 data_type, namespace = getargs('data_type', 'namespace', kwargs)
119 return __TYPE_MAP.get_dt_container_cls(data_type, namespace)
122@docval({'name': 'extensions', 'type': (str, TypeMap, list),
123 'doc': 'a path to a namespace, a TypeMap, or a list consisting paths to namespaces and TypeMaps',
124 'default': None},
125 returns="the namespaces loaded from the given file", rtype=tuple,
126 is_method=False)
127def get_type_map(**kwargs):
128 '''
129 Get a BuildManager to use for I/O using the given extensions. If no extensions are provided,
130 return a BuildManager that uses the core namespace
131 '''
132 extensions = getargs('extensions', kwargs)
133 type_map = None
134 if extensions is None: 134 ↛ 137line 134 didn't jump to line 137, because the condition on line 134 was never false
135 type_map = deepcopy(__TYPE_MAP)
136 else:
137 if isinstance(extensions, TypeMap):
138 type_map = extensions
139 else:
140 type_map = deepcopy(__TYPE_MAP)
141 if isinstance(extensions, list):
142 for ext in extensions:
143 if isinstance(ext, str):
144 type_map.load_namespaces(ext)
145 elif isinstance(ext, TypeMap):
146 type_map.merge(ext)
147 else:
148 msg = 'extensions must be a list of paths to namespace specs or a TypeMaps'
149 raise ValueError(msg)
150 elif isinstance(extensions, str):
151 type_map.load_namespaces(extensions)
152 elif isinstance(extensions, TypeMap):
153 type_map.merge(extensions)
154 return type_map
157@docval(*get_docval(get_type_map),
158 returns="a build manager with namespaces loaded from the given file", rtype=BuildManager,
159 is_method=False)
160def get_manager(**kwargs):
161 '''
162 Get a BuildManager to use for I/O using the given extensions. If no extensions are provided,
163 return a BuildManager that uses the core namespace
164 '''
165 type_map = get_type_map(**kwargs)
166 return BuildManager(type_map)
169@docval({'name': 'io', 'type': HDMFIO,
170 'doc': 'the HDMFIO object to read from'},
171 {'name': 'namespace', 'type': str,
172 'doc': 'the namespace to validate against', 'default': CORE_NAMESPACE},
173 {'name': 'experimental', 'type': bool,
174 'doc': 'data type is an experimental data type', 'default': False},
175 returns="errors in the file", rtype=list,
176 is_method=False)
177def validate(**kwargs):
178 """Validate an file against a namespace"""
179 io, namespace, experimental = getargs('io', 'namespace', 'experimental', kwargs)
180 if experimental: 180 ↛ 181line 180 didn't jump to line 181, because the condition on line 180 was never true
181 namespace = EXP_NAMESPACE
182 builder = io.read_builder()
183 validator = ValidatorMap(io.manager.namespace_catalog.get_namespace(name=namespace))
184 return validator.validate(builder)
187@docval(*get_docval(HDF5IO.__init__), is_method=False)
188def get_hdf5io(**kwargs):
189 """
190 A convenience method for getting an HDF5IO object using an HDMF-common build manager if none is provided.
191 """
192 manager = getargs('manager', kwargs)
193 if manager is None:
194 kwargs['manager'] = get_manager()
195 return HDF5IO(**kwargs)
198# load the hdmf-common namespace
199__resources = __get_resources()
200if os.path.exists(__resources['namespace_path']): 200 ↛ 223line 200 didn't jump to line 223, because the condition on line 200 was never false
201 __TYPE_MAP = TypeMap(NamespaceCatalog())
203 load_namespaces(__resources['namespace_path'])
205 # import these so the TypeMap gets populated
206 from . import io as __io # noqa: E402
208 from . import table # noqa: E402
209 from . import alignedtable # noqa: E402
210 from . import sparse # noqa: E402
211 from . import resources # noqa: E402
212 from . import multi # noqa: E402
214 # register custom class generators
215 from .io.table import DynamicTableGenerator
216 __TYPE_MAP.register_generator(DynamicTableGenerator)
218 from .. import Data, Container
219 __TYPE_MAP.register_container_type(CORE_NAMESPACE, 'Container', Container)
220 __TYPE_MAP.register_container_type(CORE_NAMESPACE, 'Data', Data)
222else:
223 raise RuntimeError("Unable to load a TypeMap - no namespace file found")
226DynamicTable = get_class('DynamicTable', CORE_NAMESPACE)
227VectorData = get_class('VectorData', CORE_NAMESPACE)
228VectorIndex = get_class('VectorIndex', CORE_NAMESPACE)
229ElementIdentifiers = get_class('ElementIdentifiers', CORE_NAMESPACE)
230DynamicTableRegion = get_class('DynamicTableRegion', CORE_NAMESPACE)
231EnumData = get_class('EnumData', EXP_NAMESPACE)
232CSRMatrix = get_class('CSRMatrix', CORE_NAMESPACE)
233HERD = get_class('HERD', EXP_NAMESPACE)
234SimpleMultiContainer = get_class('SimpleMultiContainer', CORE_NAMESPACE)
235AlignedDynamicTable = get_class('AlignedDynamicTable', CORE_NAMESPACE)