diff --git a/CHANGELOG.md b/CHANGELOG.md index c72e78f58..9762bf20d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Documentation and tutorial enhancements - Added pre-release pull request instructions to release process documentation @stephprince [#1928](https://github.com/NeurodataWithoutBorders/pynwb/pull/1928) +### Bug fixes +- Fixed `can_read` method to return False if no nwbfile version can be found @stephprince [#1934](https://github.com/NeurodataWithoutBorders/pynwb/pull/1934) + ## PyNWB 2.8.1 (July 3, 2024) ### Documentation and tutorial enhancements diff --git a/src/pynwb/__init__.py b/src/pynwb/__init__.py index 50db92dcc..278e48948 100644 --- a/src/pynwb/__init__.py +++ b/src/pynwb/__init__.py @@ -4,6 +4,7 @@ import os.path from pathlib import Path from copy import deepcopy +from warnings import warn import h5py from hdmf.spec import NamespaceCatalog @@ -269,7 +270,15 @@ def can_read(path: str): return False try: with h5py.File(path, "r") as file: # path is HDF5 file - return get_nwbfile_version(file)[1][0] >= 2 # Major version of NWB >= 2 + version_info = get_nwbfile_version(file) + if version_info[0] is None: + warn("Cannot read because missing NWB version in the HDF5 file. The file is not a valid NWB file.") + return False + elif version_info[1][0] < 2: # Major versions of NWB < 2 not supported + warn("Cannot read because PyNWB supports NWB files version 2 and above.") + return False + else: + return True except IOError: return False diff --git a/tests/integration/hdf5/test_io.py b/tests/integration/hdf5/test_io.py index d68334c89..e1ce4269b 100644 --- a/tests/integration/hdf5/test_io.py +++ b/tests/integration/hdf5/test_io.py @@ -531,3 +531,41 @@ def test_round_trip_with_pathlib_path(self): with NWBHDF5IO(pathlib_path, 'r') as io: read_file = io.read() self.assertContainerEqual(read_file, self.nwbfile) + + def test_can_read_current_nwb_file(self): + with NWBHDF5IO(self.path, 'w') as io: + io.write(self.nwbfile) + self.assertTrue(NWBHDF5IO.can_read(self.path)) + + def test_can_read_file_does_not_exits(self): + self.assertFalse(NWBHDF5IO.can_read('not_a_file.nwb')) + + def test_can_read_file_no_version(self): + # write the example file + with NWBHDF5IO(self.path, 'w') as io: + io.write(self.nwbfile) + # remove the version attribute + with File(self.path, mode='a') as io: + del io.attrs['nwb_version'] + + # assert can_read returns False and warning raised + warn_msg = "Cannot read because missing NWB version in the HDF5 file. The file is not a valid NWB file." + with self.assertWarnsWith(UserWarning, warn_msg): + self.assertFalse(NWBHDF5IO.can_read(self.path)) + + def test_can_read_file_old_version(self): + # write the example file + with NWBHDF5IO(self.path, 'w') as io: + io.write(self.nwbfile) + # set the version attribute <2.0 + with File(self.path, mode='a') as io: + io.attrs['nwb_version'] = "1.0.5" + + # assert can_read returns False and warning raised + warn_msg = "Cannot read because PyNWB supports NWB files version 2 and above." + with self.assertWarnsWith(UserWarning, warn_msg): + self.assertFalse(NWBHDF5IO.can_read(self.path)) + + def test_can_read_file_invalid_hdf5_file(self): + # current file is not an HDF5 file + self.assertFalse(NWBHDF5IO.can_read(__file__))