diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3944a93..fc122a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ default_stages: minimum_pre_commit_version: 2.16.0 repos: - repo: https://github.com/biomejs/pre-commit - rev: v2.4.4 + rev: v2.4.6 hooks: - id: biome-format exclude: ^\.cruft\.json$ # inconsistent indentation with cruft - file never to be modified manually. @@ -16,7 +16,7 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.4 + rev: v0.15.5 hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] diff --git a/pyproject.toml b/pyproject.toml index bb9eefa..c674976 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,18 +14,16 @@ maintainers = [ authors = [ { name = "Tim Treis" }, ] -requires-python = ">=3.10" +requires-python = ">=3.12" classifiers = [ "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] dependencies = [ "anndata", - "holoviews", + "hv-anndata", # for debug logging (referenced from the issue template) "session-info2", ] @@ -69,9 +67,9 @@ envs.docs.scripts.clean = "git clean -fdX -- {args:docs}" envs.hatch-test.features = [ "dev", "test" ] envs.hatch-test.matrix = [ # Test the lowest and highest supported Python versions with normal deps - { deps = [ "stable" ], python = [ "3.10", "3.13" ] }, + { deps = [ "stable" ], python = [ "3.14", "3.12" ] }, # Test the newest supported Python version also with pre-release deps - { deps = [ "pre" ], python = [ "3.13" ] }, + { deps = [ "pre" ], python = [ "3.14" ] }, ] # If the matrix variable `deps` is set to "pre", # set the environment variable `UV_PRERELEASE` to "allow". diff --git a/src/anndata_plot/__init__.py b/src/anndata_plot/__init__.py index 5e3d9ab..199a50d 100644 --- a/src/anndata_plot/__init__.py +++ b/src/anndata_plot/__init__.py @@ -1,16 +1,3 @@ -from importlib.metadata import version - -import holoviews as hv - from . import pl, pp, tl -from .pl.utils import AnnDataInterface - -__all__ = ["pl"] - -__version__ = version("anndata-plot") - -# register the AnnDataInterface with holoviews -if AnnDataInterface.datatype not in hv.core.data.datatypes: - hv.core.data.datatypes.append(AnnDataInterface.datatype) -hv.core.Interface.register(AnnDataInterface) +__all__ = ["pl", "pp", "tl"] diff --git a/src/anndata_plot/pl/utils.py b/src/anndata_plot/pl/utils.py deleted file mode 100644 index a8b05d8..0000000 --- a/src/anndata_plot/pl/utils.py +++ /dev/null @@ -1,94 +0,0 @@ -from dataclasses import dataclass -from enum import Enum, auto -from typing import cast, overload - -import anndata as ad -import holoviews as hv -import numpy as np - - -class Raise(Enum): - """Marker sentry for raising an exception.""" - - Sentry = auto() - - -@dataclass -class AnnDataProxy: - """Proxy for anndata, used to get arrays with a mapping-like interface.""" - - # from: https://gist.github.com/flying-sheep/3ff54234019cc7c84e84cbbe649209c5 - - adata: ad.AnnData - - @overload - def get(self, k: str, /, default: None = None) -> np.ndarray | None: ... - @overload - def get(self, k: str, /, default: np.ndarray | Raise) -> np.ndarray: ... - - def get(self, k: str, /, default: np.ndarray | Raise | None = None) -> np.ndarray | None: - """Get an array from an AnnData object.""" - k_orig = k - if "." not in k: - if default is not Raise.Sentry and k not in self.adata.var_names: - return default - return self.adata[:, k].X.flatten() - attr_name, k = k.split(".", 1) - attr = getattr(self.adata, attr_name) - if "." not in k: - if default is not Raise.Sentry and k not in attr: - return default - return attr[k] - k, i = k.split(".", 1) - arr = attr[k] - if "." not in i: - if default is not Raise.Sentry and not (0 <= int(i) < arr.shape[1]): - return default - return arr[:, int(i)] - raise KeyError(k_orig) - - def __contains__(self, k: str) -> bool: - return self.get(k) is not None - - def __getitem__(self, k: str) -> object: - return self.get(k, Raise.Sentry) - - def __len__(self) -> int: - return len(self.adata) - - -class AnnDataInterface(hv.core.Interface): - """Interface for AnnData objects.""" - - types = (ad.AnnData,) - datatype = "anndata" - - @classmethod - def init( - cls, eltype, data: ad.AnnData | AnnDataProxy, kdims: list[str] | None, vdims: list[str] | None - ) -> tuple[AnnDataProxy, dict, dict]: - """Initialize the interface and return dataset and dimensions.""" - proxy = AnnDataProxy(data) if isinstance(data, ad.AnnData) else data - return proxy, {"kdims": kdims, "vdims": vdims}, {} - - @classmethod - def values( - cls, - data: hv.Dataset, - dim: hv.Dimension | str, - expanded=True, - flat=True, - compute=True, - keep_index=False, - ) -> np.ndarray: - """Get the values of a dimension.""" - dim = data.get_dimension(dim) - proxy = cast(AnnDataProxy, data.data) - return proxy[dim.name] - - @classmethod - def dimension_type(cls, data: hv.Dataset, dim: hv.Dimension | str) -> np.dtype: - """Get the dtype of a dimension.""" - dim = data.get_dimension(dim) - proxy = cast(AnnDataProxy, data.data) - return proxy[dim.name].dtype diff --git a/tests/test_basic.py b/tests/test_basic.py index 19d6058..05eda61 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -3,10 +3,10 @@ import anndata_plot -def test_package_has_version(): - assert anndata_plot.__version__ is not None +def test_all() -> None: + assert set(dir(anndata_plot)) > {"pl", "pp", "tl"} @pytest.mark.skip(reason="This decorator should be removed when test passes.") -def test_example(): +def test_example() -> None: assert 1 == 0 # This test is designed to fail.