Skip to content

Commit 020d2f4

Browse files
committed
Cache type map in container cls, use global map for type configs
1 parent 1e8bd8f commit 020d2f4

File tree

5 files changed

+37
-26
lines changed

5 files changed

+37
-26
lines changed

src/pynwb/__init__.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ def load_type_config(**kwargs):
4646
This method will either load the default config or the config provided by the path.
4747
"""
4848
config_path = kwargs['config_path']
49-
type_map = kwargs['type_map'] or get_type_map()
49+
type_map = kwargs['type_map'] or __TYPE_MAP
5050

5151
hdmf_load_type_config(config_path=config_path, type_map=type_map)
5252

5353
@docval({'name': 'type_map', 'type': TypeMap, 'doc': 'The TypeMap.', 'default': None},
5454
is_method=False)
5555
def get_loaded_type_config(**kwargs):
56-
type_map = kwargs['type_map'] or get_type_map()
56+
type_map = kwargs['type_map'] or __TYPE_MAP
5757
return hdmf_get_loaded_type_config(type_map=type_map)
5858

5959
@docval({'name': 'type_map', 'type': TypeMap, 'doc': 'The TypeMap.', 'default': None},
@@ -62,7 +62,7 @@ def unload_type_config(**kwargs):
6262
"""
6363
Remove validation.
6464
"""
65-
type_map = kwargs['type_map'] or get_type_map()
65+
type_map = kwargs['type_map'] or __TYPE_MAP
6666
hdmf_unload_type_config(type_map=type_map)
6767

6868
def __get_resources() -> dict:
@@ -113,6 +113,7 @@ def get_type_map(**kwargs):
113113
if extensions is None:
114114
type_map = deepcopy(__TYPE_MAP)
115115
else:
116+
warnings.warn("The 'extensions' argument is deprecated and will be removed in HDMF 5.0", DeprecationWarning)
116117
if isinstance(extensions, TypeMap):
117118
type_map = extensions
118119
else:
@@ -538,7 +539,7 @@ def read_nwb(**kwargs):
538539
# Retrieve the filepath
539540
path = popargs('path', kwargs)
540541
file = popargs('file', kwargs)
541-
542+
542543
path = str(path) if path is not None else None
543544

544545
# Streaming case
@@ -556,18 +557,18 @@ def read_nwb(**kwargs):
556557

557558
return nwbfile
558559

559-
@docval({'name': 'path', 'type': (str, Path),
560+
@docval({'name': 'path', 'type': (str, Path),
560561
'doc': 'Path to the NWB file. Can be either a local filesystem path to '
561-
'an HDF5 (.nwb) or Zarr (.zarr) file.'},
562+
'an HDF5 (.nwb) or Zarr (.zarr) file.'},
562563
is_method=False)
563564
def read_nwb(**kwargs):
564565
"""Read an NWB file from a local path.
565566
566-
High-level interface for reading NWB files. Automatically handles both HDF5
567-
and Zarr formats. For advanced use cases (parallel I/O, custom namespaces),
567+
High-level interface for reading NWB files. Automatically handles both HDF5
568+
and Zarr formats. For advanced use cases (parallel I/O, custom namespaces),
568569
use NWBHDF5IO or NWBZarrIO.
569570
570-
See also
571+
See also
571572
* :py:class:`~pynwb.NWBHDF5IO`: Core I/O class for HDF5 files with advanced options.
572573
* :py:class:`~hdmf_zarr.nwb.NWBZarrIO`: Core I/O class for Zarr files with advanced options.
573574
@@ -585,17 +586,17 @@ def read_nwb(**kwargs):
585586
* Write or append modes
586587
* Pre-opened HDF5 file objects or Zarr stores
587588
* Remote file access configuration
588-
589+
589590
Example usage reading a local NWB file:
590591
591592
.. code-block:: python
592593
593594
from pynwb import read_nwb
594-
nwbfile = read_nwb("path/to/file.nwb")
595+
nwbfile = read_nwb("path/to/file.nwb")
595596
596597
:Returns: pynwb.NWBFile The loaded NWB file object.
597598
"""
598-
599+
599600
path = popargs('path', kwargs)
600601
# HDF5 is always available so we try that first
601602
backend_is_hdf5 = NWBHDF5IO.can_read(path=path)
@@ -607,18 +608,18 @@ def read_nwb(**kwargs):
607608
from hdmf_zarr import NWBZarrIO
608609
backend_is_zarr = NWBZarrIO.can_read(path=path)
609610
if backend_is_zarr:
610-
return NWBZarrIO.read_nwb(path=path)
611+
return NWBZarrIO.read_nwb(path=path)
611612
else:
612613
raise ValueError(
613614
f"Unable to read file: '{path}'. The file is not recognized as "
614615
"either a valid HDF5 or Zarr NWB file. Please ensure the file exists and contains valid NWB data."
615-
)
616+
)
616617
except ImportError:
617618
raise ValueError(
618619
f"Unable to read file: '{path}'. The file is not recognized as an HDF5 NWB file. "
619620
"If you are trying to read a Zarr file, please install hdmf-zarr using: pip install hdmf-zarr"
620621
)
621-
622+
622623

623624

624625
from . import io as __io # noqa: F401,E402
@@ -642,7 +643,7 @@ def read_nwb(**kwargs):
642643
# Functions
643644
'get_type_map',
644645
'get_manager',
645-
'load_namespaces',
646+
'load_namespaces',
646647
'available_namespaces',
647648
'clear_cache_dir',
648649
'register_class',
@@ -653,11 +654,11 @@ def read_nwb(**kwargs):
653654
'unload_type_config',
654655
'read_nwb',
655656
'get_nwbfile_version',
656-
657+
657658
# Classes
658659
'NWBHDF5IO',
659660
'NWBContainer',
660-
'NWBData',
661+
'NWBData',
661662
'TimeSeries',
662663
'ProcessingModule',
663664
'NWBFile',

src/pynwb/core.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class NWBMixin(AbstractContainer):
3232

3333
__nwbfields__ = tuple()
3434

35-
__type_map = get_type_map() # static type map created at module import
35+
__type_map = None
3636

3737
@docval({'name': 'neurodata_type', 'type': str, 'doc': 'the data_type to search for', 'default': None})
3838
def get_ancestor(self, **kwargs):
@@ -63,6 +63,10 @@ def _error_on_new_pass_on_construct(self, error_msg: str):
6363
raise ValueError(error_msg)
6464

6565
def _get_type_map(self):
66+
# Cache the type map to avoid repeated calls to get_type_map()
67+
if self.__type_map is None:
68+
self.__type_map = get_type_map()
69+
6670
return self.__type_map
6771

6872
@property

tests/integration/helpers/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def create_test_extension(specs, container_classes, mappers=None):
1919
export_spec(ns_builder, specs, output_dir.name)
2020

2121
# this will copy the global pynwb TypeMap and add the extension types to the copy
22-
type_map = get_type_map(f"{output_dir.name}/{NAMESPACE_NAME}.namespace.yaml")
22+
type_map = get_type_map()
23+
type_map.load_namespaces(f"{output_dir.name}/{NAMESPACE_NAME}.namespace.yaml")
2324
for type_name, container_cls in container_classes.items():
2425
type_map.register_container_type(NAMESPACE_NAME, type_name, container_cls)
2526
if mappers:

tests/unit/test_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class TestPyNWBTypeConfig(TestCase):
2020
def setUp(self):
2121
if not REQUIREMENTS_INSTALLED:
2222
self.skipTest("optional LinkML module is not installed")
23-
CUR_DIR = os.path.dirname(os.path.realpath(__file__))
23+
CUR_DIR = os.path.dirname(os.path.realpath(__file__))
2424
path_to_config = os.path.join(CUR_DIR, 'test_config/test_nwb_config.yaml')
2525
load_type_config(config_path=path_to_config)
2626

tests/unit/test_extension.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ def test_export(self):
4545

4646
def test_load_namespace(self):
4747
self.test_export()
48-
get_type_map(extensions=os.path.join(self.tempdir, self.ns_path))
48+
type_map = get_type_map()
49+
type_map.load_namespaces(os.path.join(self.tempdir, self.ns_path))
4950

5051
def test_get_class(self):
5152
self.test_export()
52-
type_map = get_type_map(extensions=os.path.join(self.tempdir, self.ns_path))
53+
type_map = get_type_map()
54+
type_map.load_namespaces(os.path.join(self.tempdir, self.ns_path))
5355
type_map.get_dt_container_cls('TetrodeSeries', self.prefix)
5456

5557
def test_load_namespace_with_reftype_attribute(self):
@@ -62,7 +64,8 @@ def test_load_namespace_with_reftype_attribute(self):
6264
neurodata_type_def='my_new_type')
6365
ns_builder.add_spec(self.ext_source, test_ds_ext)
6466
ns_builder.export(self.ns_path, outdir=self.tempdir)
65-
get_type_map(extensions=os.path.join(self.tempdir, self.ns_path))
67+
type_map = get_type_map()
68+
type_map.load_namespaces(os.path.join(self.tempdir, self.ns_path))
6669

6770
def test_load_namespace_with_reftype_attribute_check_autoclass_const(self):
6871
ns_builder = NWBNamespaceBuilder('Extension for use in my Lab', self.prefix, version='0.1.0')
@@ -74,7 +77,8 @@ def test_load_namespace_with_reftype_attribute_check_autoclass_const(self):
7477
neurodata_type_def='my_new_type')
7578
ns_builder.add_spec(self.ext_source, test_ds_ext)
7679
ns_builder.export(self.ns_path, outdir=self.tempdir)
77-
type_map = get_type_map(extensions=os.path.join(self.tempdir, self.ns_path))
80+
type_map = get_type_map()
81+
type_map.load_namespaces(os.path.join(self.tempdir, self.ns_path))
7882
my_new_type = type_map.get_dt_container_cls('my_new_type', self.prefix)
7983
docval = None
8084
for tmp in get_docval(my_new_type.__init__):
@@ -172,7 +176,8 @@ def test_catch_dup_name(self):
172176
neurodata_type_def='TetrodeSeries')
173177
ns_builder2.add_spec(self.ext_source2, ext2)
174178
ns_builder2.export(self.ns_path2, outdir=self.tempdir)
175-
type_map = get_type_map(extensions=os.path.join(self.tempdir, self.ns_path1))
179+
type_map = get_type_map()
180+
type_map.load_namespaces(os.path.join(self.tempdir, self.ns_path1))
176181
type_map.load_namespaces(os.path.join(self.tempdir, self.ns_path2))
177182

178183
def test_catch_dup_name_core_newer(self):

0 commit comments

Comments
 (0)