From deda913c620979fe697c6c1390263989a129d68f Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Mon, 28 Jul 2025 13:52:56 +0200 Subject: [PATCH 1/9] (fix): skip X writing if X is None --- src/anndata/_io/h5ad.py | 8 +++++--- src/anndata/_io/specs/methods.py | 3 ++- tests/test_readwrite.py | 30 ++++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/anndata/_io/h5ad.py b/src/anndata/_io/h5ad.py index ec27a3083..754acc78a 100644 --- a/src/anndata/_io/h5ad.py +++ b/src/anndata/_io/h5ad.py @@ -4,7 +4,7 @@ from functools import partial from pathlib import Path from types import MappingProxyType -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING, TypeVar, cast from warnings import warn import h5py @@ -82,7 +82,7 @@ def write_h5ad( # TODO: Use spec writing system for this # Currently can't use write_dispatched here because this function is also called to do an # inplace update of a backed object, which would delete "/" - f = f["/"] + f = cast("h5py.Group", f["/"]) f.attrs.setdefault("encoding-type", "anndata") f.attrs.setdefault("encoding-version", "0.1.0") @@ -90,7 +90,9 @@ def write_h5ad( adata.X, CSMatrix | BaseCompressedSparseDataset ): write_sparse_as_dense(f, "X", adata.X, dataset_kwargs=dataset_kwargs) - elif not (adata.isbacked and Path(adata.filename) == Path(filepath)): + elif adata.X is not None and not ( + adata.isbacked and Path(adata.filename) == Path(filepath) + ): # If adata.isbacked, X should already be up to date write_elem(f, "X", adata.X, dataset_kwargs=dataset_kwargs) if "raw/X" in as_dense and isinstance( diff --git a/src/anndata/_io/specs/methods.py b/src/anndata/_io/specs/methods.py index 97d1a8640..5dd3a07e8 100644 --- a/src/anndata/_io/specs/methods.py +++ b/src/anndata/_io/specs/methods.py @@ -275,7 +275,8 @@ def write_anndata( dataset_kwargs: Mapping[str, Any] = MappingProxyType({}), ): g = f.require_group(k) - _writer.write_elem(g, "X", adata.X, dataset_kwargs=dataset_kwargs) + if adata.X is not None: + _writer.write_elem(g, "X", adata.X, dataset_kwargs=dataset_kwargs) _writer.write_elem(g, "obs", adata.obs, dataset_kwargs=dataset_kwargs) _writer.write_elem(g, "var", adata.var, dataset_kwargs=dataset_kwargs) _writer.write_elem(g, "obsm", dict(adata.obsm), dataset_kwargs=dataset_kwargs) diff --git a/tests/test_readwrite.py b/tests/test_readwrite.py index dd41e994a..3731d3267 100644 --- a/tests/test_readwrite.py +++ b/tests/test_readwrite.py @@ -2,7 +2,7 @@ import re import warnings -from contextlib import contextmanager +from contextlib import contextmanager, nullcontext from functools import partial from importlib.util import find_spec from pathlib import Path @@ -38,6 +38,7 @@ ) if TYPE_CHECKING: + from collections.abc import Generator from typing import Literal HERE = Path(__file__).parent @@ -100,6 +101,15 @@ def rw(backing_h5ad): return curr, orig +@contextmanager +def open_store( + path: Path, diskfmt: Literal["h5ad", "zarr"] +) -> Generator[h5py.File | zarr.Group, None, None]: + f = zarr.open_group(path) if diskfmt == "zarr" else h5py.File(path, "r") + with f if isinstance(f, h5py.File) else nullcontext(): + yield f + + @pytest.fixture(params=[np.uint8, np.int32, np.int64, np.float32, np.float64]) def dtype(request): return request.param @@ -403,10 +413,10 @@ def check_compressed(value, key): not_compressed.append(key) if is_zarr_v2(): - with zarr.open(str(pth), "r") as f: + with zarr.open(pth, "r") as f: f.visititems(check_compressed) else: - f = zarr.open(str(pth), mode="r") + f = zarr.open(pth, mode="r") for key, value in f.members(max_depth=None): check_compressed(value, key) @@ -765,12 +775,24 @@ def test_zarr_chunk_X(tmp_path): adata = gen_adata((100, 100), X_type=np.array, **GEN_ADATA_NO_XARRAY_ARGS) adata.write_zarr(zarr_pth, chunks=(10, 10)) - z = zarr.open(str(zarr_pth)) # As of v2.3.2 zarr won’t take a Path + z = zarr.open(zarr_pth) assert z["X"].chunks == (10, 10) from_zarr = ad.read_zarr(zarr_pth) assert_equal(from_zarr, adata) +def test_write_x_none(tmp_path: Path, diskfmt: Literal["h5ad", "zarr"]) -> None: + adata = ad.AnnData(shape=(10, 10), obs={"a": np.ones(10)}, var={"b": np.ones(10)}) + p = tmp_path / f"adata.{diskfmt}" + write = getattr(adata, f"write_{diskfmt}") + + write(p) + with open_store(p, diskfmt) as f: + root_keys = list(f.keys()) + + assert "X" not in root_keys + + ################################ # Round-tripping scanpy datasets ################################ From 0c1f663d2df1bde5b3f7e2f5679f0c0b69a6669c Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Mon, 28 Jul 2025 13:56:22 +0200 Subject: [PATCH 2/9] relnote --- docs/release-notes/2054.bugfix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/release-notes/2054.bugfix.md diff --git a/docs/release-notes/2054.bugfix.md b/docs/release-notes/2054.bugfix.md new file mode 100644 index 000000000..62a9b9a1d --- /dev/null +++ b/docs/release-notes/2054.bugfix.md @@ -0,0 +1 @@ +Revert accidental change where {attr}`~anndata.AnnData.X` got written to disk when it was `None` {user}`flying-sheep` From 9b8b2d92a3e8dbe13242af7e851fc8bbd4de582b Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Mon, 28 Jul 2025 13:59:52 +0200 Subject: [PATCH 3/9] remove more useless path-to-string ops --- src/anndata/_io/zarr.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/anndata/_io/zarr.py b/src/anndata/_io/zarr.py index 3b9667300..dbfdc9483 100644 --- a/src/anndata/_io/zarr.py +++ b/src/anndata/_io/zarr.py @@ -1,6 +1,5 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING, TypeVar from warnings import warn @@ -37,8 +36,6 @@ def write_zarr( **ds_kwargs, ) -> None: """See :meth:`~anndata.AnnData.write_zarr`.""" - if isinstance(store, Path): - store = str(store) if convert_strings_to_categoricals: adata.strings_to_categoricals() if adata.raw is not None: @@ -75,9 +72,6 @@ def read_zarr(store: PathLike[str] | str | MutableMapping | zarr.Group) -> AnnDa store The filename, a :class:`~typing.MutableMapping`, or a Zarr storage class. """ - if isinstance(store, Path): - store = str(store) - f = store if isinstance(store, zarr.Group) else zarr.open(store, mode="r") # Read with handling for backwards compat From 3829f41652705317478e9957b61fcbc0cb5c2a2f Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Mon, 28 Jul 2025 15:40:47 +0200 Subject: [PATCH 4/9] fix backing issues --- src/anndata/_io/h5ad.py | 59 +++++++++++++++++++++++++-------------- tests/conftest.py | 3 +- tests/test_backed_hdf5.py | 2 +- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/anndata/_io/h5ad.py b/src/anndata/_io/h5ad.py index 754acc78a..4d747dae6 100644 --- a/src/anndata/_io/h5ad.py +++ b/src/anndata/_io/h5ad.py @@ -36,11 +36,13 @@ ) if TYPE_CHECKING: - from collections.abc import Callable, Collection, Mapping, Sequence + from collections.abc import Callable, Collection, Container, Mapping, Sequence from os import PathLike from typing import Any, Literal from .._core.file_backing import AnnDataFileManager + from .._core.raw import Raw + from ..typing import XDataType T = TypeVar("T") @@ -86,27 +88,10 @@ def write_h5ad( f.attrs.setdefault("encoding-type", "anndata") f.attrs.setdefault("encoding-version", "0.1.0") - if "X" in as_dense and isinstance( - adata.X, CSMatrix | BaseCompressedSparseDataset - ): - write_sparse_as_dense(f, "X", adata.X, dataset_kwargs=dataset_kwargs) - elif adata.X is not None and not ( - adata.isbacked and Path(adata.filename) == Path(filepath) - ): + if not adata.isbacked or adata.filename != filepath: # If adata.isbacked, X should already be up to date - write_elem(f, "X", adata.X, dataset_kwargs=dataset_kwargs) - if "raw/X" in as_dense and isinstance( - adata.raw.X, CSMatrix | BaseCompressedSparseDataset - ): - write_sparse_as_dense( - f, "raw/X", adata.raw.X, dataset_kwargs=dataset_kwargs - ) - write_elem(f, "raw/var", adata.raw.var, dataset_kwargs=dataset_kwargs) - write_elem( - f, "raw/varm", dict(adata.raw.varm), dataset_kwargs=dataset_kwargs - ) - elif adata.raw is not None: - write_elem(f, "raw", adata.raw, dataset_kwargs=dataset_kwargs) + _write_x(f, adata.X, as_dense=as_dense, dataset_kwargs=dataset_kwargs) + _write_raw(f, adata.raw, as_dense=as_dense, dataset_kwargs=dataset_kwargs) write_elem(f, "obs", adata.obs, dataset_kwargs=dataset_kwargs) write_elem(f, "var", adata.var, dataset_kwargs=dataset_kwargs) write_elem(f, "obsm", dict(adata.obsm), dataset_kwargs=dataset_kwargs) @@ -117,6 +102,38 @@ def write_h5ad( write_elem(f, "uns", dict(adata.uns), dataset_kwargs=dataset_kwargs) +def _write_x( + f: h5py.Group, + x: XDataType, + *, + as_dense: Container[str], + dataset_kwargs: Mapping[str, Any], +) -> None: + if "X" in as_dense and isinstance(x, CSMatrix | BaseCompressedSparseDataset): + write_sparse_as_dense(f, "X", x, dataset_kwargs=dataset_kwargs) + elif x is None: + f.pop("X", None) + else: + write_elem(f, "X", x, dataset_kwargs=dataset_kwargs) + + +def _write_raw( + f: h5py.Group, + raw: Raw, + *, + as_dense: Container[str], + dataset_kwargs: Mapping[str, Any], +) -> None: + if "raw/X" in as_dense and isinstance( + raw.X, CSMatrix | BaseCompressedSparseDataset + ): + write_sparse_as_dense(f, "raw/X", raw.X, dataset_kwargs=dataset_kwargs) + write_elem(f, "raw/var", raw.var, dataset_kwargs=dataset_kwargs) + write_elem(f, "raw/varm", dict(raw.varm), dataset_kwargs=dataset_kwargs) + elif raw is not None: + write_elem(f, "raw", raw, dataset_kwargs=dataset_kwargs) + + @report_write_key_on_error @write_spec(IOSpec("array", "0.2.0")) def write_sparse_as_dense( diff --git a/tests/conftest.py b/tests/conftest.py index 39bbb067f..8441dc940 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,11 +24,12 @@ if TYPE_CHECKING: from collections.abc import Generator + from pathlib import Path from types import EllipsisType @pytest.fixture -def backing_h5ad(tmp_path): +def backing_h5ad(tmp_path: Path) -> Path: return tmp_path / "test.h5ad" diff --git a/tests/test_backed_hdf5.py b/tests/test_backed_hdf5.py index 40f9e4bf3..3b8305dce 100644 --- a/tests/test_backed_hdf5.py +++ b/tests/test_backed_hdf5.py @@ -108,7 +108,7 @@ def test_read_write_X(tmp_path, mtx_format, backed_mode, as_dense): # this is very similar to the views test @pytest.mark.filterwarnings("ignore::anndata.ImplicitModificationWarning") -def test_backing(adata, tmp_path, backing_h5ad): +def test_backing(adata: ad.AnnData, tmp_path: Path, backing_h5ad: Path) -> None: assert not adata.isbacked adata.filename = backing_h5ad From e650bd46de1ec3dd8f18beaa44f45e5d2a8d6499 Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Mon, 28 Jul 2025 15:56:34 +0200 Subject: [PATCH 5/9] pull densifying up --- src/anndata/_io/h5ad.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/anndata/_io/h5ad.py b/src/anndata/_io/h5ad.py index 4d747dae6..db432a86c 100644 --- a/src/anndata/_io/h5ad.py +++ b/src/anndata/_io/h5ad.py @@ -88,9 +88,13 @@ def write_h5ad( f.attrs.setdefault("encoding-type", "anndata") f.attrs.setdefault("encoding-version", "0.1.0") - if not adata.isbacked or adata.filename != filepath: - # If adata.isbacked, X should already be up to date - _write_x(f, adata.X, as_dense=as_dense, dataset_kwargs=dataset_kwargs) + _write_x( + f, + adata.X, + is_backed=adata.isbacked and adata.filename == filepath, + as_dense=as_dense, + dataset_kwargs=dataset_kwargs, + ) _write_raw(f, adata.raw, as_dense=as_dense, dataset_kwargs=dataset_kwargs) write_elem(f, "obs", adata.obs, dataset_kwargs=dataset_kwargs) write_elem(f, "var", adata.var, dataset_kwargs=dataset_kwargs) @@ -106,11 +110,14 @@ def _write_x( f: h5py.Group, x: XDataType, *, + is_backed: bool, as_dense: Container[str], dataset_kwargs: Mapping[str, Any], ) -> None: if "X" in as_dense and isinstance(x, CSMatrix | BaseCompressedSparseDataset): write_sparse_as_dense(f, "X", x, dataset_kwargs=dataset_kwargs) + elif is_backed: + pass # If adata.isbacked, X should already be up to date elif x is None: f.pop("X", None) else: From eac34b91bfe81b0ff73b6b490fa62c65604d2a9b Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Mon, 28 Jul 2025 16:29:43 +0200 Subject: [PATCH 6/9] fix accidental X access --- src/anndata/_io/h5ad.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/anndata/_io/h5ad.py b/src/anndata/_io/h5ad.py index db432a86c..2dc4c1bb2 100644 --- a/src/anndata/_io/h5ad.py +++ b/src/anndata/_io/h5ad.py @@ -42,7 +42,6 @@ from .._core.file_backing import AnnDataFileManager from .._core.raw import Raw - from ..typing import XDataType T = TypeVar("T") @@ -90,7 +89,7 @@ def write_h5ad( _write_x( f, - adata.X, + adata, is_backed=adata.isbacked and adata.filename == filepath, as_dense=as_dense, dataset_kwargs=dataset_kwargs, @@ -108,20 +107,20 @@ def write_h5ad( def _write_x( f: h5py.Group, - x: XDataType, + adata: AnnData, *, is_backed: bool, as_dense: Container[str], dataset_kwargs: Mapping[str, Any], ) -> None: - if "X" in as_dense and isinstance(x, CSMatrix | BaseCompressedSparseDataset): - write_sparse_as_dense(f, "X", x, dataset_kwargs=dataset_kwargs) + if "X" in as_dense and isinstance(adata.X, CSMatrix | BaseCompressedSparseDataset): + write_sparse_as_dense(f, "X", adata.X, dataset_kwargs=dataset_kwargs) elif is_backed: pass # If adata.isbacked, X should already be up to date - elif x is None: + elif adata.X is None: f.pop("X", None) else: - write_elem(f, "X", x, dataset_kwargs=dataset_kwargs) + write_elem(f, "X", adata.X, dataset_kwargs=dataset_kwargs) def _write_raw( From dc4b3626c9cd59348589820a92b2dec16e975051 Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Mon, 28 Jul 2025 17:42:43 +0200 Subject: [PATCH 7/9] add backwards compat test --- src/anndata/_io/h5ad.py | 2 +- tests/data/archives/v0.11.4/adata.h5ad | Bin 0 -> 16408 bytes tests/data/archives/v0.11.4/adata.zarr.zip | Bin 0 -> 6539 bytes tests/data/archives/v0.11.4/readme.md | 10 ++++++ tests/test_io_backwards_compat.py | 39 ++++++++++++++++----- 5 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 tests/data/archives/v0.11.4/adata.h5ad create mode 100644 tests/data/archives/v0.11.4/adata.zarr.zip create mode 100644 tests/data/archives/v0.11.4/readme.md diff --git a/src/anndata/_io/h5ad.py b/src/anndata/_io/h5ad.py index 2dc4c1bb2..6c33e1ba9 100644 --- a/src/anndata/_io/h5ad.py +++ b/src/anndata/_io/h5ad.py @@ -89,7 +89,7 @@ def write_h5ad( _write_x( f, - adata, + adata, # accessing adata.X reopens adata.file if it’s backed is_backed=adata.isbacked and adata.filename == filepath, as_dense=as_dense, dataset_kwargs=dataset_kwargs, diff --git a/tests/data/archives/v0.11.4/adata.h5ad b/tests/data/archives/v0.11.4/adata.h5ad new file mode 100644 index 0000000000000000000000000000000000000000..1b5f4a21351c1496ac7e52d85c692f345c9da414 GIT binary patch literal 16408 zcmeHOO>-MX5S_IXA`&~;!GIjT6iC2)*x129s8B={G3Y=M7jV!G2}^*hVkxCKDSY!W z+&KE^BS-&)J@ONL)ISN+Gw(^Gjy9@T+NxA}_MqPG>6x8<({FaCcl3AV_II=AFP}GZ z6bj~ynRBQ7d5oi2_7>_aui+$td<6OLls`#qi&9pEeGcuHroADB{gwJ!Wu|DnWHv2(4Bl98 z&0J9a7*;c*>}$|xm7Vxsgq`*7?{?&AQ15i=ds6fg%6b2*E7u)tetcwLb>@vbJNuU)OdF zIOx%f!!-=}oABp&GtNMxUi>l6-d6sMvy!qi&aNmsY~FL& zF=NWkEcr;umvX&2Dxc}rw^;1UB_tQ*u7JxVNMt6sE?>hbr){~oW?knj+MAm4%@)w! zbYe=b!{*Qn{PuAD&J+i*XUGa&v2;9u=st<*bv+KJDP2`c_L;zImsm15Zw2;`A} zmz)p3AL#t!xN+px@fdW;A?4A3$vtI#Kj3!_)2b^E4NN}aNPM?3z&-3k)?OPr^Ucx;O6AovPQgYj&F3+k^mt1+M%N!Ky z^2`l&$)Sb1ZiTw!L___W(yuCAat=Ap&06tD-QLN z(yv(E<-38Oc6AyambRDUk-I~8|&(tT9ndRo4X@_p|geCQo_J#CiQ z(0j_)XzLs&vJ6!k5k}{9?Wmzg@?OQ#@2ksPL_;s(z+_wt1C{8H?dfkhqg4yRr z3E;v>adSdAk$+{ipKVk2wEdEuo0VD-Zk$(UpilGQ(@y~E05o+bd zKfPablKhh?BoF?{Uac(t9b@BE3fv<907Mi+5y;}-KH`X#^5S0^8w0sddy@Q@((yfF&u#`{_P`HkMfl7F(dJ`euMt3ek3s<%91sTYxdz#)p^2xRfk ZAdXloFaGKO|4ovA@@UP2fAU|K{{gb^{2>4U literal 0 HcmV?d00001 diff --git a/tests/data/archives/v0.11.4/adata.zarr.zip b/tests/data/archives/v0.11.4/adata.zarr.zip new file mode 100644 index 0000000000000000000000000000000000000000..b0347956906ee12e9e923ae8a48fae5b2e353020 GIT binary patch literal 6539 zcmeHLO^6&-5boYhOx&mmaSsv$S|l zSedC#?XD@{%A3>I3;>~oU^56jpMx!@=GL3eg;KE7wg7IJj#D=S)1YR)y?ABy446R4 zVdg{_h^@4d>uZZw*ez*S4HeCul4i1NyrP`zU+HnaH8PWwH_$mdS=?9F%*{ZG8F?qy*(LH8tf4 zeO_OJH5B~+6bNKcE2FBQs-l`lwSa07)e@>xsFqPZuO7^&kN$ph7M4+lOCjxy`hkZ> zmX*uU0Xp>gm%Vdf5~YAyp4j+b!O|1^^;fSS z?|@^Jhwhw)r}VVAt)V@wYkI|cT4yibK<*_O$B%J#A(t z>gc2NG_qkCJ#FQ!PxfDgl%QZwlTyQang?qr_?`2 znOW9uVPO1Hv$6qD+j8xG>w?PwhDY>Qn_C!QzU%> z70Bx-zZ)%c~%i?DMLUWAz6{VV77WyL* zFlSkuO$XAKD#7t82d0+=o%{uUWxx6!K)EKD#Vr+?ke%eRIQ^*NIRT$s7Tlw)8Xnz4D&>SP$ zeL$Ndb4I(CflQ9}2SCCO4MwBU_x8R(=8X2m#|J)Rk;&1114!7RF`~UAkg-0KD`5pD z7w$6_DMyP#PW1HkKK!XpU}?LnVsFdXSif?_Dt7*$^BtacMx-DUW Path: return request.param -def test_backwards_compat_files(archive_dir) -> None: - with pytest.warns(ad.OldFormatWarning): - from_h5ad = ad.read_h5ad(archive_dir / "adata.h5ad") - path = archive_dir / "adata.zarr.zip" - store = path if is_zarr_v2() else zarr.storage.ZipStore(path) - with pytest.warns(ad.OldFormatWarning): - from_zarr = ad.read_zarr(store) +def read_archive( + archive_dir: Path, format: Literal["h5ad", "zarr"] +) -> tuple[ad.AnnData, Path]: + if format == "h5ad": + path = archive_dir / "adata.h5ad" + return ad.read_h5ad(path), path + if format == "zarr": + path = archive_dir / "adata.zarr.zip" + store = path if is_zarr_v2() else zarr.storage.ZipStore(path) + return ad.read_zarr(store), path + pytest.fail(f"Unknown format: {format}") + +@pytest.mark.filterwarnings("ignore::anndata.OldFormatWarning") +def test_backwards_compat_files(archive_dir: Path) -> None: + from_h5ad, _ = read_archive(archive_dir, "h5ad") + from_zarr, _ = read_archive(archive_dir, "zarr") assert_equal(from_h5ad, from_zarr, exact=True) +def test_no_diff(tmp_path: Path, archive_dir: Path) -> None: + if archive_dir.name in {"v0.7.8", "v0.7.0"}: + pytest.skip("DataFrame encoding changed between 0.7 and now") + adata, in_path = read_archive(archive_dir, "h5ad") + adata.write_h5ad(out_path := tmp_path / "adata.h5ad") + diff_proc = run(["h5diff", in_path, out_path], check=False) + assert diff_proc.returncode == 0 + + def test_clean_uns_backwards_compat(tmp_path, diskfmt): pth = tmp_path / f"test_write.{diskfmt}" write = lambda x, y: getattr(x, f"write_{diskfmt}")(y) From 793a2b91b3bd7d05ad560e939c4e2dd48dbacf32 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 29 Jul 2025 09:48:15 +0200 Subject: [PATCH 8/9] install hdf5-tools --- .github/workflows/test-cpu.yml | 3 +++ .github/workflows/test-gpu.yml | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-cpu.yml b/.github/workflows/test-cpu.yml index 25453894d..4350db1d1 100644 --- a/.github/workflows/test-cpu.yml +++ b/.github/workflows/test-cpu.yml @@ -56,6 +56,9 @@ jobs: fetch-depth: 0 filter: blob:none + - name: Install system dependencies + run: sudo apt install -y hdf5-tools + - name: Set up Python ${{ matrix.env.python }} uses: actions/setup-python@v5 with: diff --git a/.github/workflows/test-gpu.yml b/.github/workflows/test-gpu.yml index 63512d0ae..32ea62a2a 100644 --- a/.github/workflows/test-gpu.yml +++ b/.github/workflows/test-gpu.yml @@ -56,9 +56,10 @@ jobs: - name: Nvidia SMI sanity check run: nvidia-smi - - name: Install yq + - name: Install system dependencies run: | sudo snap install yq + sudo apt install -y hdf5-tools - name: Extract max Python version from classifiers run: | From a3b23fb4efaf0cc2fa28169933104c5095507885 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 29 Jul 2025 10:06:07 +0200 Subject: [PATCH 9/9] revert GPU changes --- .github/workflows/test-gpu.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-gpu.yml b/.github/workflows/test-gpu.yml index 32ea62a2a..63512d0ae 100644 --- a/.github/workflows/test-gpu.yml +++ b/.github/workflows/test-gpu.yml @@ -56,10 +56,9 @@ jobs: - name: Nvidia SMI sanity check run: nvidia-smi - - name: Install system dependencies + - name: Install yq run: | sudo snap install yq - sudo apt install -y hdf5-tools - name: Extract max Python version from classifiers run: |