Skip to content

Commit

Permalink
can_read method for NWBHDF5IO respects NWB version (#1703)
Browse files Browse the repository at this point in the history
Co-authored-by: Ryan Ly <[email protected]>
  • Loading branch information
bendichter and rly authored Oct 4, 2023
1 parent 734a6c4 commit 2aceed0
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 17 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# PyNWB Changelog

## PyNWB 2.6.0 (Upcoming)

### Enhancements and minor changes
- Add `NWBHDF5IO.can_read()`. @bendichter [#1703](https://github.com/NeurodataWithoutBorders/pynwb/pull/1703)
- Add `pynwb.get_nwbfile_version()`. @bendichter [#1703](https://github.com/NeurodataWithoutBorders/pynwb/pull/1703)

## PyNWB 2.5.0 (August 18, 2023)

### Enhancements and minor changes
Expand Down
56 changes: 39 additions & 17 deletions src/pynwb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,33 @@ def _dec(cls):
_dec(container_cls)


def get_nwbfile_version(h5py_file: h5py.File):
"""
Get the NWB version of the file if it is an NWB file.
:returns: Tuple consisting of: 1) the original version string as stored in the file and
2) a tuple with the parsed components of the version string, consisting of integers
and strings, e.g., (2, 5, 1, beta). (None, None) will be returned if the file is not a valid NWB file
or the nwb_version is missing, e.g., in the case when no data has been written to the file yet.
"""
# Get the version string for the NWB file
try:
nwb_version_string = h5py_file.attrs['nwb_version']
# KeyError occurs when the file is empty (e.g., when creating a new file nothing has been written)
# or when the HDF5 file is not a valid NWB file
except KeyError:
return None, None
# Other system may have written nwb_version as a fixed-length string, resulting in a numpy.bytes_ object
# on read, rather than a variable-length string. To address this, decode the bytes if necessary.
if not isinstance(nwb_version_string, str):
nwb_version_string = nwb_version_string.decode()

# Parse the version string
nwb_version_parts = nwb_version_string.replace("-", ".").replace("_", ".").split(".")
nwb_version = tuple([int(i) if i.isnumeric() else i
for i in nwb_version_parts])
return nwb_version_string, nwb_version


# a function to register an object mapper for a container class
@docval({"name": "container_cls", "type": type,
"doc": "the Container class for which the given ObjectMapper class gets used"},
Expand Down Expand Up @@ -201,6 +228,17 @@ def get_sum(self, a, b):

class NWBHDF5IO(_HDF5IO):

@staticmethod
def can_read(path: str):
"""Determine whether a given path is readable by this class"""
if not os.path.isfile(path): # path is file that exists
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
except IOError:
return False

@docval({'name': 'path', 'type': (str, Path), 'doc': 'the path to the HDF5 file', 'default': None},
{'name': 'mode', 'type': str,
'doc': 'the mode to open the HDF5 file with, one of ("w", "r", "r+", "a", "w-", "x")',
Expand Down Expand Up @@ -263,23 +301,7 @@ def nwb_version(self):
and strings, e.g., (2, 5, 1, beta). (None, None) will be returned if the nwb_version
is missing, e.g., in the case when no data has been written to the file yet.
"""
# Get the version string for the NWB file
try:
nwb_version_string = self._file.attrs['nwb_version']
# KeyError occurs when the file is empty (e.g., when creating a new file nothing has been written)
# or when the HDF5 file is not a valid NWB file
except KeyError:
return None, None
# Other system may have written nwb_version as a fixed-length string, resulting in a numpy.bytes_ object
# on read, rather than a variable-length string. To address this, decode the bytes if necessary.
if not isinstance(nwb_version_string, str):
nwb_version_string = nwb_version_string.decode()

# Parse the version string
nwb_version_parts = nwb_version_string.replace("-", ".").replace("_", ".").split(".")
nwb_version = tuple([int(i) if i.isnumeric() else i
for i in nwb_version_parts])
return nwb_version_string, nwb_version
return get_nwbfile_version(self._file)

@docval(*get_docval(_HDF5IO.read),
{'name': 'skip_version_check', 'type': bool, 'doc': 'skip checking of NWB version', 'default': False})
Expand Down

0 comments on commit 2aceed0

Please sign in to comment.