diff --git a/.github/workflows/test-cpu.yml b/.github/workflows/test-cpu.yml index 5262e3493..25453894d 100644 --- a/.github/workflows/test-cpu.yml +++ b/.github/workflows/test-cpu.yml @@ -38,7 +38,7 @@ jobs: ENVS_JSON=$(NO_COLOR=1 uvx hatch env show --json | jq -c 'to_entries | map( select(.key | startswith("hatch-test")) - | { name: .key, python: .value.python } + | { name: .key, python: .value.python, args: (.value."extra-args" // [] | join(" ")) } )') echo "envs=${ENVS_JSON}" | tee $GITHUB_OUTPUT test: @@ -71,7 +71,7 @@ jobs: run: uvx hatch -v env create ${{ matrix.env.name }} - name: Run tests - run: uvx hatch run ${{ matrix.env.name }}:run-cov -v --color=yes -n auto --cov --cov-report=xml --junitxml=test-data/test-results.xml -m "${{matrix.io_mark}}" + run: uvx hatch run ${{ matrix.env.name }}:run-cov -v --color=yes -n auto --cov --cov-report=xml --junitxml=test-data/test-results.xml -m "${{ matrix.io_mark }}" ${{ matrix.env.args }} - name: Upload coverage data uses: codecov/codecov-action@v5 diff --git a/hatch.toml b/hatch.toml index a856fa81e..34d03fca1 100644 --- a/hatch.toml +++ b/hatch.toml @@ -36,6 +36,7 @@ overrides.matrix.deps.python = [ overrides.matrix.deps.features = [ { if = [ "stable", "pre" ], value = "test" }, ] +overrides.matrix.deps.extra-args = { if = [ "stable", "pre" ], value = [ "--strict-warnings" ] } [[envs.hatch-test.matrix]] deps = [ "stable", "pre", "min" ] diff --git a/pyproject.toml b/pyproject.toml index 02df77780..e479e266c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -146,6 +146,7 @@ addopts = [ filterwarnings = [ "ignore::anndata._warnings.OldFormatWarning", "ignore::anndata._warnings.ExperimentalFeatureWarning", + "ignore:.*first_column_names:FutureWarning:scanpy", # scanpy 1.10.x ] # When `--strict-warnings` is used, all warnings are treated as errors, except those: filterwarnings_when_strict = [ @@ -158,6 +159,8 @@ filterwarnings_when_strict = [ "default:The codec `vlen-utf8:UserWarning", "default:The dtype `StringDType():UserWarning", "default:Consolidated metadata is:UserWarning", + "default:.*Structured:zarr.core.dtype.common.UnstableSpecificationWarning", + "default:.*FixedLengthUTF32:zarr.core.dtype.common.UnstableSpecificationWarning", ] python_files = "test_*.py" testpaths = [ diff --git a/src/anndata/_io/read.py b/src/anndata/_io/read.py index 52ac6573d..3a06542c3 100644 --- a/src/anndata/_io/read.py +++ b/src/anndata/_io/read.py @@ -48,7 +48,9 @@ def read_csv( dtype Numpy data type. """ - return read_text(filename, delimiter, first_column_names, dtype) + return read_text( + filename, delimiter, first_column_names=first_column_names, dtype=dtype + ) def read_excel( @@ -360,18 +362,26 @@ def read_text( Numpy data type. """ if not isinstance(filename, PathLike | str | bytes): - return _read_text(filename, delimiter, first_column_names, dtype) + return _read_text( + filename, delimiter, first_column_names=first_column_names, dtype=dtype + ) filename = Path(filename) if filename.suffix == ".gz": with gzip.open(str(filename), mode="rt") as f: - return _read_text(f, delimiter, first_column_names, dtype) + return _read_text( + f, delimiter, first_column_names=first_column_names, dtype=dtype + ) elif filename.suffix == ".bz2": with bz2.open(str(filename), mode="rt") as f: - return _read_text(f, delimiter, first_column_names, dtype) + return _read_text( + f, delimiter, first_column_names=first_column_names, dtype=dtype + ) else: with filename.open() as f: - return _read_text(f, delimiter, first_column_names, dtype) + return _read_text( + f, delimiter, first_column_names=first_column_names, dtype=dtype + ) def _iter_lines(file_like: Iterable[str]) -> Generator[str, None, None]: @@ -385,7 +395,8 @@ def _iter_lines(file_like: Iterable[str]) -> Generator[str, None, None]: def _read_text( # noqa: PLR0912, PLR0915 f: Iterator[str], delimiter: str | None, - first_column_names: bool | None, # noqa: FBT001 + *, + first_column_names: bool | None, dtype: str, ) -> AnnData: comments = [] diff --git a/src/anndata/_io/specs/methods.py b/src/anndata/_io/specs/methods.py index 97d1a8640..9def0ee7a 100644 --- a/src/anndata/_io/specs/methods.py +++ b/src/anndata/_io/specs/methods.py @@ -695,12 +695,11 @@ def write_recarray_zarr( from anndata.compat import _to_fixed_length_strings elem = _to_fixed_length_strings(elem) - if isinstance(f, H5Group) or is_zarr_v2(): + if is_zarr_v2(): f.create_dataset(k, data=elem, shape=elem.shape, **dataset_kwargs) else: dataset_kwargs = dataset_kwargs.copy() dataset_kwargs = zarr_v3_compressor_compat(dataset_kwargs) - # TODO: zarr’s on-disk format v3 doesn’t support this dtype f.create_array(k, shape=elem.shape, dtype=elem.dtype, **dataset_kwargs) f[k][...] = elem diff --git a/tests/test_readwrite.py b/tests/test_readwrite.py index dd41e994a..d3d2d46d3 100644 --- a/tests/test_readwrite.py +++ b/tests/test_readwrite.py @@ -844,7 +844,10 @@ def test_scanpy_krumsiek11(tmp_path, diskfmt, roundtrip): import scanpy as sc # TODO: this should be fixed in scanpy instead - with pytest.warns(UserWarning, match=r"Observation names are not unique"): + with pytest.warns(UserWarning, match=r"Observation names are not unique"): # noqa: PT031 + warnings.filterwarnings( + "ignore", r".*first_column_names.*no longer positional", FutureWarning + ) orig = sc.datasets.krumsiek11() del orig.uns["highlights"] # Can’t write int keys # Can’t write "string" dtype: https://github.com/scverse/anndata/issues/679 diff --git a/tests/test_structured_arrays.py b/tests/test_structured_arrays.py index 3787d38c8..ef79716ac 100644 --- a/tests/test_structured_arrays.py +++ b/tests/test_structured_arrays.py @@ -1,5 +1,6 @@ from __future__ import annotations +import warnings from itertools import combinations, product from typing import TYPE_CHECKING @@ -7,6 +8,7 @@ import anndata as ad from anndata import AnnData +from anndata.compat import is_zarr_v2 from anndata.tests.helpers import gen_vstr_recarray if TYPE_CHECKING: @@ -27,7 +29,14 @@ def assert_str_contents_equal(A, B): def test_io( tmp_path, diskfmt: Literal["zarr", "h5ad"], diskfmt2: Literal["zarr", "h5ad"] -): +) -> None: + if not is_zarr_v2(): + from zarr.core.dtype.common import UnstableSpecificationWarning + + warnings.filterwarnings( # raised by “S10” dtype in the recarray below + "default", r".*NullTerminatedBytes", UnstableSpecificationWarning + ) + read1 = lambda pth: getattr(ad, f"read_{diskfmt}")(pth) write1 = lambda adata, pth: getattr(adata, f"write_{diskfmt}")(pth) read2 = lambda pth: getattr(ad, f"read_{diskfmt2}")(pth)