diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a0845ed..fb8cf7a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Bug Fixes * Fix reading of cached specs and caching of specs during export. @rly [#232](https://github.com/hdmf-dev/hdmf-zarr/pull/232) +* Fix hiding of pynwb compatibility errors. @rly [242](https://github.com/hdmf-dev/hdmf-zarr/pull/242) ## 0.9.0 (September 16, 2024) ### Enhancements diff --git a/pyproject.toml b/pyproject.toml index 199a0f56..09a5c881 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,11 +29,11 @@ classifiers = [ "Topic :: Scientific/Engineering :: Medical Science Apps." ] dependencies = [ - 'hdmf>=3.14.3', + 'hdmf>=3.14.5', 'zarr>=2.11.0, <3.0', # pin below 3.0 until HDMF-zarr supports zarr 3.0 'numpy>=1.24', 'numcodecs>=0.9.1', - 'pynwb>=2.5.0', + 'pynwb>=2.8.3', 'threadpoolctl>=3.1.0', ] dynamic = ["version"] @@ -123,7 +123,7 @@ exclude = [ ] line-length = 120 -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "docs/gallery/*" = ["E402", "T201"] "src/*/__init__.py" = ["F401"] "test_gallery.py" = ["T201"] diff --git a/requirements.txt b/requirements.txt index ff40e127..57647cf2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,8 @@ hdmf==3.14.5 zarr==2.18.3; python_version >= "3.10" # zarr 2.18.3 dropped support for python 3.9 zarr==2.18.2; python_version < "3.10" -pynwb==2.8.2 -numpy==2.1.3 +pynwb==2.8.3 +numpy==2.1.3; python_version >= "3.10" # numpy 2.1.0 dropped support for python 3.9 +numpy==2.0.2; python_version < "3.10" numcodecs==0.13.1 threadpoolctl==3.5.0 diff --git a/src/hdmf_zarr/nwb.py b/src/hdmf_zarr/nwb.py index 762b6b3c..2b4d3bfb 100644 --- a/src/hdmf_zarr/nwb.py +++ b/src/hdmf_zarr/nwb.py @@ -1,5 +1,4 @@ """Module with Zarr backend for NWB for integration with PyNWB""" -from warnings import warn from pathlib import Path from .backend import ZarrIO, SUPPORTED_ZARR_STORES @@ -9,85 +8,82 @@ from hdmf.backends.io import HDMFIO from hdmf.build import (BuildManager, TypeMap) -try: - from pynwb import get_manager, get_type_map +from pynwb import get_manager, get_type_map - class NWBZarrIO(ZarrIO): - """ - IO backend for PyNWB for writing NWB files - This class is similar to the :py:class:`~pynwb.NWBHDF5IO` class in PyNWB. The main purpose of this class - is to perform default setup for BuildManager, loading or namespaces etc., in the context - of the NWB format. - """ - @docval(*get_docval(ZarrIO.__init__), - {'name': 'load_namespaces', 'type': bool, - 'doc': 'whether or not to load cached namespaces from given path - not applicable in write mode', - 'default': True}, - {'name': 'extensions', 'type': (str, TypeMap, list), - 'doc': 'a path to a namespace, a TypeMap, or a list consisting paths to namespaces and TypeMaps', - 'default': None}) - def __init__(self, **kwargs): - path, mode, manager, extensions, load_namespaces, synchronizer, storage_options = \ - popargs('path', 'mode', 'manager', 'extensions', - 'load_namespaces', 'synchronizer', 'storage_options', kwargs) +class NWBZarrIO(ZarrIO): + """ + IO backend for PyNWB for writing NWB files - io_modes_that_create_file = ['w', 'w-', 'x'] - if mode in io_modes_that_create_file or manager is not None or extensions is not None: - load_namespaces = False + This class is similar to the :py:class:`~pynwb.NWBHDF5IO` class in PyNWB. The main purpose of this class + is to perform default setup for BuildManager, loading or namespaces etc., in the context + of the NWB format. + """ + @docval(*get_docval(ZarrIO.__init__), + {'name': 'load_namespaces', 'type': bool, + 'doc': 'whether or not to load cached namespaces from given path - not applicable in write mode', + 'default': True}, + {'name': 'extensions', 'type': (str, TypeMap, list), + 'doc': 'a path to a namespace, a TypeMap, or a list consisting paths to namespaces and TypeMaps', + 'default': None}) + def __init__(self, **kwargs): + path, mode, manager, extensions, load_namespaces, synchronizer, storage_options = \ + popargs('path', 'mode', 'manager', 'extensions', + 'load_namespaces', 'synchronizer', 'storage_options', kwargs) - if load_namespaces: - tm = get_type_map() - super(NWBZarrIO, self).load_namespaces(tm, path, storage_options) - manager = BuildManager(tm) - else: - if manager is not None and extensions is not None: - raise ValueError("'manager' and 'extensions' cannot be specified together") - elif extensions is not None: - manager = get_manager(extensions=extensions) - elif manager is None: - manager = get_manager() - super(NWBZarrIO, self).__init__(path, - manager=manager, - mode=mode, - synchronizer=synchronizer, - storage_options=storage_options) + io_modes_that_create_file = ['w', 'w-', 'x'] + if mode in io_modes_that_create_file or manager is not None or extensions is not None: + load_namespaces = False - @docval({'name': 'src_io', 'type': HDMFIO, 'doc': 'the HDMFIO object for reading the data to export'}, - {'name': 'nwbfile', 'type': 'NWBFile', - 'doc': 'the NWBFile object to export. If None, then the entire contents of src_io will be exported', - 'default': None}, - {'name': 'write_args', 'type': dict, 'doc': 'arguments to pass to :py:meth:`write_builder`', - 'default': dict()}) - def export(self, **kwargs): - nwbfile = popargs('nwbfile', kwargs) - kwargs['container'] = nwbfile - super().export(**kwargs) + if load_namespaces: + tm = get_type_map() + super(NWBZarrIO, self).load_namespaces(tm, path, storage_options) + manager = BuildManager(tm) + else: + if manager is not None and extensions is not None: + raise ValueError("'manager' and 'extensions' cannot be specified together") + elif extensions is not None: + manager = get_manager(extensions=extensions) + elif manager is None: + manager = get_manager() + super(NWBZarrIO, self).__init__(path, + manager=manager, + mode=mode, + synchronizer=synchronizer, + storage_options=storage_options) - @staticmethod - @docval({'name': 'path', - 'type': (str, Path, *SUPPORTED_ZARR_STORES), - 'doc': 'the path to the Zarr file or a supported Zarr store'}, - is_method=False) - def read_nwb(**kwargs): - """ - Helper factory method for reading an NWB file and return the NWBFile object - """ - # Retrieve the filepath - path = popargs('path', kwargs) - if isinstance(path, Path): - path = str(path) - # determine default storage options to use when opening a file from S3 - storage_options = {} - if isinstance(path, str) and path.startswith(("s3://")): - storage_options = dict(anon=True) + @docval({'name': 'src_io', 'type': HDMFIO, 'doc': 'the HDMFIO object for reading the data to export'}, + {'name': 'nwbfile', 'type': 'NWBFile', + 'doc': 'the NWBFile object to export. If None, then the entire contents of src_io will be exported', + 'default': None}, + {'name': 'write_args', 'type': dict, 'doc': 'arguments to pass to :py:meth:`write_builder`', + 'default': dict()}) + def export(self, **kwargs): + nwbfile = popargs('nwbfile', kwargs) + kwargs['container'] = nwbfile + super().export(**kwargs) - # open the file with NWBZarrIO and rad the file - io = NWBZarrIO(path=path, mode="r", load_namespaces=True, storage_options=storage_options) - nwbfile = io.read() + @staticmethod + @docval({'name': 'path', + 'type': (str, Path, *SUPPORTED_ZARR_STORES), + 'doc': 'the path to the Zarr file or a supported Zarr store'}, + is_method=False) + def read_nwb(**kwargs): + """ + Helper factory method for reading an NWB file and return the NWBFile object + """ + # Retrieve the filepath + path = popargs('path', kwargs) + if isinstance(path, Path): + path = str(path) + # determine default storage options to use when opening a file from S3 + storage_options = {} + if isinstance(path, str) and path.startswith(("s3://")): + storage_options = dict(anon=True) - # return the NWBFile object - return nwbfile + # open the file with NWBZarrIO and rad the file + io = NWBZarrIO(path=path, mode="r", load_namespaces=True, storage_options=storage_options) + nwbfile = io.read() -except ImportError: - warn("PyNWB is not installed. Support for NWBZarrIO is disabled.") + # return the NWBFile object + return nwbfile