Skip to content

Commit 262f3bc

Browse files
authored
Fix check for swmr compatibility in h5 files (#368)
* Check compatability via super block version Previous check of SWMR compatibility was trying to open the file with old HDF5 library versions, to see if an error was raised. With HDF5>2.0 this no longer works because a fix was implemented to only raise an error when trying to create or write new objects that are incompatible rather than when opening the file. * Add test for swmr compatability function
1 parent 7adfe02 commit 262f3bc

3 files changed

Lines changed: 27 additions & 14 deletions

File tree

src/dlstbx/services/validation.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def validate(self, rw, header, message):
8686

8787
fail = functools.partial(self.fail_validation, rw, header, output)
8888

89-
# Verify HDF5 file version is _not_ compatible with HDF5 1.8 format
89+
# Verify HDF5 file version is SWMR compatible (doesn't require SWMR mode to be enabled)
9090
if filename.endswith((".h5", ".nxs")):
9191
if not hdf5_util.is_readable(filename):
9292
return fail(f"{filename} is an invalid HDF5 file")
@@ -106,17 +106,16 @@ def validate(self, rw, header, message):
106106
return fail(
107107
f"HDF5 file {filename} links to invalid file(s) {', '.join(errors)}"
108108
)
109-
hdf_18 = [
109+
hdf_swmr_incompatible = [
110110
link
111111
for link, image_count in hdf5_util.find_all_references(
112112
filename
113113
).items()
114-
if image_count and hdf5_util.is_HDF_1_8_compatible(link)
114+
if image_count and not hdf5_util.is_SWMR_compatible(link)
115115
]
116-
if hdf_18:
116+
if hdf_swmr_incompatible:
117117
return fail(
118-
f"HDF5 file {filename} links to HDF5 1.8 format data in %s"
119-
% ", ".join(hdf_18)
118+
f"HDF5 file {filename} links to non-SWMR compatible file(s) {', '.join(hdf_swmr_incompatible)}"
120119
)
121120
try:
122121
hdf5_util.validate_pixel_mask(filename)

src/dlstbx/util/hdf5.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,15 @@ def is_readable(filename: str) -> bool:
104104
return False
105105

106106

107-
def is_HDF_1_8_compatible(filename: str) -> bool:
108-
"""Check if a file can be read with HDF 1.8. This excludes all SWMR formatted files."""
109-
110-
try:
111-
with h5py.File(filename, "r", libver=("earliest", "v108")):
112-
return True
113-
except OSError:
114-
return False
107+
def is_SWMR_compatible(filename: str) -> bool:
108+
"""Check if a file format is SWMR compatible by checking superblock version."""
109+
110+
with h5py.File(filename, "r") as f:
111+
fcpl = f.id.get_create_plist()
112+
# returns a tuple of (superblock_version, freelist_version, symbol_table_version, shared_header_version)
113+
versions = fcpl.get_version()
114+
superblock_version = versions[0]
115+
return superblock_version >= 3
115116

116117

117118
def validate_pixel_mask(filename: str) -> bool:

tests/util/test_hdf5.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44

5+
import h5py
56
import pytest
67

78
import dlstbx.util.hdf5
@@ -61,3 +62,15 @@ def test_validate_pixel_mask_shape_int64():
6162
dlstbx.util.hdf5.validate_pixel_mask(
6263
"/dls/mx/data/cm31104/cm31104-1/020222/RT/ToNV_WT/x_27_master.h5"
6364
)
65+
66+
67+
def test_is_SWMR_compatible(tmp_path):
68+
# Test that the function correctly identifies SWMR compatible and incompatible files
69+
swmr_compatible_file = tmp_path / "swmr_compatible.h5"
70+
with h5py.File(swmr_compatible_file.as_posix(), "w", libver=("v110", "latest")):
71+
pass
72+
assert dlstbx.util.hdf5.is_SWMR_compatible(swmr_compatible_file.as_posix())
73+
swmr_incompatible_file = tmp_path / "swmr_incompatible.h5"
74+
with h5py.File(swmr_incompatible_file.as_posix(), "w", libver=("earliest", "v108")):
75+
pass
76+
assert not dlstbx.util.hdf5.is_SWMR_compatible(swmr_incompatible_file.as_posix())

0 commit comments

Comments
 (0)