From 4a0cd7d54c4905be6da3fb7b3ff1cad67fc98d90 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Thu, 14 May 2026 16:26:19 -0700 Subject: [PATCH 1/3] fix: handle H5Dataset missing fillvalue attribute with dtype-based fallback Some h5py Dataset objects do not expose a fillvalue attribute. Guard against AttributeError in _get_fill_value and fall back to np.ma.default_fill_value for the dataset's dtype. Adds a unit test covering this path. Co-Authored-By: Claude Sonnet 4.6 --- virtualizarr/parsers/hdf/hdf.py | 5 ++++- .../tests/test_parsers/test_hdf/test_hdf.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/virtualizarr/parsers/hdf/hdf.py b/virtualizarr/parsers/hdf/hdf.py index aa2051b4..2ee7456e 100644 --- a/virtualizarr/parsers/hdf/hdf.py +++ b/virtualizarr/parsers/hdf/hdf.py @@ -40,7 +40,10 @@ def _get_fill_value(dataset: H5Dataset): Extract the fill value from an h5py dataset, handling string/bytes dtypes that don't return numpy scalars from dataset.fillvalue. """ - raw = dataset.fillvalue + try: + raw = dataset.fillvalue + except AttributeError: + return np.ma.default_fill_value(dataset.dtype) if isinstance(raw, bytes): return raw.decode("utf-8", errors="replace") elif isinstance(raw, str): diff --git a/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py b/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py index 9d59224d..d3fcf6aa 100644 --- a/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py +++ b/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py @@ -240,3 +240,20 @@ def test_netcdf_over_https(): ): np.testing.assert_allclose(ds["z"].min().to_numpy(), -6) np.testing.assert_allclose(ds["z"].max().to_numpy(), 817) + + +def test_missing_fillvalue_attribute(): + from virtualizarr.parsers.hdf.hdf import _get_fill_value + + dtype = np.dtype("float32") + + class _NoFillValueDataset: + @property + def fillvalue(self): + raise AttributeError("no fillvalue") + + dataset = _NoFillValueDataset() + dataset.dtype = dtype # type: ignore[attr-defined] + + result = _get_fill_value(dataset) + assert result == np.ma.default_fill_value(dtype) From 62230e1015635b0665763456f1d14b65f0ec3575 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Thu, 14 May 2026 16:36:30 -0700 Subject: [PATCH 2/3] Add release note --- docs/releases.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/releases.md b/docs/releases.md index 0f1ce158..022a76aa 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,5 +1,11 @@ # Release notes +## Unreleased + +### Bug Fixes + +HDFParser now includes a `_get_fill_value` function which wraps `dataset.fillvalue` in a try/except AttributeError. + ## v2.6.1 (3rd May 2026) Adds end-to-end support for inlined chunk references in `ChunkManifest` (read via Kerchunk parsers, write via Kerchunk and Icechunk writers), plus Zarr-Python 3.2.0 compatibility and several bug fixes. From 492daeb9a05aeecd6b8489653705f9842a629f78 Mon Sep 17 00:00:00 2001 From: Aimee Barciauskas Date: Thu, 14 May 2026 16:47:28 -0700 Subject: [PATCH 3/3] fix: catch RuntimeError (not AttributeError) from h5py fillvalue h5py.Dataset.fillvalue delegates to the HDF5 C library via Cython and raises RuntimeError for unsupported dtypes (e.g. variable-length strings, compound types). AttributeError can never fire on a real h5py.Dataset since fillvalue is always a defined property. Co-Authored-By: Claude Sonnet 4.6 --- virtualizarr/parsers/hdf/hdf.py | 2 +- virtualizarr/tests/test_parsers/test_hdf/test_hdf.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/virtualizarr/parsers/hdf/hdf.py b/virtualizarr/parsers/hdf/hdf.py index 2ee7456e..d21c5f7e 100644 --- a/virtualizarr/parsers/hdf/hdf.py +++ b/virtualizarr/parsers/hdf/hdf.py @@ -42,7 +42,7 @@ def _get_fill_value(dataset: H5Dataset): """ try: raw = dataset.fillvalue - except AttributeError: + except RuntimeError: return np.ma.default_fill_value(dataset.dtype) if isinstance(raw, bytes): return raw.decode("utf-8", errors="replace") diff --git a/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py b/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py index d3fcf6aa..fac802a6 100644 --- a/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py +++ b/virtualizarr/tests/test_parsers/test_hdf/test_hdf.py @@ -242,17 +242,17 @@ def test_netcdf_over_https(): np.testing.assert_allclose(ds["z"].max().to_numpy(), 817) -def test_missing_fillvalue_attribute(): +def test_fillvalue_runtime_error(): from virtualizarr.parsers.hdf.hdf import _get_fill_value dtype = np.dtype("float32") - class _NoFillValueDataset: + class _RuntimeErrorDataset: @property def fillvalue(self): - raise AttributeError("no fillvalue") + raise RuntimeError("Unable to get fill value") - dataset = _NoFillValueDataset() + dataset = _RuntimeErrorDataset() dataset.dtype = dtype # type: ignore[attr-defined] result = _get_fill_value(dataset)