fix(py): a panic in the library raises clawhdf5.InternalError, not PanicException
PanicException derives from BaseException, so `except Exception` let a library bug through. Every call from the bindings into the library now runs under catch_unwind and a panic becomes InternalError (RuntimeError) naming the object. Tests: a hidden hook panics inside the guard; and the v4 chunk indexes are compared with h5py from Python — with the library fix reverted, ds[0:30] of the implicit-index dataset now raises InternalError instead of aborting the test run. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -467,3 +467,47 @@ def test_threads_read_the_same_file(pair):
|
||||
with ThreadPoolExecutor(8) as pool:
|
||||
list(pool.map(work, range(8)))
|
||||
assert not errors, errors[:3]
|
||||
|
||||
|
||||
def _v4_index_fixture(h5py, path):
|
||||
"""One 2-D dataset per v4 chunk index (HDF5 1.10+ layout, libver='latest')."""
|
||||
data = (np.arange(37 * 23, dtype="<i4") * 7 - 1000).reshape(37, 23)
|
||||
early = h5py.h5p.create(h5py.h5p.DATASET_CREATE)
|
||||
early.set_alloc_time(h5py.h5d.ALLOC_TIME_EARLY)
|
||||
with h5py.File(path, "w", libver="latest") as f:
|
||||
f.create_dataset("implicit", data=data, chunks=(5, 4), dcpl=early)
|
||||
f.create_dataset("fixed_array", data=data, chunks=(5, 4), compression="gzip")
|
||||
f.create_dataset("extensible_array", data=data, chunks=(5, 4), maxshape=(None, 23), compression="gzip")
|
||||
f.create_dataset("btree2", data=data, chunks=(5, 4), maxshape=(None, None), compression="gzip")
|
||||
f.create_dataset("single_chunk", data=data, chunks=(37, 23), compression="gzip")
|
||||
|
||||
|
||||
V4_KEYS = [
|
||||
slice(0, 3), slice(0, 30), (slice(7, 16), slice(3, 9)), (36, 22), (slice(None, None, 3), slice(1, None, 4)),
|
||||
(slice(1, None, 2), Ellipsis), (Ellipsis, slice(2, 22)), [0, 5, 6, 36], (slice(None), [0, 3, 22]), -1, (),
|
||||
]
|
||||
|
||||
|
||||
def test_every_v4_chunk_index_matches_h5py(h5py, tmp_path):
|
||||
"""Partial reads of each v4 chunk index. The implicit index (early
|
||||
allocation, no filters) used to panic in the library for any selection
|
||||
covering more than half the dataset, e.g. ds[0:30]."""
|
||||
path = str(tmp_path / "v4.h5")
|
||||
_v4_index_fixture(h5py, path)
|
||||
with h5py.File(path, "r") as theirs, clawhdf5.File(path, "r") as ours:
|
||||
for name in theirs:
|
||||
for key in V4_KEYS:
|
||||
assert_same(ours[name][key], theirs[name][key], f"{name}[{key!r}]")
|
||||
|
||||
|
||||
def test_a_library_panic_is_an_ordinary_exception():
|
||||
"""PyO3 turns a Rust panic into PanicException, a BaseException that
|
||||
`except Exception` does not catch. Every call into the library is
|
||||
guarded, so a panic surfaces as clawhdf5.InternalError instead."""
|
||||
assert issubclass(clawhdf5.InternalError, RuntimeError)
|
||||
with pytest.raises(clawhdf5.InternalError, match="deliberate panic"):
|
||||
clawhdf5._panic_for_test()
|
||||
try:
|
||||
clawhdf5._panic_for_test()
|
||||
except Exception: # noqa: BLE001 - the point of the test
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user