diff --git a/.cruft.json b/.cruft.json index 7c632141..b3f28eea 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,7 +1,7 @@ { "template": "https://github.com/scverse/cookiecutter-scverse", - "commit": "8e96abb5c3e2d5078c44713958da672711cf2a48", - "checkout": "v0.3.0", + "commit": "586b1652162ff7994b0070a034023d64289ae416", + "checkout": "v0.3.1", "context": { "cookiecutter": { "project_name": "squidpy", @@ -13,7 +13,8 @@ "project_repo": "https://github.com/scverse/squidpy", "license": "BSD 3-Clause License", "_copy_without_render": [ - ".github/workflows/**.yaml", + ".github/workflows/build.yaml", + ".github/workflows/test.yaml", "docs/_templates/autosummary/**.rst" ], "_render_devdocs": false, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e3f838fa..9c1e6fe4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,11 +12,6 @@ repos: - id: mypy additional_dependencies: [numpy, pandas, types-requests] exclude: .scripts/ci/download_data.py|squidpy/datasets/_(dataset|image).py # See https://github.com/pre-commit/mirrors-mypy/issues/33 - - repo: https://github.com/psf/black - rev: 24.2.0 - hooks: - - id: black - additional_dependencies: [toml] - repo: https://github.com/pre-commit/mirrors-prettier rev: v4.0.0-alpha.8 hooks: @@ -50,16 +45,14 @@ repos: - id: script-must-have-extension name: Check executable files use .sh extension types: [shell, executable] - - repo: https://github.com/asottile/blacken-docs - rev: 1.16.0 - hooks: - - id: blacken-docs - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. rev: v0.2.2 hooks: - id: ruff + types_or: [python, pyi, jupyter] args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format + types_or: [python, pyi, jupyter] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: diff --git a/.scripts/ci/download_data.py b/.scripts/ci/download_data.py index 0bfea217..177a751d 100644 --- a/.scripts/ci/download_data.py +++ b/.scripts/ci/download_data.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +from __future__ import annotations + import argparse from pathlib import Path from typing import Any diff --git a/docs/_ext/typed_returns.py b/docs/_ext/typed_returns.py index 9b143126..8d43ba5c 100644 --- a/docs/_ext/typed_returns.py +++ b/docs/_ext/typed_returns.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import re -from typing import Iterable, Iterator, List +from collections.abc import Iterable, Iterator from sphinx.application import Sphinx from sphinx.ext.napoleon import NumpyDocstring diff --git a/docs/conf.py b/docs/conf.py index 5456b83c..661768ee 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,6 +4,8 @@ # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html +from __future__ import annotations + # -- Path setup -------------------------------------------------------------- import os import sys diff --git a/docs/utils.py b/docs/utils.py index f70b83ed..43864851 100644 --- a/docs/utils.py +++ b/docs/utils.py @@ -1,10 +1,12 @@ +from __future__ import annotations + import os import re from logging import info, warning from pathlib import Path from shutil import copytree, rmtree from tempfile import TemporaryDirectory -from typing import Any, Dict, ForwardRef, List, Union +from typing import Any, ForwardRef from enchant.tokenize import Filter from git import Repo @@ -14,7 +16,7 @@ def _fetch_notebooks(repo_url: str) -> None: - def copy_files(repo_path: Union[str, Path]) -> None: + def copy_files(repo_path: str | Path) -> None: repo_path = Path(repo_path) for dirname in ["tutorials", "auto_examples", "gen_modules"]: @@ -30,7 +32,7 @@ def fetch_remote(repo_url: str) -> None: copy_files(repo_dir) - def fetch_local(repo_path: Union[str, Path]) -> None: + def fetch_local(repo_path: str | Path) -> None: info(f"Fetching notebooks from local path `{repo_path}`") repo_path = Path(repo_path) if not repo_path.is_dir(): @@ -75,7 +77,7 @@ def run(self) -> list[str]: return [] -def _get_thumbnails(root: Union[str, Path]) -> dict[str, str]: +def _get_thumbnails(root: str | Path) -> dict[str, str]: res = {} root = Path(root) thumb_path = Path(__file__).parent.parent.parent / "docs" / "source" diff --git a/pyproject.toml b/pyproject.toml index 564413a4..8983654a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,65 +117,11 @@ include-package-data = true [tool.setuptools_scm] -[tool.black] -line-length = 120 -target-version = ['py39'] -include = '\.pyi?$' -exclude = ''' -( - /( - \.eggs - | \.git - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - )/ - -) -''' - -[tool.isort] -profile = "black" -py_version = "38" -skip = "docs/source/conf.py,.tox,build" -line_length = 88 -multi_line_output = 3 -include_trailing_comma = true -use_parentheses = true -known_stdlib = "joblib" -known_bio = "anndata,scanpy" -known_num = "numpy,numba,scipy,sklearn,statsmodels,pandas,xarray,dask" -known_plot = "matplotlib,seaborn,napari" -known_gui = "PyQt5,superqt" -known_img = "skimage,tifffile,dask_image" -known_graph = "networkx" -sections = "FUTURE,STDLIB,THIRDPARTY,BIO,NUM,GUI,PLOT,IMG,GRAPH,FIRSTPARTY,LOCALFOLDER" -no_lines_before="LOCALFOLDER" -balanced_wrapping = true -force_grid_wrap = 0 -length_sort = "1" -indent = " " -from_first = true -order_by_type = true -atomic = true -combine_star = true -combine_as_imports = true -honor_noqa = true -remove_redundant_aliases = true -only_modified = true -group_by_package = true -force_alphabetical_sort_within_sections = true -lexicographical = true - [tool.hatch.version] source = "vcs" [tool.ruff] +line-length = 120 exclude = [ ".git", ".tox", @@ -185,8 +131,13 @@ exclude = [ "dist", "setup.py" ] + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint] ignore = [ - # line too long -> we accept long comment lines; black gets rid of long code lines + # line too long -> we accept long comment lines; formatter gets rid of long code lines "E501", # Do not assign a lambda expression, use a def -> lambda expression assignments are convenient "E731", @@ -243,7 +194,6 @@ ignore = [ # "E111", # "E114", ] -line-length = 120 select = [ "I", # isort "E", # pycodestyle @@ -255,19 +205,22 @@ select = [ "B", # flake8-bugbear "BLE", # flake8-blind-except ] -unfixable = ["B", "UP", "C4", "BLE"] -target-version = "py38" -[tool.ruff.per-file-ignores] - "*/__init__.py" = ["D104", "F401"] - "tests/*"= ["D"] - "docs/*"= ["D","B"] - "squidpy/pl/_ligrec.py"= ["D","B"] - "squidpy/_constants/_pkg_constants.py"= ["D101","D102","D106"] - "squidpy/_constants/_constants.py"= ["D101"] - "squidpy/pl/_interactive/_widgets.py"= ["D"] - ".scripts/ci/download_data.py"= ["D","B"] - # "squidpy/*.py"= ["RST303"] +unfixable = ["B", "C4", "BLE"] + +[tool.ruff.lint.isort] +required-imports = ["from __future__ import annotations"] + +[tool.ruff.lint.per-file-ignores] +"*/__init__.py" = ["D104", "F401"] +"tests/*"= ["D"] +"docs/*"= ["D","B"] +"squidpy/pl/_ligrec.py"= ["D","B"] +"squidpy/_constants/_pkg_constants.py"= ["D101","D102","D106"] +"squidpy/_constants/_constants.py"= ["D101"] +"squidpy/pl/_interactive/_widgets.py"= ["D"] +".scripts/ci/download_data.py"= ["D","B"] +# "squidpy/*.py"= ["RST303"] -[tool.ruff.flake8-tidy-imports] +[tool.ruff.lint.flake8-tidy-imports] # Disallow all relative imports. ban-relative-imports = "all" diff --git a/src/squidpy/__init__.py b/src/squidpy/__init__.py index b8746064..9782ae4e 100644 --- a/src/squidpy/__init__.py +++ b/src/squidpy/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from importlib import metadata from squidpy import datasets, gr, im, pl, read, tl diff --git a/src/squidpy/_constants/_constants.py b/src/squidpy/_constants/_constants.py index 2bdcbb6f..6d6bdab7 100644 --- a/src/squidpy/_constants/_constants.py +++ b/src/squidpy/_constants/_constants.py @@ -1,5 +1,7 @@ """Constants that user deals with.""" +from __future__ import annotations + from enum import unique from squidpy._constants._utils import ModeEnum diff --git a/src/squidpy/_constants/_pkg_constants.py b/src/squidpy/_constants/_pkg_constants.py index 5ef73ff6..372257d7 100644 --- a/src/squidpy/_constants/_pkg_constants.py +++ b/src/squidpy/_constants/_pkg_constants.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Callable, Mapping, Optional, Sequence, Union +from collections.abc import Callable, Mapping, Sequence +from typing import Any from anndata import AnnData diff --git a/src/squidpy/_constants/_utils.py b/src/squidpy/_constants/_utils.py index c2c22a74..6b9e2860 100644 --- a/src/squidpy/_constants/_utils.py +++ b/src/squidpy/_constants/_utils.py @@ -1,9 +1,10 @@ from __future__ import annotations from abc import ABC, ABCMeta +from collections.abc import Mapping from enum import Enum, EnumMeta from functools import wraps -from typing import Any, Callable, Mapping, Tuple +from typing import Any, Callable def _pretty_raise_enum(cls: type[ModeEnum], fun: Callable[..., Any]) -> Callable[..., Any]: @@ -33,7 +34,9 @@ class ErrorFormatterABC(ABC): @classmethod def _format(cls, value: Enum) -> str: return cls.__error_format__.format( - value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined] + value, + cls.__name__, + [m.value for m in cls.__members__.values()], # type: ignore[attr-defined] ) diff --git a/src/squidpy/_utils.py b/src/squidpy/_utils.py index f1ad295a..fb07dce0 100644 --- a/src/squidpy/_utils.py +++ b/src/squidpy/_utils.py @@ -5,21 +5,13 @@ import functools import inspect import warnings +from collections.abc import Generator, Hashable, Iterable, Sequence from contextlib import contextmanager from enum import Enum from multiprocessing import Manager, cpu_count from queue import Queue from threading import Thread -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Generator, - Hashable, - Iterable, - Sequence, - Union, # noqa: F401 -) +from typing import TYPE_CHECKING, Any, Callable import joblib as jl import numpy as np diff --git a/src/squidpy/datasets/__init__.py b/src/squidpy/datasets/__init__.py index d89d1046..81e20f57 100644 --- a/src/squidpy/datasets/__init__.py +++ b/src/squidpy/datasets/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from squidpy.datasets._10x_datasets import visium from squidpy.datasets._dataset import * # noqa: F403 from squidpy.datasets._image import * # noqa: F403 diff --git a/src/squidpy/datasets/_dataset.py b/src/squidpy/datasets/_dataset.py index 489c4b98..83c5e361 100644 --- a/src/squidpy/datasets/_dataset.py +++ b/src/squidpy/datasets/_dataset.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from copy import copy from squidpy.datasets._utils import AMetadata diff --git a/src/squidpy/datasets/_image.py b/src/squidpy/datasets/_image.py index d8425a35..fe1aa513 100644 --- a/src/squidpy/datasets/_image.py +++ b/src/squidpy/datasets/_image.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from copy import copy from squidpy.datasets._utils import ImgMetadata diff --git a/src/squidpy/datasets/_utils.py b/src/squidpy/datasets/_utils.py index ed3cf6bd..e815e378 100644 --- a/src/squidpy/datasets/_utils.py +++ b/src/squidpy/datasets/_utils.py @@ -1,9 +1,12 @@ +from __future__ import annotations + import os from abc import ABC, abstractmethod +from collections.abc import Sequence from dataclasses import dataclass, field from inspect import Parameter, Signature, signature from pathlib import Path -from typing import Any, Callable, Dict, Optional, Sequence, Tuple, Union +from typing import Any, Callable, Union import anndata from anndata import AnnData @@ -22,10 +25,10 @@ class Metadata(ABC): name: str url: str - doc_header: Optional[str] = field(default=None, repr=False) - path: Optional[PathLike] = field(default=None, repr=False) - shape: Optional[tuple[int, int]] = field(default=None, repr=False) - library_id: Optional[Union[str, Sequence[str]]] = field(default=None, repr=False) + doc_header: str | None = field(default=None, repr=False) + path: PathLike | None = field(default=None, repr=False) + shape: tuple[int, int] | None = field(default=None, repr=False) + library_id: str | Sequence[str] | None = field(default=None, repr=False) _DOC_FMT = "" @@ -66,7 +69,7 @@ def _create_function(self, name: str, glob_ns: dict[str, Any]) -> None: glob_ns, ) - def download(self, fpath: Optional[PathLike] = None, **kwargs: Any) -> Any: + def download(self, fpath: PathLike | None = None, **kwargs: Any) -> Any: """Download the dataset into ``fpath``.""" fpath = str(self.path if fpath is None else fpath) if not fpath.endswith(self._extension): diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index 0a370f2e..9045b6a5 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -1,5 +1,7 @@ """The graph module.""" +from __future__ import annotations + from squidpy.gr._build import spatial_neighbors from squidpy.gr._ligrec import ligrec from squidpy.gr._nhood import centrality_scores, interaction_matrix, nhood_enrichment diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 46be0b88..e3803016 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -3,9 +3,9 @@ from __future__ import annotations import warnings +from collections.abc import Iterable # noqa: F401 from functools import partial from itertools import chain -from typing import Iterable, List, Tuple, Union # noqa: F401 import numpy as np from anndata import AnnData diff --git a/src/squidpy/gr/_ligrec.py b/src/squidpy/gr/_ligrec.py index 628d1b33..8c441ceb 100644 --- a/src/squidpy/gr/_ligrec.py +++ b/src/squidpy/gr/_ligrec.py @@ -4,10 +4,11 @@ from abc import ABC from collections import namedtuple +from collections.abc import Iterable, Mapping, Sequence from functools import partial from itertools import product from types import MappingProxyType -from typing import TYPE_CHECKING, Any, Iterable, Literal, Mapping, Sequence, Union +from typing import TYPE_CHECKING, Any, Literal, Union import numpy as np import pandas as pd @@ -822,7 +823,8 @@ def _analysis_helper( fn_key = f"_test_{n_cls}_{int(return_means)}_{bool(numba_parallel)}" if fn_key not in globals(): exec( - compile(_create_template(n_cls, return_means=return_means, parallel=numba_parallel), "", "exec"), globals() # type: ignore[arg-type] + compile(_create_template(n_cls, return_means=return_means, parallel=numba_parallel), "", "exec"), # type: ignore[arg-type] + globals(), ) _test = globals()[fn_key] diff --git a/src/squidpy/gr/_nhood.py b/src/squidpy/gr/_nhood.py index 6d21257c..db5ce0f3 100644 --- a/src/squidpy/gr/_nhood.py +++ b/src/squidpy/gr/_nhood.py @@ -2,14 +2,9 @@ from __future__ import annotations +from collections.abc import Iterable, Sequence from functools import partial -from typing import ( - Any, - Callable, - Iterable, - Sequence, - Union, # noqa: F401 -) +from typing import Any, Callable import networkx as nx import numba.types as nt diff --git a/src/squidpy/gr/_ppatterns.py b/src/squidpy/gr/_ppatterns.py index d23be9a0..9f672832 100644 --- a/src/squidpy/gr/_ppatterns.py +++ b/src/squidpy/gr/_ppatterns.py @@ -2,16 +2,9 @@ from __future__ import annotations +from collections.abc import Iterable, Sequence from itertools import chain -from typing import ( - TYPE_CHECKING, - Any, - Dict, - Iterable, - Literal, # < 3.8 - Sequence, - Union, # noqa: F401 -) +from typing import TYPE_CHECKING, Any, Literal import numba.types as nt import numpy as np diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index c9fa915a..6ca6b216 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -1,11 +1,7 @@ from __future__ import annotations -from typing import ( - Callable, - Literal, # < 3.8 - Sequence, - Union, # noqa: F401 -) +from collections.abc import Sequence +from typing import Callable, Literal import numpy as np import pandas as pd diff --git a/src/squidpy/gr/_utils.py b/src/squidpy/gr/_utils.py index be373d9f..f9f72e24 100644 --- a/src/squidpy/gr/_utils.py +++ b/src/squidpy/gr/_utils.py @@ -2,15 +2,9 @@ from __future__ import annotations +from collections.abc import Hashable, Iterable, Sequence from contextlib import contextmanager -from typing import ( - TYPE_CHECKING, - Any, - Hashable, - Iterable, - Sequence, - Union, # noqa: F401 -) +from typing import TYPE_CHECKING, Any import numpy as np import pandas as pd diff --git a/src/squidpy/im/__init__.py b/src/squidpy/im/__init__.py index f400dba7..fcf54cf9 100644 --- a/src/squidpy/im/__init__.py +++ b/src/squidpy/im/__init__.py @@ -1,5 +1,7 @@ """The image module.""" +from __future__ import annotations + from squidpy.im._container import ImageContainer from squidpy.im._feature import calculate_image_features from squidpy.im._process import process diff --git a/src/squidpy/im/_container.py b/src/squidpy/im/_container.py index 65e1c72c..e73b89ed 100644 --- a/src/squidpy/im/_container.py +++ b/src/squidpy/im/_container.py @@ -1,23 +1,13 @@ from __future__ import annotations import re +from collections.abc import Iterable, Iterator, Mapping, Sequence from copy import copy, deepcopy from functools import partial from itertools import chain from pathlib import Path from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Iterable, - Iterator, - Literal, - Mapping, - Sequence, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, Union import dask.array as da import matplotlib as mpl diff --git a/src/squidpy/im/_coords.py b/src/squidpy/im/_coords.py index b9b12d93..b2cd796c 100644 --- a/src/squidpy/im/_coords.py +++ b/src/squidpy/im/_coords.py @@ -1,12 +1,9 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Hashable from dataclasses import dataclass -from typing import ( - Any, - Hashable, - Union, # noqa: F401 -) +from typing import Any import numpy as np diff --git a/src/squidpy/im/_feature.py b/src/squidpy/im/_feature.py index 08521ac0..91cf4a8c 100644 --- a/src/squidpy/im/_feature.py +++ b/src/squidpy/im/_feature.py @@ -1,13 +1,8 @@ from __future__ import annotations +from collections.abc import Mapping, Sequence from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - Mapping, - Sequence, - Union, # noqa: F401 -) +from typing import TYPE_CHECKING, Any import pandas as pd from anndata import AnnData diff --git a/src/squidpy/im/_feature_mixin.py b/src/squidpy/im/_feature_mixin.py index 6ab74f99..d0caddf1 100644 --- a/src/squidpy/im/_feature_mixin.py +++ b/src/squidpy/im/_feature_mixin.py @@ -1,15 +1,7 @@ from __future__ import annotations -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - Iterable, - Protocol, - Sequence, - Union, -) +from collections.abc import Iterable, Sequence +from typing import TYPE_CHECKING, Any, Callable, Protocol, Union import numpy as np import skimage.measure @@ -74,15 +66,18 @@ def _get_channels(xr_img: NDArrayA | xr.DataArray, channels: Channel_t | None) - class HasGetItemProtocol(Protocol): """Protocol for FeatureMixin to have correct definition of ImageContainer.""" - def __getitem__(self, key: str) -> xr.DataArray: ... + def __getitem__(self, key: str) -> xr.DataArray: + ... @property def data(self) -> xr.Dataset: # noqa: D102 ... - def _get_layer(self, layer: str | None) -> str: ... + def _get_layer(self, layer: str | None) -> str: + ... - def _get_library_id(self, library_id: str | None) -> str: ... + def _get_library_id(self, library_id: str | None) -> str: + ... class FeatureMixin: diff --git a/src/squidpy/im/_io.py b/src/squidpy/im/_io.py index 916969d8..80a330bb 100644 --- a/src/squidpy/im/_io.py +++ b/src/squidpy/im/_io.py @@ -1,10 +1,7 @@ from __future__ import annotations +from collections.abc import Mapping from pathlib import Path -from typing import ( - Mapping, - Union, # noqa: F401 -) import dask.array as da import numpy as np diff --git a/src/squidpy/im/_process.py b/src/squidpy/im/_process.py index bd85d48a..6048fc60 100644 --- a/src/squidpy/im/_process.py +++ b/src/squidpy/im/_process.py @@ -1,13 +1,8 @@ from __future__ import annotations +from collections.abc import Mapping, Sequence from types import MappingProxyType -from typing import ( - Any, - Callable, - Mapping, - Sequence, - Union, # noqa: F401 -) +from typing import Any, Callable import dask.array as da from dask_image.ndfilters import gaussian_filter as dask_gf diff --git a/src/squidpy/im/_segment.py b/src/squidpy/im/_segment.py index beea362d..88a4281f 100644 --- a/src/squidpy/im/_segment.py +++ b/src/squidpy/im/_segment.py @@ -1,15 +1,9 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Mapping, Sequence from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Mapping, - Sequence, - Union, # noqa: F401 -) +from typing import TYPE_CHECKING, Any, Callable import dask.array as da import numpy as np diff --git a/src/squidpy/pl/__init__.py b/src/squidpy/pl/__init__.py index b1e39798..051490c4 100644 --- a/src/squidpy/pl/__init__.py +++ b/src/squidpy/pl/__init__.py @@ -1,5 +1,7 @@ """The plotting module.""" +from __future__ import annotations + from squidpy.pl._graph import ( centrality_scores, co_occurrence, diff --git a/src/squidpy/pl/_color_utils.py b/src/squidpy/pl/_color_utils.py index d57417ff..4f4fa104 100644 --- a/src/squidpy/pl/_color_utils.py +++ b/src/squidpy/pl/_color_utils.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Mapping, Optional, Sequence, Union +from collections.abc import Mapping, Sequence +from typing import Any, Union import matplotlib.pyplot as plt import numpy as np diff --git a/src/squidpy/pl/_graph.py b/src/squidpy/pl/_graph.py index 106f4f4d..fab2a4b5 100644 --- a/src/squidpy/pl/_graph.py +++ b/src/squidpy/pl/_graph.py @@ -2,16 +2,10 @@ from __future__ import annotations +from collections.abc import Mapping, Sequence from pathlib import Path from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - Literal, - Mapping, - Sequence, - Union, # noqa: F401 -) +from typing import TYPE_CHECKING, Any, Literal import matplotlib.pyplot as plt import numpy as np diff --git a/src/squidpy/pl/_interactive/__init__.py b/src/squidpy/pl/_interactive/__init__.py index 400bc165..4cc6b18d 100644 --- a/src/squidpy/pl/_interactive/__init__.py +++ b/src/squidpy/pl/_interactive/__init__.py @@ -1 +1,3 @@ +from __future__ import annotations + from squidpy.pl._interactive.interactive import Interactive diff --git a/src/squidpy/pl/_interactive/_model.py b/src/squidpy/pl/_interactive/_model.py index 4d1c4290..9fce0b09 100644 --- a/src/squidpy/pl/_interactive/_model.py +++ b/src/squidpy/pl/_interactive/_model.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Sequence +from typing import TYPE_CHECKING import numpy as np from anndata import AnnData diff --git a/src/squidpy/pl/_interactive/_widgets.py b/src/squidpy/pl/_interactive/_widgets.py index 21ffbe93..879b8bcc 100644 --- a/src/squidpy/pl/_interactive/_widgets.py +++ b/src/squidpy/pl/_interactive/_widgets.py @@ -2,7 +2,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import Any, Iterable, Tuple +from collections.abc import Iterable +from typing import Any import numpy as np import pandas as pd diff --git a/src/squidpy/pl/_ligrec.py b/src/squidpy/pl/_ligrec.py index 6b98aefb..c1fcd9e0 100644 --- a/src/squidpy/pl/_ligrec.py +++ b/src/squidpy/pl/_ligrec.py @@ -1,13 +1,8 @@ from __future__ import annotations +from collections.abc import Mapping, Sequence from pathlib import Path -from typing import ( - TYPE_CHECKING, - Any, - Mapping, - Sequence, - Union, # noqa: F401 -) +from typing import TYPE_CHECKING, Any import matplotlib.pyplot as plt import numpy as np diff --git a/src/squidpy/pl/_spatial.py b/src/squidpy/pl/_spatial.py index b57d2adb..94d02ed6 100644 --- a/src/squidpy/pl/_spatial.py +++ b/src/squidpy/pl/_spatial.py @@ -1,9 +1,10 @@ from __future__ import annotations import itertools +from collections.abc import Mapping, Sequence from pathlib import Path from types import MappingProxyType -from typing import Any, Callable, List, Mapping, Optional, Sequence, Tuple, Union +from typing import Any, Callable from anndata import AnnData from matplotlib.axes import Axes diff --git a/src/squidpy/pl/_spatial_utils.py b/src/squidpy/pl/_spatial_utils.py index 77b42e2b..937f0722 100644 --- a/src/squidpy/pl/_spatial_utils.py +++ b/src/squidpy/pl/_spatial_utils.py @@ -1,23 +1,12 @@ from __future__ import annotations import itertools +from collections.abc import Mapping, Sequence from copy import copy from functools import partial from numbers import Number from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - List, - Literal, - Mapping, - NamedTuple, - Optional, - Sequence, - Tuple, - Type, - Union, -) +from typing import TYPE_CHECKING, Any, Literal, NamedTuple, Optional, Union import dask.array as da import numpy as np @@ -60,7 +49,7 @@ _SeqStr = Union[str, Sequence[str]] _SeqFloat = Union[float, Sequence[float]] _SeqArray = Union[NDArrayA, Sequence[NDArrayA]] -_CoordTuple = Tuple[int, int, int, int] +_CoordTuple = tuple[int, int, int, int] _FontWeight = Literal["light", "normal", "medium", "semibold", "bold", "heavy", "black"] _FontSize = Literal["xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"] diff --git a/src/squidpy/pl/_utils.py b/src/squidpy/pl/_utils.py index ba004c07..b30aa297 100644 --- a/src/squidpy/pl/_utils.py +++ b/src/squidpy/pl/_utils.py @@ -1,21 +1,12 @@ from __future__ import annotations import os +from collections.abc import Mapping, Sequence from functools import wraps from inspect import signature from pathlib import Path from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - Callable, - List, - Mapping, - Optional, - Sequence, - Tuple, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Optional, Union import matplotlib as mpl import numpy as np diff --git a/src/squidpy/pl/_var_by_distance.py b/src/squidpy/pl/_var_by_distance.py index 720eafd9..f0d511ab 100644 --- a/src/squidpy/pl/_var_by_distance.py +++ b/src/squidpy/pl/_var_by_distance.py @@ -1,8 +1,9 @@ from __future__ import annotations +from collections.abc import Mapping, Sequence from pathlib import Path from types import MappingProxyType -from typing import Any, List, Mapping, Optional, Sequence, Tuple, Union +from typing import Any import matplotlib.cm as cm import matplotlib.colors as colors @@ -97,7 +98,7 @@ def var_by_distance( df[var] = np.array(adata[:, var].X.A) if issparse(adata[:, var].X) else np.array(adata[:, var].X) # add var column # if several variables are plotted, make a panel grid - if isinstance(var, List): + if isinstance(var, list): fig, grid = _panel_grid( hspace=0.25, wspace=0.75 / rcParams["figure.figsize"][0] + 0.02, ncols=4, num_panels=len(var) ) diff --git a/src/squidpy/read/__init__.py b/src/squidpy/read/__init__.py index 7c8d4810..ae3eb736 100644 --- a/src/squidpy/read/__init__.py +++ b/src/squidpy/read/__init__.py @@ -1 +1,3 @@ +from __future__ import annotations + from squidpy.read._read import nanostring, visium, vizgen diff --git a/src/squidpy/read/_utils.py b/src/squidpy/read/_utils.py index c5c82097..1114c043 100644 --- a/src/squidpy/read/_utils.py +++ b/src/squidpy/read/_utils.py @@ -1,7 +1,7 @@ from __future__ import annotations from pathlib import Path -from typing import Any, Optional, Tuple +from typing import Any import numpy as np from anndata import AnnData, read_text diff --git a/src/squidpy/tl/__init__.py b/src/squidpy/tl/__init__.py index a7db3dcb..edaa3182 100644 --- a/src/squidpy/tl/__init__.py +++ b/src/squidpy/tl/__init__.py @@ -1,3 +1,5 @@ """The design matrix module.""" +from __future__ import annotations + from squidpy.tl._var_by_distance import var_by_distance diff --git a/src/squidpy/tl/_var_by_distance.py b/src/squidpy/tl/_var_by_distance.py index 5f1147c3..38d62318 100644 --- a/src/squidpy/tl/_var_by_distance.py +++ b/src/squidpy/tl/_var_by_distance.py @@ -2,7 +2,7 @@ from functools import reduce from itertools import product -from typing import Any, Dict, List, Tuple, Union # noqa: F401 +from typing import Any import numpy as np import pandas as pd diff --git a/tests/conftest.py b/tests/conftest.py index 84762601..363c44a7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,10 +4,10 @@ import sys import warnings from abc import ABC, ABCMeta +from collections.abc import Callable, Mapping, Sequence from functools import wraps from itertools import product from pathlib import Path -from typing import Callable, Mapping, Optional, Sequence, Tuple import anndata as ad import matplotlib diff --git a/tests/datasets/test_dataset.py b/tests/datasets/test_dataset.py index d84dd1cb..109a27ce 100644 --- a/tests/datasets/test_dataset.py +++ b/tests/datasets/test_dataset.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import warnings from http.client import RemoteDisconnected from pathlib import Path diff --git a/tests/datasets/test_download_visium_dataset.py b/tests/datasets/test_download_visium_dataset.py index 69d3aa24..887bd392 100644 --- a/tests/datasets/test_download_visium_dataset.py +++ b/tests/datasets/test_download_visium_dataset.py @@ -2,6 +2,8 @@ Tests to make sure the Visium example datasets load. """ +from __future__ import annotations + import subprocess from pathlib import Path diff --git a/tests/graph/test_ligrec.py b/tests/graph/test_ligrec.py index c6e1b35a..dc087846 100644 --- a/tests/graph/test_ligrec.py +++ b/tests/graph/test_ligrec.py @@ -1,7 +1,10 @@ +from __future__ import annotations + import sys +from collections.abc import Mapping, Sequence from itertools import product from time import time -from typing import TYPE_CHECKING, Mapping, Optional, Sequence, Tuple +from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -223,7 +226,7 @@ def test_return_no_write(self, adata: AnnData, interactions: Interactions_t): assert isinstance(r["metadata"], pd.DataFrame) @pytest.mark.parametrize("fdr_method", [None, "fdr_bh"]) - def test_pvals_in_correct_range(self, adata: AnnData, interactions: Interactions_t, fdr_method: Optional[str]): + def test_pvals_in_correct_range(self, adata: AnnData, interactions: Interactions_t, fdr_method: str | None): r = ligrec( adata, _CK, diff --git a/tests/graph/test_nhood.py b/tests/graph/test_nhood.py index 597f0fd1..d8448ba2 100644 --- a/tests/graph/test_nhood.py +++ b/tests/graph/test_nhood.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np import pandas as pd import pytest diff --git a/tests/graph/test_ppatterns.py b/tests/graph/test_ppatterns.py index d10f30ed..01c26e8e 100644 --- a/tests/graph/test_ppatterns.py +++ b/tests/graph/test_ppatterns.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Any, Literal import numpy as np diff --git a/tests/graph/test_ripley.py b/tests/graph/test_ripley.py index c0e426b6..f74b7349 100644 --- a/tests/graph/test_ripley.py +++ b/tests/graph/test_ripley.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np import pytest from anndata import AnnData diff --git a/tests/graph/test_sepal.py b/tests/graph/test_sepal.py index 0cb3d1f3..28c76c2b 100644 --- a/tests/graph/test_sepal.py +++ b/tests/graph/test_sepal.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np from anndata import AnnData from pandas.testing import assert_frame_equal diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index e53e5100..c3d69875 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -1,4 +1,4 @@ -from typing import Tuple +from __future__ import annotations import anndata as ad import numpy as np diff --git a/tests/graph/test_utils.py b/tests/graph/test_utils.py index 9b8915c0..c0eeb364 100644 --- a/tests/graph/test_utils.py +++ b/tests/graph/test_utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np import pandas as pd import pytest diff --git a/tests/image/test_container.py b/tests/image/test_container.py index 5273fea8..815360f2 100644 --- a/tests/image/test_container.py +++ b/tests/image/test_container.py @@ -1,9 +1,12 @@ +from __future__ import annotations + import subprocess from collections import defaultdict +from collections.abc import Sequence, Set from html.parser import HTMLParser from itertools import permutations from pathlib import Path -from typing import Any, List, Optional, Sequence, Set, Tuple, Union +from typing import Any import anndata as ad import dask.array as da @@ -135,7 +138,7 @@ def test_load_zarr_2_objects_can_overwrite_store(self, tmpdir): ((100, 200, 3), (100, 200)), ], ) - def test_add_img(self, shape1: Tuple[int, ...], shape2: Tuple[int, ...]): + def test_add_img(self, shape1: tuple[int, ...], shape2: tuple[int, ...]): img_orig = np.random.randint(low=0, high=255, size=shape1, dtype=np.uint8) cont = ImageContainer(img_orig, layer="img_orig") @@ -152,7 +155,7 @@ def test_add_img_invalid_zdim(self, cont: ImageContainer): @pytest.mark.parametrize("ext", ["jpg", "png"]) @pytest.mark.parametrize("shape", [(100, 200, 3), (100, 200, 1)]) - def test_load_ext(self, shape: Tuple[int, ...], ext: str, tmpdir): + def test_load_ext(self, shape: tuple[int, ...], ext: str, tmpdir): fname = tmpdir / f"tmp.{ext}" if shape == (100, 200, 1): @@ -168,7 +171,7 @@ def test_load_ext(self, shape: Tuple[int, ...], ext: str, tmpdir): np.testing.assert_array_equal(cont["image"].values.squeeze(), gt.squeeze()) @pytest.mark.parametrize("shape", [(100, 200, 3), (100, 200, 1), (10, 100, 200, 1)]) - def test_load_tiff(self, shape: Tuple[int, ...], tmpdir): + def test_load_tiff(self, shape: tuple[int, ...], tmpdir): img_orig = np.random.randint(low=0, high=255, size=shape, dtype=np.uint8) fname = tmpdir / "tmp.tiff" tifffile.imwrite(fname, img_orig) @@ -181,7 +184,7 @@ def test_load_tiff(self, shape: Tuple[int, ...], tmpdir): np.testing.assert_array_equal(np.squeeze(cont["image"]), np.squeeze(img_orig)) @pytest.mark.parametrize("dims", [("y", "x", "z", "c"), ("foo", "bar", "faa", "baz")]) - def test_load_netcdf(self, tmpdir, dims: Tuple[str, ...]): + def test_load_netcdf(self, tmpdir, dims: tuple[str, ...]): arr = np.random.normal(size=(100, 10, 1, 4)) ds = xr.Dataset({"quux": xr.DataArray(arr, dims=dims)}) fname = tmpdir / "tmp.nc" @@ -200,7 +203,7 @@ def test_load_netcdf(self, tmpdir, dims: Tuple[str, ...]): @pytest.mark.parametrize( "array", [np.zeros((10, 10, 3), dtype=np.uint8), np.random.rand(10, 10, 1).astype(np.float32)] ) - def test_array_dtypes(self, array: Union[np.ndarray, xr.DataArray]): + def test_array_dtypes(self, array: np.ndarray | xr.DataArray): img = ImageContainer(array) np.testing.assert_array_equal(np.squeeze(img["image"].data), np.squeeze(array)) assert img["image"].data.dtype == array.dtype @@ -284,7 +287,7 @@ def test_delete(self, small_cont_1c: ImageContainer): del small_cont_1c["image"] @pytest.mark.parametrize("img_key", [None, "hires", "lowres"]) - def test_read_from_adata(self, adata: AnnData, img_key: Optional[str]): + def test_read_from_adata(self, adata: AnnData, img_key: str | None): img = sq.im.ImageContainer.from_adata(adata, img_key=img_key) if img_key is None: img_key = "hires" @@ -296,7 +299,7 @@ def test_read_from_adata(self, adata: AnnData, img_key: Optional[str]): np.testing.assert_array_equal(img[img_key].shape, shape) @pytest.mark.parametrize("scale", [None, 42]) - def test_read_from_adata_scalefactor(self, adata: AnnData, scale: Optional[int]): + def test_read_from_adata_scalefactor(self, adata: AnnData, scale: int | None): img_key = "lowres" library_id = Key.uns.library_id(adata, Key.uns.spatial) del adata.uns[Key.uns.spatial][library_id]["scalefactors"][f"tissue_{img_key}_scalef"] @@ -393,9 +396,7 @@ def test_lazy_scale(self, as_dask: bool): @pytest.mark.parametrize("dy", [-10, 25, 0.3]) @pytest.mark.parametrize("dx", [-10, 30, 0.5]) - def test_crop_corner_size( - self, small_cont_1c: ImageContainer, dy: Optional[Union[int, float]], dx: Optional[Union[int, float]] - ): + def test_crop_corner_size(self, small_cont_1c: ImageContainer, dy: int | float | None, dx: int | float | None): crop = small_cont_1c.crop_corner(dy, dx, size=20) # original coordinates ody, odx = max(dy, 0), max(dx, 0) @@ -431,7 +432,7 @@ def test_test_crop_corner_cval(self, cval: float): np.testing.assert_array_equal(crop["image"].data[-10:, -10:], cval) @pytest.mark.parametrize("size", [(10, 10), (10, 11)]) - def test_crop_corner_mask_circle(self, small_cont_1c: ImageContainer, size: Tuple[int, int]): + def test_crop_corner_mask_circle(self, small_cont_1c: ImageContainer, size: tuple[int, int]): if size[0] != size[1]: with pytest.raises(ValueError, match=r"Masking circle is only"): small_cont_1c.crop_corner(0, 0, size=size, mask_circle=True, cval=np.nan) @@ -444,9 +445,7 @@ def test_crop_corner_mask_circle(self, small_cont_1c: ImageContainer, size: Tupl @pytest.mark.parametrize("ry", [23, 1.0]) @pytest.mark.parametrize("rx", [30, 0.5]) - def test_crop_center_radius( - self, small_cont_1c: ImageContainer, ry: Optional[Union[int, float]], rx: Optional[Union[int, float]] - ): + def test_crop_center_radius(self, small_cont_1c: ImageContainer, ry: int | float | None, rx: int | float | None): crop = small_cont_1c.crop_center(0, 0, radius=(ry, rx)) sy = int(ry * small_cont_1c.shape[0]) if isinstance(ry, float) else ry sx = int(rx * small_cont_1c.shape[1]) if isinstance(rx, float) else rx @@ -456,7 +455,7 @@ def test_crop_center_radius( @pytest.mark.parametrize("squeeze", [False, True]) @pytest.mark.parametrize("as_array", [False, True, "image", ["image", "baz"]]) def test_equal_crops_as_array(self, small_cont: ImageContainer, as_array: bool, squeeze: bool): - def assert_shape(expected: xr.DataArray, actual: Union[np.ndarray, xr.DataArray]): + def assert_shape(expected: xr.DataArray, actual: np.ndarray | xr.DataArray): expected_shape = list(expected.shape) expected_shape[:2] = [11, 11] # because crop is 11x11 if squeeze: @@ -517,7 +516,7 @@ def test_spot_crops_as_array_return_obs( assert crop.shape == size @pytest.mark.parametrize("n_names", [None, 4]) - def test_spot_crops_obs_names(self, adata: AnnData, cont: ImageContainer, n_names: Optional[int]): + def test_spot_crops_obs_names(self, adata: AnnData, cont: ImageContainer, n_names: int | None): obs = adata.obs_names[:n_names] if isinstance(n_names, int) else adata.obs_names crops = list(cont.generate_spot_crops(adata, obs_names=obs)) @@ -678,7 +677,7 @@ def test_get_size(self): @pytest.mark.parametrize("sx", [-1, -1.0, 0.5, 10]) @pytest.mark.parametrize("sy", [-1, -1.0, 0.5, 10]) - def test_to_pixel_space(self, sy: Union[int, float], sx: Union[int, float]): + def test_to_pixel_space(self, sy: int | float, sx: int | float): cont = ImageContainer(np.empty((10, 10))) if (isinstance(sy, float) and sy < 0) or (isinstance(sx, float) and sx < 0): @@ -698,7 +697,7 @@ def test_to_pixel_space(self, sy: Union[int, float], sx: Union[int, float]): @pytest.mark.parametrize("channel", [None, 0]) @pytest.mark.parametrize("copy", [False, True]) - def test_apply(self, copy: bool, channel: Optional[int]): + def test_apply(self, copy: bool, channel: int | None): cont = ImageContainer(np.random.normal(size=(100, 100, 3))) orig = cont.copy() @@ -718,7 +717,7 @@ def test_apply(self, copy: bool, channel: Optional[int]): np.testing.assert_allclose(data.values[..., 0], orig["image"].values[..., channel] + 42) @pytest.mark.parametrize("depth", [None, (30, 30, 0)]) - def test_apply_overlap(self, small_cont: ImageContainer, mocker: MockerFixture, depth: Optional[Tuple[int, ...]]): + def test_apply_overlap(self, small_cont: ImageContainer, mocker: MockerFixture, depth: tuple[int, ...] | None): if depth is None: kwargs = {} spy = mocker.spy(da, "map_blocks") @@ -732,9 +731,7 @@ def test_apply_overlap(self, small_cont: ImageContainer, mocker: MockerFixture, @pytest.mark.parametrize("copy", [False, True]) @pytest.mark.parametrize("chunks", [25, (50, 50, 1, 3), "auto"]) @pytest.mark.parametrize("lazy", [False, True]) - def test_apply_dask( - self, small_cont: ImageContainer, copy: bool, chunks: Union[int, Tuple[int, ...], str], lazy: bool - ): + def test_apply_dask(self, small_cont: ImageContainer, copy: bool, chunks: int | tuple[int, ...] | str, lazy: bool): def func(chunk: np.ndarray) -> np.ndarray: if isinstance(chunks, tuple): np.testing.assert_array_equal(chunk.shape, chunks) @@ -758,7 +755,7 @@ def func(chunk: np.ndarray) -> np.ndarray: @pytest.mark.parametrize("as_dask", [False, True]) def test_apply_passes_correct_array_type(self, as_dask: bool): - def func(arr: Union[np.ndarray, da.Array]): + def func(arr: np.ndarray | da.Array): if as_dask: assert isinstance(arr, da.Array) else: @@ -933,7 +930,7 @@ def test_generate_spot_crops(self): @pytest.mark.parametrize("channel", [None, 0]) @pytest.mark.parametrize("copy", [False, True]) @pytest.mark.parametrize("library_id", [["l1"], ["l2"], ["l1", "l2", "l3"], None]) - def test_apply(self, copy: bool, channel: Optional[int], library_id: Optional[Union[list[str], str]]): + def test_apply(self, copy: bool, channel: int | None, library_id: list[str] | str | None): cont = ImageContainer( np.random.normal(size=(100, 100, 3, 2)), dims=("y", "x", "z", "channels"), library_id=["l1", "l2", "l3"] ) @@ -976,7 +973,7 @@ def test_apply(self, copy: bool, channel: Optional[int], library_id: Optional[Un class TestExplicitDims: @pytest.mark.parametrize("dims", list(permutations(["y", "x", "z", "c"]))) - def test_explicit_dims(self, dims: Tuple[str, str, str, str]): + def test_explicit_dims(self, dims: tuple[str, str, str, str]): shape = (2, 3, 4, 5) img = ImageContainer(np.random.normal(size=shape), dims=dims) @@ -1087,7 +1084,7 @@ def test_get_from_xarray(self): @pytest.mark.parametrize("library_id", ["a", ["b", "a"], ["c"]]) @pytest.mark.parametrize("empty", [False, True]) - def test_get_library_ids(self, empty: bool, library_id: Union[str, Sequence[str]]): + def test_get_library_ids(self, empty: bool, library_id: str | Sequence[str]): img = ImageContainer() if not empty: img.add_img(np.random.normal(size=(2, 3, 4, 5)), dims=["z", "y", "x", "c"], library_id=["a", "b"]) diff --git a/tests/image/test_features.py b/tests/image/test_features.py index b12d8a75..75a6d80e 100644 --- a/tests/image/test_features.py +++ b/tests/image/test_features.py @@ -1,4 +1,6 @@ -from typing import Sequence, Tuple +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import pandas as pd diff --git a/tests/image/test_io.py b/tests/image/test_io.py index 72f5ad7a..cc63478c 100644 --- a/tests/image/test_io.py +++ b/tests/image/test_io.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Tuple, Union +from __future__ import annotations import dask.array as da import numpy as np @@ -81,7 +81,7 @@ def test_infer_dimensions(self, shape: tuple[int, ...], infer_dim: str, mocker: np.testing.assert_array_equal(actual_shape, shape) @pytest.mark.parametrize("chunks", [100, (1, 100, 100, 3), "auto", None, {"y": 100, "x": 100}]) - def test_lazy_load_image(self, chunks: Optional[Union[int, tuple[int, ...], str, dict[str, int]]], tmpdir): + def test_lazy_load_image(self, chunks: int | tuple[int, ...] | str | dict[str, int] | None, tmpdir): path = str(tmpdir / "img.tiff") img = self._create_image(path, (256, 256, 3)) diff --git a/tests/image/test_processing.py b/tests/image/test_processing.py index 5ac9994a..b2b4d9ca 100644 --- a/tests/image/test_processing.py +++ b/tests/image/test_processing.py @@ -1,4 +1,6 @@ -from typing import Callable, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence import dask.array as da import numpy as np @@ -15,7 +17,7 @@ def test_invalid_layer(self, small_cont: ImageContainer): @pytest.mark.parametrize("dy", [25, 0.3, None]) @pytest.mark.parametrize("dx", [30, 0.5, None]) - def test_size(self, small_cont: ImageContainer, dy: Optional[Union[int, float]], dx: Optional[Union[int, float]]): + def test_size(self, small_cont: ImageContainer, dy: int | float | None, dx: int | float | None): res = process(small_cont, method="smooth", copy=True) key = Key.img.process("smooth", "image") @@ -23,7 +25,7 @@ def test_size(self, small_cont: ImageContainer, dy: Optional[Union[int, float]], np.testing.assert_array_equal(res[key].dims, small_cont["image"].dims) @pytest.mark.parametrize("method", ["smooth", "gray", lambda arr: arr]) - def test_method(self, small_cont: ImageContainer, method: Union[str, Callable[[np.ndarray], np.ndarray]]): + def test_method(self, small_cont: ImageContainer, method: str | Callable[[np.ndarray], np.ndarray]): res = process(small_cont, method=method, copy=True) key = Key.img.process(method, "image") @@ -35,7 +37,7 @@ def test_method(self, small_cont: ImageContainer, method: Union[str, Callable[[n assert not np.all(np.allclose(small_cont["image"].values, res[key].values)) @pytest.mark.parametrize("method", ["smooth", "gray", lambda arr: arr[..., 0]]) - def test_channel_dim(self, small_cont: ImageContainer, method: Union[str, Callable[[np.ndarray], np.ndarray]]): + def test_channel_dim(self, small_cont: ImageContainer, method: str | Callable[[np.ndarray], np.ndarray]): res = process(small_cont, method=method, copy=True, channel_dim="foo") key = Key.img.process(method, "image") @@ -52,7 +54,7 @@ def test_gray_not_rgb(self, small_cont_1c: ImageContainer): process(small_cont_1c, method="gray") @pytest.mark.parametrize("key_added", [None, "foo"]) - def test_key_added(self, small_cont: ImageContainer, key_added: Optional[str]): + def test_key_added(self, small_cont: ImageContainer, key_added: str | None): res = process(small_cont, method="smooth", copy=False, layer_added=key_added, layer="image") assert res is None @@ -87,7 +89,7 @@ def test_apply_kwargs(self, small_cont: ImageContainer, mocker: MockerFixture): @pytest.mark.parametrize("chunks", [25, (50, 50, 1, 3), "auto"]) @pytest.mark.parametrize("lazy", [False, True]) def test_dask_processing( - self, small_cont: ImageContainer, dask_input: bool, chunks: Union[int, tuple[int, ...], str], lazy: bool + self, small_cont: ImageContainer, dask_input: bool, chunks: int | tuple[int, ...] | str, lazy: bool ): def func(chunk: np.ndarray): if isinstance(chunks, tuple): @@ -114,7 +116,7 @@ def func(chunk: np.ndarray): assert isinstance(small_cont["bar"].data, np.ndarray) @pytest.mark.parametrize("library_id", [None, "3", ["1", "2"]]) - def test_library_id(self, cont_4d: ImageContainer, library_id: Optional[Union[str, Sequence[str]]]): + def test_library_id(self, cont_4d: ImageContainer, library_id: str | Sequence[str] | None): def func(arr: np.ndarray): if library_id is None: assert arr.shape == cont_4d["image"].shape diff --git a/tests/image/test_segmentation.py b/tests/image/test_segmentation.py index 9744acc7..e39e89c8 100644 --- a/tests/image/test_segmentation.py +++ b/tests/image/test_segmentation.py @@ -1,4 +1,6 @@ -from typing import Callable, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence import dask.array as da import numpy as np @@ -59,7 +61,7 @@ def test_segment_container(self): class TestWatershed: @pytest.mark.parametrize("thresh", [None, 0.1, 0.5, 1.0]) - def test_threshold(self, thresh: Optional[float], mocker: MockerFixture): + def test_threshold(self, thresh: float | None, mocker: MockerFixture): img = np.zeros((100, 200), dtype=np.float64) img[2:10, 2:10] = 1.0 img[30:34, 10:16] = 1.0 @@ -83,7 +85,7 @@ def test_invalid_layer(self, small_cont: ImageContainer): segment(small_cont, layer="foobar") @pytest.mark.parametrize("method", ["watershed", dummy_segment]) - def test_method(self, small_cont: ImageContainer, method: Union[str, Callable]): + def test_method(self, small_cont: ImageContainer, method: str | Callable): res = segment(small_cont, method=method, copy=True) assert isinstance(res, ImageContainer) @@ -99,7 +101,7 @@ def test_method(self, small_cont: ImageContainer, method: Union[str, Callable]): @pytest.mark.parametrize("dy", [11, 0.5, None]) @pytest.mark.parametrize("dx", [15, 0.1, None]) - def test_size(self, small_cont: ImageContainer, dy: Optional[Union[int, float]], dx: Optional[Union[int, float]]): + def test_size(self, small_cont: ImageContainer, dy: int | float | None, dx: int | float | None): res = segment(small_cont, size=(dy, dx), copy=True) assert isinstance(res, ImageContainer) @@ -127,7 +129,7 @@ def func(arr: np.ndarray): assert small_cont["seg"].dtype == _SEG_DTYPE @pytest.mark.parametrize("key_added", [None, "foo"]) - def test_key_added(self, small_cont: ImageContainer, key_added: Optional[str]): + def test_key_added(self, small_cont: ImageContainer, key_added: str | None): res = segment(small_cont, copy=False, layer="image", layer_added=key_added) assert res is None @@ -148,7 +150,7 @@ def func(chunk: np.ndarray, sentinel: bool = False): @pytest.mark.parametrize("chunks", [25, (50, 50, 1), "auto"]) @pytest.mark.parametrize("lazy", [False, True]) def test_dask_segment( - self, small_cont: ImageContainer, dask_input: bool, chunks: Union[int, tuple[int, ...], str], lazy: bool + self, small_cont: ImageContainer, dask_input: bool, chunks: int | tuple[int, ...] | str, lazy: bool ): def func(chunk: np.ndarray): if isinstance(chunks, tuple): @@ -216,7 +218,7 @@ def func(chunk: np.ndarray): np.testing.assert_array_equal(small_cont["bar"].values, expected) @pytest.mark.parametrize("size", [None, 11]) - def test_watershed_works(self, size: Optional[int]): + def test_watershed_works(self, size: int | None): img_orig = np.zeros((100, 200, 30), dtype=np.float64) img_orig[2:10, 2:10] = 1.0 img_orig[30:34, 10:16] = 1.0 @@ -239,7 +241,7 @@ def test_watershed_works(self, size: Optional[int]): # but outside, the assertion fails, as it should @pytest.mark.parametrize("library_id", [None, "3", ["1", "2"]]) - def test_library_id(self, cont_4d: ImageContainer, library_id: Optional[Union[str, Sequence[str]]]): + def test_library_id(self, cont_4d: ImageContainer, library_id: str | Sequence[str] | None): def func(arr: np.ndarray): assert arr.shape == cont_4d.shape + (1,) return np.ones(arr[..., 0].shape, dtype=_SEG_DTYPE) diff --git a/tests/plotting/test_graph.py b/tests/plotting/test_graph.py index 6620fbd4..e89ce701 100644 --- a/tests/plotting/test_graph.py +++ b/tests/plotting/test_graph.py @@ -1,5 +1,7 @@ +from __future__ import annotations + +from collections.abc import Mapping from copy import deepcopy -from typing import Mapping import matplotlib.pyplot as plt import numpy as np diff --git a/tests/plotting/test_image.py b/tests/plotting/test_image.py index 258c8772..1f53504f 100644 --- a/tests/plotting/test_image.py +++ b/tests/plotting/test_image.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import matplotlib.pyplot as plt import numpy as np import pandas as pd diff --git a/tests/plotting/test_interactive.py b/tests/plotting/test_interactive.py index af45d50e..94807a11 100644 --- a/tests/plotting/test_interactive.py +++ b/tests/plotting/test_interactive.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import platform import sys -from typing import Tuple import matplotlib.pyplot as plt import numpy as np diff --git a/tests/plotting/test_spatial_static.py b/tests/plotting/test_spatial_static.py index 5adc35d3..04be1be1 100644 --- a/tests/plotting/test_spatial_static.py +++ b/tests/plotting/test_spatial_static.py @@ -1,8 +1,8 @@ from __future__ import annotations import platform +from collections.abc import Sequence from functools import partial -from typing import Sequence import matplotlib.pyplot as plt import numpy as np diff --git a/tests/plotting/test_var_by_distance_plot.py b/tests/plotting/test_var_by_distance_plot.py index b435d300..0ff18d88 100644 --- a/tests/plotting/test_var_by_distance_plot.py +++ b/tests/plotting/test_var_by_distance_plot.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import scanpy as sc from anndata import AnnData from squidpy import pl, tl diff --git a/tests/read/test_visium.py b/tests/read/test_visium.py index a4a6e0d3..a92b488a 100644 --- a/tests/read/test_visium.py +++ b/tests/read/test_visium.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from anndata.tests.helpers import assert_adata_equal from squidpy._constants._pkg_constants import Key from squidpy.read import visium diff --git a/tests/tools/test_var_by_distance.py b/tests/tools/test_var_by_distance.py index 9da7046b..ca039293 100644 --- a/tests/tools/test_var_by_distance.py +++ b/tests/tools/test_var_by_distance.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import List - import pytest from anndata import AnnData from squidpy.tl import var_by_distance