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

add NWBHDF5IO.read_nwb() method #1979

Merged
merged 12 commits into from
Nov 12, 2024
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## PyNWB 2.8.3 (Upcoming)

### Enhancements and minor changes
* Added `NWBHDF5IO.read_nwb` convenience method to simplify reading an NWB file. @h-mayorquin [#1979](https://github.com/NeurodataWithoutBorders/pynwb/pull/1979)

### Documentation and tutorial enhancements
- Added documentation example for `SpikeEventSeries`. @stephprince [#1983](https://github.com/NeurodataWithoutBorders/pynwb/pull/1983)

Expand Down
1 change: 1 addition & 0 deletions requirements-opt.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
linkml-runtime==1.7.4; python_version >= "3.9"
schemasheets==0.2.1; python_version >= "3.9"
oaklib==0.5.32; python_version >= "3.9"
fsspec==2024.10.0
28 changes: 28 additions & 0 deletions src/pynwb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,34 @@
kwargs['container'] = nwbfile
super().export(**kwargs)

@staticmethod
@docval({'name': 'path', 'type': (str, Path), 'doc': 'the path to the HDF5 file', 'default': None},
{'name': 'file', 'type': [h5py.File, 'S3File'], 'doc': 'a pre-existing h5py.File object', 'default': None},
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)
file = popargs('file', kwargs)

path = str(path) if path is not None else None

# Streaming case
if path is not None and (path.startswith("s3://") or path.startswith("http")):
import fsspec
fsspec_file_system = fsspec.filesystem("http")
ffspec_file = fsspec_file_system.open(path, "rb")

Check warning on line 527 in src/pynwb/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/pynwb/__init__.py#L525-L527

Added lines #L525 - L527 were not covered by tests

open_file = h5py.File(ffspec_file, "r")
io = NWBHDF5IO(file=open_file)
nwbfile = io.read()

Check warning on line 531 in src/pynwb/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/pynwb/__init__.py#L529-L531

Added lines #L529 - L531 were not covered by tests
else:
io = NWBHDF5IO(path=path, file=file, mode="r", load_namespaces=True)
nwbfile = io.read()

return nwbfile

from . import io as __io # noqa: F401,E402
from .core import NWBContainer, NWBData # noqa: F401,E402
Expand Down
42 changes: 42 additions & 0 deletions tests/integration/hdf5/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
from pynwb.testing.mock.file import mock_NWBFile


import unittest
try:
import fsspec # noqa f401
HAVE_FSSPEC = True
except ImportError:
HAVE_FSSPEC = False

class TestHDF5Writer(TestCase):

_required_tests = ('test_nwbio', 'test_write_clobber', 'test_write_cache_spec', 'test_write_no_cache_spec')
Expand Down Expand Up @@ -586,3 +593,38 @@ def test_can_read_file_old_version(self):
def test_can_read_file_invalid_hdf5_file(self):
# current file is not an HDF5 file
self.assertFalse(NWBHDF5IO.can_read(__file__))

def test_read_nwb_method_path(self):

# write the example file
with NWBHDF5IO(self.path, 'w') as io:
io.write(self.nwbfile)

# test that the read_nwb method works
read_nwbfile = NWBHDF5IO.read_nwb(path=self.path)
self.assertContainerEqual(read_nwbfile, self.nwbfile)

read_nwbfile.get_read_io().close()

def test_read_nwb_method_file(self):

# write the example file
with NWBHDF5IO(self.path, 'w') as io:
io.write(self.nwbfile)

import h5py

file = h5py.File(self.path, 'r')

read_nwbfile = NWBHDF5IO.read_nwb(file=file)
self.assertContainerEqual(read_nwbfile, self.nwbfile)

read_nwbfile.get_read_io().close()

@unittest.skipIf(not HAVE_FSSPEC, "fsspec library not available")
oruebel marked this conversation as resolved.
Show resolved Hide resolved
def test_read_nwb_method_s3_path():
rly marked this conversation as resolved.
Show resolved Hide resolved
s3_test_path = "https://dandiarchive.s3.amazonaws.com/blobs/11e/c89/11ec8933-1456-4942-922b-94e5878bb991"
read_nwbfile = NWBHDF5IO.read_nwb(path=s3_test_path)
assert read_nwbfile.identifier == "3f77c586-6139-4777-a05d-f603e90b1330"

assert read_nwbfile.subject.subject_id == "1"
Loading