From 5d9c13d69c3b0241fa69021869f0745df524baee Mon Sep 17 00:00:00 2001 From: Philipp A Date: Thu, 25 Sep 2025 13:23:35 +0200 Subject: [PATCH] Backport PR #1318: fix: use standard version API --- .github/workflows/test-cpu.yml | 5 -- docs/release-notes/1318.fix.md | 1 + pyproject.toml | 6 +- src/anndata/__init__.py | 41 ++++++------ src/anndata/_core/merge.py | 14 +---- src/anndata/_core/sparse_dataset.py | 4 +- src/anndata/_io/h5ad.py | 29 ++++----- src/anndata/_io/specs/methods.py | 12 ++-- src/anndata/_io/utils.py | 9 +-- src/anndata/_version.py | 62 ------------------- src/anndata/compat/__init__.py | 14 +---- src/anndata/experimental/merge.py | 4 +- .../multi_files/_anncollection.py | 4 +- src/testing/anndata/_pytest.py | 8 +-- tests/conftest.py | 4 +- tests/test_concatenate.py | 4 +- tests/test_io_elementwise.py | 5 +- tests/test_io_partial.py | 7 +-- tests/test_io_warnings.py | 17 +---- tests/test_readwrite.py | 41 +++++------- tests/test_views.py | 21 ++----- 21 files changed, 85 insertions(+), 227 deletions(-) create mode 100644 docs/release-notes/1318.fix.md delete mode 100644 src/anndata/_version.py diff --git a/.github/workflows/test-cpu.yml b/.github/workflows/test-cpu.yml index 256f953aa..9965414f1 100644 --- a/.github/workflows/test-cpu.yml +++ b/.github/workflows/test-cpu.yml @@ -120,11 +120,6 @@ jobs: python -m build --sdist --wheel . twine check dist/* - - name: Check runtime version - run: | - pip install dist/*.whl - python -c 'import anndata; print(anndata.__version__)' - check: if: always() needs: diff --git a/docs/release-notes/1318.fix.md b/docs/release-notes/1318.fix.md new file mode 100644 index 000000000..9f128a008 --- /dev/null +++ b/docs/release-notes/1318.fix.md @@ -0,0 +1 @@ +Deprecate `__version__` and use standard {func}`~importlib.metadata.version` API {user}`flying-sheep` diff --git a/pyproject.toml b/pyproject.toml index 03f7bece5..4c1b7a79a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,8 +57,6 @@ Home-page = "https://github.com/scverse/anndata" [project.optional-dependencies] dev = [ - # runtime dev version generation - "hatch-vcs", "anndata[dev-doc]", ] doc = [ @@ -146,7 +144,9 @@ addopts = [ filterwarnings = [ "ignore::anndata._warnings.OldFormatWarning", "ignore::anndata._warnings.ExperimentalFeatureWarning", - "ignore:.*first_column_names:FutureWarning:scanpy", # scanpy 1.10.x + "ignore:.*first_column_names:FutureWarning:scanpy", # scanpy 1.10.x + "ignore:Importing read_.* from `anndata` is deprecated:FutureWarning:scanpy", + "ignore:`__version__` is deprecated:FutureWarning:scanpy", ] # When `--strict-warnings` is used, all warnings are treated as errors, except those: filterwarnings_when_strict = [ diff --git a/src/anndata/__init__.py b/src/anndata/__init__.py index 5925837f6..b3857356c 100644 --- a/src/anndata/__init__.py +++ b/src/anndata/__init__.py @@ -12,7 +12,6 @@ from ._core.merge import concat from ._core.raw import Raw from ._settings import settings -from ._version import __version__ from ._warnings import ( ExperimentalFeatureWarning, ImplicitModificationWarning, @@ -28,22 +27,6 @@ # We use these in tests by attribute access from . import logging # noqa: F401 # isort: skip -_DEPRECATED_IO = ( - "read_loom", - "read_hdf", - "read_excel", - "read_umi_tools", - "read_csv", - "read_text", - "read_mtx", -) -_DEPRECATED = {method: f"io.{method}" for method in _DEPRECATED_IO} - - -def __getattr__(attr_name: str) -> Any: - return module_get_attr_redirect(attr_name, deprecated_mapping=_DEPRECATED) - - __all__ = [ "AnnData", "ExperimentalFeatureWarning", @@ -51,7 +34,6 @@ def __getattr__(attr_name: str) -> Any: "OldFormatWarning", "Raw", "WriteWarning", - "__version__", "abc", "concat", "experimental", @@ -63,3 +45,26 @@ def __getattr__(attr_name: str) -> Any: "types", "typing", ] + +_DEPRECATED_IO = ( + "read_loom", + "read_hdf", + "read_excel", + "read_umi_tools", + "read_csv", + "read_text", + "read_mtx", +) +_DEPRECATED = {method: f"io.{method}" for method in _DEPRECATED_IO} + + +def __getattr__(attr_name: str) -> Any: + if attr_name == "__version__": + import warnings + from importlib.metadata import version + + msg = "`__version__` is deprecated, use `importlib.metadata.version('anndata')` instead." + warnings.warn(msg, FutureWarning, stacklevel=2) + return version("anndata") + + return module_get_attr_redirect(attr_name, deprecated_mapping=_DEPRECATED) diff --git a/src/anndata/_core/merge.py b/src/anndata/_core/merge.py index 36d3120fb..11ce6a166 100644 --- a/src/anndata/_core/merge.py +++ b/src/anndata/_core/merge.py @@ -14,9 +14,7 @@ import numpy as np import pandas as pd -import scipy from natsort import natsorted -from packaging.version import Version from scipy import sparse from anndata._core.file_backing import to_memory @@ -30,7 +28,6 @@ CupyCSRMatrix, CupySparseMatrix, DaskArray, - _map_cat_to_str, ) from ..utils import asarray, axis_len, warn_once from .anndata import AnnData @@ -185,15 +182,6 @@ def equal_sparse(a, b) -> bool: # Comparison broken for CSC matrices # https://github.com/cupy/cupy/issues/7757 a, b = CupyCSRMatrix(a), CupyCSRMatrix(b) - if Version(scipy.__version__) >= Version("1.16.0rc1"): - # TODO: https://github.com/scipy/scipy/issues/23068 - return bool( - a.format == b.format - and (a.shape == b.shape) - and np.all(a.indptr == b.indptr) - and np.all(a.indices == b.indices) - and np.all((a.data == b.data) | (np.isnan(a.data) & np.isnan(b.data))) - ) comp = a != b if isinstance(comp, bool): return not comp @@ -1646,7 +1634,7 @@ def concat( # noqa: PLR0912, PLR0913, PLR0915 ) if index_unique is not None: concat_indices = concat_indices.str.cat( - _map_cat_to_str(label_col), sep=index_unique + label_col.map(str, na_action="ignore"), sep=index_unique ) concat_indices = pd.Index(concat_indices) diff --git a/src/anndata/_core/sparse_dataset.py b/src/anndata/_core/sparse_dataset.py index fcdd80dd3..feb5868f8 100644 --- a/src/anndata/_core/sparse_dataset.py +++ b/src/anndata/_core/sparse_dataset.py @@ -16,6 +16,7 @@ from abc import ABC from collections.abc import Iterable from functools import cached_property +from importlib.metadata import version from itertools import accumulate, chain, pairwise from math import floor from pathlib import Path @@ -23,7 +24,6 @@ import h5py import numpy as np -import scipy import scipy.sparse as ss from packaging.version import Version from scipy.sparse import _sparsetools @@ -54,7 +54,7 @@ from scipy.sparse import spmatrix as _cs_matrix -SCIPY_1_15 = Version(scipy.__version__) >= Version("1.15rc0") +SCIPY_1_15 = Version(version("scipy")) >= Version("1.15rc0") class BackedFormat(NamedTuple): diff --git a/src/anndata/_io/h5ad.py b/src/anndata/_io/h5ad.py index 1a842a4b4..93e9984ca 100644 --- a/src/anndata/_io/h5ad.py +++ b/src/anndata/_io/h5ad.py @@ -27,7 +27,6 @@ from .specs import read_elem, write_elem from .specs.registry import IOSpec, write_spec from .utils import ( - H5PY_V3, _read_legacy_raw, idx_chunks_along_axis, no_write_dataset_2d, @@ -324,16 +323,12 @@ def read_dataframe_legacy(dataset: h5py.Dataset) -> pd.DataFrame: "Consider rewriting it." ) warn(msg, OldFormatWarning, stacklevel=2) - if H5PY_V3: - df = pd.DataFrame( - _decode_structured_array( - _from_fixed_length_strings(dataset[()]), dtype=dataset.dtype - ) + df = pd.DataFrame( + _decode_structured_array( + _from_fixed_length_strings(dataset[()]), dtype=dataset.dtype ) - else: - df = pd.DataFrame(_from_fixed_length_strings(dataset[()])) - df.set_index(df.columns[0], inplace=True) - return df + ) + return df.set_index(df.columns[0]) def read_dataframe(group: h5py.Group | h5py.Dataset) -> pd.DataFrame: @@ -346,10 +341,9 @@ def read_dataframe(group: h5py.Group | h5py.Dataset) -> pd.DataFrame: @report_read_key_on_error def read_dataset(dataset: h5py.Dataset): - if H5PY_V3: - string_dtype = h5py.check_string_dtype(dataset.dtype) - if (string_dtype is not None) and (string_dtype.encoding == "utf-8"): - dataset = dataset.asstr() + string_dtype = h5py.check_string_dtype(dataset.dtype) + if (string_dtype is not None) and (string_dtype.encoding == "utf-8"): + dataset = dataset.asstr() value = dataset[()] if not hasattr(value, "dtype"): return value @@ -362,10 +356,9 @@ def read_dataset(dataset: h5py.Dataset): return value[0] elif len(value.dtype.descr) > 1: # Compound dtype # For backwards compat, now strings are written as variable length - dtype = value.dtype - value = _from_fixed_length_strings(value) - if H5PY_V3: - value = _decode_structured_array(value, dtype=dtype) + value = _decode_structured_array( + _from_fixed_length_strings(value), dtype=value.dtype + ) if value.shape == (): value = value[()] return value diff --git a/src/anndata/_io/specs/methods.py b/src/anndata/_io/specs/methods.py index 2130b8c92..ceb05b34e 100644 --- a/src/anndata/_io/specs/methods.py +++ b/src/anndata/_io/specs/methods.py @@ -4,6 +4,7 @@ from collections.abc import Mapping from copy import copy from functools import partial +from importlib.metadata import version from itertools import product from types import MappingProxyType from typing import TYPE_CHECKING @@ -21,7 +22,7 @@ from anndata._core.index import _normalize_indices from anndata._core.merge import intersect_keys from anndata._core.sparse_dataset import _CSCDataset, _CSRDataset, sparse_dataset -from anndata._io.utils import H5PY_V3, check_key, zero_dim_array_as_scalar +from anndata._io.utils import check_key, zero_dim_array_as_scalar from anndata._warnings import OldFormatWarning from anndata.compat import ( NULLABLE_NUMPY_STRING_TYPE, @@ -609,7 +610,7 @@ def write_vlen_string_array_zarr( if is_zarr_v2(): import numcodecs - if Version(numcodecs.__version__) < Version("0.13"): + if Version(version("numcodecs")) < Version("0.13"): msg = "Old numcodecs version detected. Please update for improved performance and stability." warnings.warn(msg, UserWarning, stacklevel=2) # Workaround for https://github.com/zarr-developers/numcodecs/issues/514 @@ -665,10 +666,9 @@ def _to_hdf5_vlen_strings(value: np.ndarray) -> np.ndarray: @_REGISTRY.register_read(ZarrArray, IOSpec("rec-array", "0.2.0")) def read_recarray(d: ArrayStorageType, *, _reader: Reader) -> np.recarray | npt.NDArray: value = d[()] - dtype = value.dtype - value = _from_fixed_length_strings(value) - if H5PY_V3: - value = _decode_structured_array(value, dtype=dtype) + value = _decode_structured_array( + _from_fixed_length_strings(value), dtype=value.dtype + ) return value diff --git a/src/anndata/_io/utils.py b/src/anndata/_io/utils.py index 6bd43b9d2..223412fdd 100644 --- a/src/anndata/_io/utils.py +++ b/src/anndata/_io/utils.py @@ -1,13 +1,11 @@ from __future__ import annotations +from collections.abc import Callable from functools import WRAPPER_ASSIGNMENTS, wraps from itertools import pairwise -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Literal, cast from warnings import warn -import h5py -from packaging.version import Version - from .._core.sparse_dataset import BaseCompressedSparseDataset if TYPE_CHECKING: @@ -21,9 +19,6 @@ Storage = StorageType | BaseCompressedSparseDataset -# For allowing h5py v3 -# https://github.com/scverse/anndata/issues/442 -H5PY_V3 = Version(h5py.__version__).major >= 3 # ------------------------------------------------------------------------------- # Type conversion diff --git a/src/anndata/_version.py b/src/anndata/_version.py deleted file mode 100644 index ec64017d2..000000000 --- a/src/anndata/_version.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Get version from VCS in a dev environment or from package metadata in production. - -See . -""" - -from __future__ import annotations - -import warnings -from pathlib import Path - -__all__ = ["__version__"] - -_PROJECT_NAME = "anndata" - - -class GetVersionError(Exception): - pass - - -def _get_version_from_vcs(project_name: str) -> str: # pragma: no cover - from hatchling.metadata.core import ProjectMetadata - from hatchling.plugin.exceptions import UnknownPluginError - from hatchling.plugin.manager import PluginManager - from hatchling.utils.fs import locate_file - - if (pyproject_toml := locate_file(__file__, "pyproject.toml")) is None: - msg = "pyproject.toml not found although hatchling is installed" - raise LookupError(msg) - root = Path(pyproject_toml).parent - metadata = ProjectMetadata(root=str(root), plugin_manager=PluginManager()) - try: - # Version can be either statically set in pyproject.toml or computed dynamically: - version = metadata.core.version or metadata.hatch.version.cached - except UnknownPluginError as e: - msg = "Unable to import hatch plugin." - raise ImportError(msg) from e - except ValueError as e: - msg = f"Could not find hatchling project data in TOML file, {pyproject_toml}" - raise GetVersionError(msg) from e - except TypeError as e: - msg = "Could not parse build configuration." - raise GetVersionError(msg) from e - except Exception as e: - msg = ( - f"Unknown error getting version from hatchling config for '{project_name}'." - ) - warnings.warn(f"{msg}: {e}", stacklevel=1) - raise GetVersionError(msg) from e - - # We found a hatchling environment, but is it ours? - if metadata.core.name != project_name: - msg = f"Data in pyproject.toml is not related to {project_name}." - raise GetVersionError(msg) - return version - - -try: - __version__ = _get_version_from_vcs(_PROJECT_NAME) -except (ImportError, LookupError, GetVersionError): - import importlib.metadata - - __version__ = importlib.metadata.version(_PROJECT_NAME) diff --git a/src/anndata/compat/__init__.py b/src/anndata/compat/__init__.py index 8835804e2..03e9f5182 100644 --- a/src/anndata/compat/__init__.py +++ b/src/anndata/compat/__init__.py @@ -3,6 +3,7 @@ from codecs import decode from collections.abc import Mapping, Sequence from functools import cache, partial, singledispatch +from importlib.metadata import version from importlib.util import find_spec from types import EllipsisType from typing import TYPE_CHECKING, TypeVar @@ -75,10 +76,9 @@ class Empty: ############################# @cache def is_zarr_v2() -> bool: - import zarr from packaging.version import Version - return Version(zarr.__version__) < Version("3.0.0") + return Version(version("zarr")) < Version("3.0.0") if is_zarr_v2(): @@ -213,7 +213,7 @@ def old_positionals(*old_positionals): NULLABLE_NUMPY_STRING_TYPE = ( np.dtype("O") - if Version(np.__version__) < Version("2") + if Version(version("numpy")) < Version("2") else np.dtypes.StringDType(na_object=pd.NA) ) @@ -428,11 +428,3 @@ def _safe_transpose(x): return _transpose_by_block(x) else: return x.T - - -def _map_cat_to_str(cat: pd.Categorical) -> pd.Categorical: - if Version(pd.__version__) >= Version("2.1"): - # Argument added in pandas 2.1 - return cat.map(str, na_action="ignore") - else: - return cat.map(str) diff --git a/src/anndata/experimental/merge.py b/src/anndata/experimental/merge.py index 017345906..2f972a853 100644 --- a/src/anndata/experimental/merge.py +++ b/src/anndata/experimental/merge.py @@ -26,7 +26,7 @@ ) from .._core.sparse_dataset import BaseCompressedSparseDataset, sparse_dataset from .._io.specs import read_elem, write_elem -from ..compat import H5Array, H5Group, ZarrArray, ZarrGroup, _map_cat_to_str +from ..compat import H5Array, H5Group, ZarrArray, ZarrGroup from . import read_dispatched if TYPE_CHECKING: @@ -609,7 +609,7 @@ def concat_on_disk( # noqa: PLR0912, PLR0913, PLR0915 ) if index_unique is not None: concat_indices = concat_indices.str.cat( - _map_cat_to_str(label_col), sep=index_unique + label_col.map(str, na_action="ignore"), sep=index_unique ) # Resulting indices for {axis_name} and {alt_axis_name} diff --git a/src/anndata/experimental/multi_files/_anncollection.py b/src/anndata/experimental/multi_files/_anncollection.py index 02b83e386..77e833cbd 100644 --- a/src/anndata/experimental/multi_files/_anncollection.py +++ b/src/anndata/experimental/multi_files/_anncollection.py @@ -16,7 +16,7 @@ from ..._core.merge import concat_arrays, inner_concat_aligned_mapping from ..._core.sparse_dataset import BaseCompressedSparseDataset from ..._core.views import _resolve_idx -from ...compat import _map_cat_to_str, old_positionals +from ...compat import old_positionals if TYPE_CHECKING: from collections.abc import Iterable, Sequence @@ -731,7 +731,7 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915 ) if index_unique is not None: concat_indices = concat_indices.str.cat( - _map_cat_to_str(label_col), sep=index_unique + label_col.map(str, na_action="ignore"), sep=index_unique ) self.obs_names = pd.Index(concat_indices) diff --git a/src/testing/anndata/_pytest.py b/src/testing/anndata/_pytest.py index 7a77265ea..6a9d4f5f4 100644 --- a/src/testing/anndata/_pytest.py +++ b/src/testing/anndata/_pytest.py @@ -35,14 +35,10 @@ def _anndata_test_env(request: pytest.FixtureRequest) -> None: def _doctest_env( request: pytest.FixtureRequest, cache: pytest.Cache, tmp_path: Path ) -> Generator[None, None, None]: - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", message=r"Importing read_.* from `anndata` is deprecated" - ) - from scanpy import settings - from contextlib import chdir + from scanpy import settings + from anndata.utils import import_name assert isinstance(request.node.parent, pytest.Module) diff --git a/tests/conftest.py b/tests/conftest.py index 8441dc940..5eb81ae21 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,9 @@ from __future__ import annotations from functools import partial +from importlib.metadata import version from typing import TYPE_CHECKING -import dask import joblib import pytest from dask.base import normalize_token, tokenize @@ -11,7 +11,7 @@ from anndata.compat import is_zarr_v2 -if Version(dask.__version__) < Version("2024.8.0"): +if Version(version("dask")) < Version("2024.8.0"): from dask.base import normalize_seq else: from dask.tokenize import normalize_seq diff --git a/tests/test_concatenate.py b/tests/test_concatenate.py index 8f640004d..564efa63d 100644 --- a/tests/test_concatenate.py +++ b/tests/test_concatenate.py @@ -4,6 +4,7 @@ from collections.abc import Hashable from copy import deepcopy from functools import partial, singledispatch +from importlib.metadata import version from importlib.util import find_spec from itertools import chain, permutations, product from operator import attrgetter @@ -12,7 +13,6 @@ import numpy as np import pandas as pd import pytest -import scipy from boltons.iterutils import default_exit, remap, research from numpy import ma from packaging.version import Version @@ -1634,7 +1634,7 @@ def test_concat_X_dtype(cpu_array_type, sparse_indexer_type): if sparse_indexer_type == np.int64 and ( ( (issubclass(cpu_array_type, CSArray) or adata.X.format == "csc") - and Version(scipy.__version__) < Version("1.15.0") + and Version(version("scipy")) < Version("1.15.0") ) or issubclass(cpu_array_type, CSMatrix) ): diff --git a/tests/test_io_elementwise.py b/tests/test_io_elementwise.py index cdf6769c3..4e847bd9f 100644 --- a/tests/test_io_elementwise.py +++ b/tests/test_io_elementwise.py @@ -13,7 +13,6 @@ import pandas as pd import pytest import zarr -from packaging.version import Version from scipy import sparse import anndata as ad @@ -615,9 +614,7 @@ def test_dataframe_column_uniqueness(store): @pytest.mark.parametrize("copy_on_write", [True, False]) -def test_io_pd_cow(store, copy_on_write): - if Version(pd.__version__) < Version("2"): - pytest.xfail("copy_on_write option is not available in pandas < 2") +def test_io_pd_cow(store, copy_on_write) -> None: # https://github.com/zarr-developers/numcodecs/issues/514 with pd.option_context("mode.copy_on_write", copy_on_write): orig = gen_adata((3, 2), **GEN_ADATA_NO_XARRAY_ARGS) diff --git a/tests/test_io_partial.py b/tests/test_io_partial.py index 7ed96e9fa..70fb9b995 100644 --- a/tests/test_io_partial.py +++ b/tests/test_io_partial.py @@ -1,6 +1,5 @@ from __future__ import annotations -import warnings from importlib.util import find_spec from pathlib import Path @@ -43,11 +42,7 @@ def test_read_partial_X(tmp_path, typ, diskfmt): @pytest.mark.skipif(not find_spec("scanpy"), reason="Scanpy is not installed") def test_read_partial_adata(tmp_path, diskfmt): - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", message=r"Importing read_.* from `anndata` is deprecated" - ) - import scanpy as sc + import scanpy as sc adata = sc.datasets.pbmc68k_reduced() diff --git a/tests/test_io_warnings.py b/tests/test_io_warnings.py index 30f7d431c..c0e864dcb 100644 --- a/tests/test_io_warnings.py +++ b/tests/test_io_warnings.py @@ -5,21 +5,15 @@ from importlib.util import find_spec from pathlib import Path -import h5py import pytest -from packaging.version import Version import anndata as ad from anndata.tests.helpers import GEN_ADATA_NO_XARRAY_ARGS, gen_adata @pytest.mark.skipif(not find_spec("scanpy"), reason="Scanpy is not installed") -def test_old_format_warning_thrown(): - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", message=r"Importing read_.* from `anndata` is deprecated" - ) - import scanpy as sc +def test_old_format_warning_thrown() -> None: + import scanpy as sc def msg_re(entry: str) -> str: return re.escape( @@ -43,13 +37,6 @@ def test_old_format_warning_not_thrown(tmp_path: Path) -> None: with warnings.catch_warnings(record=True) as record: warnings.simplefilter("always", ad.OldFormatWarning) - if Version(h5py.__version__) < Version("3.2"): - # https://github.com/h5py/h5py/issues/1808 - warnings.filterwarnings( - "ignore", - r"Passing None into shape arguments as an alias for \(\) is deprecated\.", - category=DeprecationWarning, - ) ad.read_h5ad(pth) diff --git a/tests/test_readwrite.py b/tests/test_readwrite.py index c92d408ea..8fc6c4ca7 100644 --- a/tests/test_readwrite.py +++ b/tests/test_readwrite.py @@ -811,6 +811,11 @@ def roundtrip(diskfmt): return partial(_do_roundtrip, diskfmt=diskfmt) +@pytest.fixture +def roundtrip2(diskfmt2): + return partial(_do_roundtrip, diskfmt=diskfmt2) + + def test_write_string_types(tmp_path, diskfmt, roundtrip): # https://github.com/scverse/anndata/issues/456 adata_pth = tmp_path / f"adata.{diskfmt}" @@ -829,24 +834,17 @@ def test_write_string_types(tmp_path, diskfmt, roundtrip): @pytest.mark.skipif(not find_spec("scanpy"), reason="Scanpy is not installed") -def test_scanpy_pbmc68k(tmp_path, diskfmt, roundtrip, diskfmt2): - roundtrip2 = partial(_do_roundtrip, diskfmt=diskfmt2) - - filepth1 = tmp_path / f"test1.{diskfmt}" - filepth2 = tmp_path / f"test2.{diskfmt2}" - - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", message=r"Importing read_.* from `anndata` is deprecated" - ) - import scanpy as sc +def test_scanpy_pbmc68k(tmp_path, diskfmt, roundtrip, diskfmt2, roundtrip2): + import scanpy as sc with warnings.catch_warnings(): warnings.simplefilter("ignore", ad.OldFormatWarning) pbmc = sc.datasets.pbmc68k_reduced() - from_disk1 = roundtrip(pbmc, filepth1) # Do we read okay - from_disk2 = roundtrip2(from_disk1, filepth2) # Can we round trip + # Do we read okay + from_disk1 = roundtrip(pbmc, tmp_path / f"test1.{diskfmt}") + # Can we round trip + from_disk2 = roundtrip2(from_disk1, tmp_path / f"test2.{diskfmt2}") assert_equal(pbmc, from_disk1) # Not expected to be exact due to `nan`s assert_equal(pbmc, from_disk2) @@ -854,12 +852,7 @@ def test_scanpy_pbmc68k(tmp_path, diskfmt, roundtrip, diskfmt2): @pytest.mark.skipif(not find_spec("scanpy"), reason="Scanpy is not installed") def test_scanpy_krumsiek11(tmp_path, diskfmt, roundtrip): - filepth = tmp_path / f"test.{diskfmt}" - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", message=r"Importing read_.* from `anndata` is deprecated" - ) - import scanpy as sc + import scanpy as sc # TODO: this should be fixed in scanpy instead with pytest.warns(UserWarning, match=r"Observation names are not unique"): # noqa: PT031 @@ -871,7 +864,7 @@ def test_scanpy_krumsiek11(tmp_path, diskfmt, roundtrip): # Can’t write "string" dtype: https://github.com/scverse/anndata/issues/679 orig.obs["cell_type"] = orig.obs["cell_type"].astype(str) with pytest.warns(UserWarning, match=r"Observation names are not unique"): - curr = roundtrip(orig, filepth) + curr = roundtrip(orig, tmp_path / f"test.{diskfmt}") assert_equal(orig, curr, exact=True) @@ -884,12 +877,8 @@ def test_scanpy_krumsiek11(tmp_path, diskfmt, roundtrip): not Path(HERE / "data/pbmc68k_reduced_legacy.zarr.zip").is_file(), reason="File not present.", ) -def test_backwards_compat_zarr(): - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", message=r"Importing read_.* from `anndata` is deprecated" - ) - import scanpy as sc +def test_backwards_compat_zarr() -> None: + import scanpy as sc import zarr pbmc_orig = sc.datasets.pbmc68k_reduced() diff --git a/tests/test_views.py b/tests/test_views.py index cf66ca463..b6002b6aa 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,7 +1,8 @@ from __future__ import annotations -from contextlib import ExitStack, nullcontext +from contextlib import nullcontext from copy import deepcopy +from importlib.metadata import version from operator import mul from typing import TYPE_CHECKING @@ -144,20 +145,7 @@ def test_convert_error(): adata = ad.AnnData(np.array([[1, 2], [3, 0]])) no_array = [[1], []] - if Version(np.__version__) >= Version("1.24"): - stack = pytest.raises(ValueError, match=r"Failed to convert") - else: - stack = ExitStack() - stack.enter_context( - pytest.warns( - np.VisibleDeprecationWarning, - match=r"ndarray from ragged.*is deprecated", - ) - ) - stack.enter_context( - pytest.raises(ValueError, match=r"setting an array element with a sequence") - ) - with stack: + with pytest.raises(ValueError, match=r"Failed to convert"): adata[:, 0].X = no_array @@ -190,11 +178,10 @@ def test_modify_view_component(matrix_type, mapping_name, request): m[0, 0] = 100 assert not subset.is_view # TODO: Remove `raises` after https://github.com/scipy/scipy/pull/23626. - import dask is_dask_with_broken_view_setting = ( "sparse_dask" in request.node.callspec.id - and Version(dask.__version__) >= Version("2025.02.0") + and Version(version("dask")) >= Version("2025.02.0") ) is_sparse_array_in_lower_dask_version = ( not is_dask_with_broken_view_setting