From 89ea7d20328c15f806b5d5e7ac7d24a2a7473302 Mon Sep 17 00:00:00 2001 From: Giovanni Palla <25887487+giovp@users.noreply.github.com> Date: Fri, 30 Aug 2024 23:08:14 +0200 Subject: [PATCH 1/5] remove napari from install (#877) * remove napari from install * fix tox * remove qt * update pre-commit * fix mypy * cleanup pre-commit * add napari warning and release note * fix pre-commit * fixes to docs * fix mypy * fix release note * update --- .github/workflows/test.yml | 3 +- .pre-commit-config.yaml | 58 ++++--------------- .prettierignore | 1 - .scripts/ci/download_data.py | 3 +- docs/classes.rst | 2 +- docs/index.rst | 11 ++++ docs/release/notes-1.6.1.rst | 9 +++ pyproject.toml | 13 ++--- src/squidpy/_utils.py | 34 +++++++---- src/squidpy/gr/_build.py | 4 +- src/squidpy/gr/_nhood.py | 26 +++++++-- src/squidpy/gr/_ppatterns.py | 22 ++++--- src/squidpy/gr/_sepal.py | 6 +- src/squidpy/im/_container.py | 38 +++++++++--- src/squidpy/im/_feature_mixin.py | 19 +++++- src/squidpy/im/_segment.py | 7 ++- src/squidpy/pl/__init__.py | 3 +- src/squidpy/pl/_interactive/__init__.py | 2 +- src/squidpy/pl/_interactive/interactive.py | 9 +++ src/squidpy/pl/_ligrec.py | 24 ++++++-- src/squidpy/pl/_utils.py | 48 +++++++++++---- tests/conftest.py | 3 +- tests/datasets/test_dataset.py | 3 +- .../datasets/test_download_visium_dataset.py | 1 + tests/graph/test_ligrec.py | 1 + tests/graph/test_nhood.py | 1 + tests/graph/test_ppatterns.py | 1 + tests/graph/test_ripley.py | 1 + tests/graph/test_sepal.py | 1 + tests/graph/test_spatial_neighbors.py | 1 + tests/graph/test_utils.py | 1 + tests/image/test_container.py | 3 +- tests/image/test_features.py | 1 + tests/image/test_io.py | 1 + tests/image/test_processing.py | 1 + tests/image/test_segmentation.py | 1 + tests/plotting/test_graph.py | 2 +- tests/plotting/test_image.py | 4 +- tests/plotting/test_interactive.py | 2 +- tests/plotting/test_spatial_static.py | 2 +- tests/plotting/test_var_by_distance_plot.py | 2 +- tests/read/test_visium.py | 1 + tests/tools/test_var_by_distance.py | 1 + tox.ini | 6 +- 44 files changed, 256 insertions(+), 127 deletions(-) delete mode 100644 .prettierignore create mode 100644 docs/release/notes-1.6.1.rst diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 57606729..a7d9bc14 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,7 +92,8 @@ jobs: tox -vv - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3.1.1 + uses: codecov/codecov-action@v4 with: name: coverage verbose: true + token: ${{ secrets.CODECOV_TOKEN }} # required diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5441645..6318f0fa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,59 +6,21 @@ default_stages: - push minimum_pre_commit_version: 2.9.3 repos: - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.0 - hooks: - - 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/pre-commit/mirrors-prettier - rev: v4.0.0-alpha.8 - hooks: - - id: prettier - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: detect-private-key - - id: check-merge-conflict - - id: check-ast - - id: check-symlinks - - id: check-added-large-files - - id: check-executables-have-shebangs - - id: fix-encoding-pragma - args: [--remove] - - id: end-of-file-fixer - - id: mixed-line-ending - args: [--fix=lf] - - id: trailing-whitespace - exclude: ^.bumpversion.cfg$ - - id: name-tests-test - args: [--django] - - id: check-case-conflict - - id: check-docstring-first - - id: check-yaml - - id: check-toml - - id: requirements-txt-fixer - - repo: https://github.com/jumanjihouse/pre-commit-hooks - rev: 3.0.0 - hooks: - - id: script-must-have-extension - name: Check executable files use .sh extension - types: [shell, executable] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.4 + rev: v0.6.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 + - repo: https://github.com/asottile/blacken-docs + rev: 1.18.0 + hooks: + - id: blacken-docs + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.11.1 hooks: - - id: python-no-eval - - id: python-use-type-annotations - - id: python-check-blanket-noqa - - id: rst-backticks - - id: rst-directive-colons - - id: rst-inline-touching-normal + - id: mypy + additional_dependencies: [numpy, pandas, types-requests] + exclude: .scripts/ci/download_data.py|squidpy/datasets/_(dataset|image).py # See https://github.com/ diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index a188e069..00000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -docs/* diff --git a/.scripts/ci/download_data.py b/.scripts/ci/download_data.py index 177a751d..fd3dd4fe 100644 --- a/.scripts/ci/download_data.py +++ b/.scripts/ci/download_data.py @@ -30,9 +30,10 @@ def _maybe_download_data(func_name: str, path: Path) -> Any: def main(args: argparse.Namespace) -> None: - import squidpy as sq from anndata import AnnData + import squidpy as sq + all_datasets = sq.datasets._dataset.__all__ + sq.datasets._image.__all__ all_extensions = ["h5ad"] * len(sq.datasets._dataset.__all__) + ["tiff"] * len(sq.datasets._image.__all__) diff --git a/docs/classes.rst b/docs/classes.rst index ec0c699f..a2ada89e 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -7,6 +7,6 @@ Classes :toctree: classes im.ImageContainer - pl.Interactive im.SegmentationWatershed im.SegmentationCustom + .. pl.Interactive diff --git a/docs/index.rst b/docs/index.rst index 5239c283..958d46a6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,17 @@ tissue images if available. :align: center :target: https://doi.org/10.1038/s41592-021-01358-2 +.. warning:: + 🚨🚨🚨 **Warning!** 🚨🚨🚨 + + The original napari-plugin of Squidpy has been moved to `napari-spatialdata `_. + + All the functionalities previously available are also implemented in the new plugin, which also has many additional new features. + + You can find a rich set of `documentation and examples `_, and we suggest starting with this `tutorial `_. + + If you are new to SpatialData, we invite you to take a look at the documentation `here `_. + Manuscript ---------- Please see our manuscript :cite:`palla:22` in **Nature Methods** to learn more. diff --git a/docs/release/notes-1.6.1.rst b/docs/release/notes-1.6.1.rst new file mode 100644 index 00000000..94cb64a2 --- /dev/null +++ b/docs/release/notes-1.6.1.rst @@ -0,0 +1,9 @@ +Squidpy 1.6.1 (2024-08-23) +========================== + +Maintenance +----------- + +- The Squidpy `interactive` module has been removed. Please use the ``napari-spatialdata`` plugin instead. You can find it `here `__ `@giovp `__ + `#877 `__ + diff --git a/pyproject.toml b/pyproject.toml index bdf4b24e..c340b882 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,10 +76,6 @@ dependencies = [ ] [project.optional-dependencies] -interactive = [ - "napari[pyqt5]==0.4.15", - "pydantic==1.10.13" -] dev = [ "pre-commit>=3.0.0", "tox>=4.0.0", @@ -214,10 +210,11 @@ required-imports = ["from __future__ import annotations"] "*/__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"] +"src/squidpy/pl/_ligrec.py"= ["D","B"] +"src/squidpy/_constants/_pkg_constants.py"= ["D101","D102","D106"] +"src/squidpy/_constants/_constants.py"= ["D101"] +"src/squidpy/pl/_interactive/_widgets.py"= ["D"] +"src/squidpy/pl/_interactive/interactive.py"= ["F","E"] ".scripts/ci/download_data.py"= ["D","B"] # "squidpy/*.py"= ["RST303"] diff --git a/src/squidpy/_utils.py b/src/squidpy/_utils.py index fb07dce0..5aa65c54 100644 --- a/src/squidpy/_utils.py +++ b/src/squidpy/_utils.py @@ -37,20 +37,18 @@ def wrapper(*args: Any, **kw: Any) -> Any: return wrapper -try: - from numpy.typing import NDArray +from numpy.typing import NDArray - NDArrayA = NDArray[Any] -except (ImportError, TypeError): - NDArray = np.ndarray # type: ignore[misc] - NDArrayA = np.ndarray # type: ignore[misc] +NDArrayA = NDArray[Any] class SigQueue(Queue["Signal"] if TYPE_CHECKING else Queue): # type: ignore[misc] """Signalling queue.""" -def _unique_order_preserving(iterable: Iterable[Hashable]) -> tuple[list[Hashable], set[Hashable]]: +def _unique_order_preserving( + iterable: Iterable[Hashable], +) -> tuple[list[Hashable], set[Hashable]]: """Remove items from an iterable while preserving the order.""" seen: set[Hashable] = set() seen_add = seen.add @@ -124,7 +122,12 @@ def parallelize( else: tqdm = None - def runner(iterable: Iterable[Any], *args: Any, queue: SigQueue | None = None, **kwargs: Any) -> list[Any]: + def runner( + iterable: Iterable[Any], + *args: Any, + queue: SigQueue | None = None, + **kwargs: Any, + ) -> list[Any]: result: list[Any] = [] for it in iterable: @@ -200,7 +203,10 @@ def wrapper(*args: Any, **kwargs: Any) -> Any: col_len = len(collection) step = int(np.ceil(len(collection) / n_split)) collections = list( - filter(len, (collection[i * step : (i + 1) * step] for i in range(int(np.ceil(col_len / step))))) + filter( + len, + (collection[i * step : (i + 1) * step] for i in range(int(np.ceil(col_len / step)))), + ) ) if use_runner: @@ -289,7 +295,9 @@ def decorator(func1: Callable[..., Any]) -> Callable[..., Any]: def new_func1(*args: Any, **kwargs: Any) -> Any: warnings.simplefilter("always", DeprecationWarning) warnings.warn( - fmt1.format(name=func1.__name__, reason=reason), category=DeprecationWarning, stacklevel=2 + fmt1.format(name=func1.__name__, reason=reason), + category=DeprecationWarning, + stacklevel=2, ) warnings.simplefilter("default", DeprecationWarning) return func1(*args, **kwargs) @@ -317,7 +325,11 @@ def new_func1(*args: Any, **kwargs: Any) -> Any: @functools.wraps(func2) def new_func2(*args: Any, **kwargs: Any) -> Any: warnings.simplefilter("always", DeprecationWarning) - warnings.warn(fmt2.format(name=func2.__name__), category=DeprecationWarning, stacklevel=2) + warnings.warn( + fmt2.format(name=func2.__name__), + category=DeprecationWarning, + stacklevel=2, + ) warnings.simplefilter("default", DeprecationWarning) return func2(*args, **kwargs) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 793e4f5c..aec37ce4 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -233,11 +233,11 @@ def spatial_neighbors( if library_key is not None: mats: list[tuple[spmatrix, spmatrix]] = [] - ixs = [] # type: ignore[var-annotated] + ixs: list[int] = [] for lib in libs: ixs.extend(np.where(adata.obs[library_key] == lib)[0]) mats.append(_build_fun(adata[adata.obs[library_key] == lib])) - ixs = np.argsort(ixs) # type: ignore[assignment] # invert + ixs = np.argsort(ixs) # type: ignore[assignment] # invert Adj = block_diag([m[0] for m in mats], format="csr")[ixs, :][:, ixs] Dst = block_diag([m[1] for m in mats], format="csr")[ixs, :][:, ixs] else: diff --git a/src/squidpy/gr/_nhood.py b/src/squidpy/gr/_nhood.py index db5ce0f3..c31762a5 100644 --- a/src/squidpy/gr/_nhood.py +++ b/src/squidpy/gr/_nhood.py @@ -190,7 +190,15 @@ def nhood_enrichment( n_jobs=n_jobs, backend=backend, show_progress_bar=show_progress_bar, - )(callback=_test, indices=indices, indptr=indptr, int_clust=int_clust, libraries=libraries, n_cls=n_cls, seed=seed) + )( + callback=_test, + indices=indices, + indptr=indptr, + int_clust=int_clust, + libraries=libraries, + n_cls=n_cls, + seed=seed, + ) zscore = (count - perms.mean(axis=0)) / perms.std(axis=0) if copy: @@ -293,7 +301,13 @@ def centrality_scores( if copy: return df - _save_data(adata, attr="uns", key=Key.uns.centrality_scores(cluster_key), data=df, time=start) + _save_data( + adata, + attr="uns", + key=Key.uns.centrality_scores(cluster_key), + data=df, + time=start, + ) @d.dedent @@ -345,7 +359,7 @@ def interaction_matrix( g_data = g.data if weights else np.broadcast_to(1, shape=len(g.data)) dtype = int if pd.api.types.is_bool_dtype(g.dtype) or pd.api.types.is_integer_dtype(g.dtype) else float - output = np.zeros((n_cats, n_cats), dtype=dtype) # type: ignore[var-annotated] + output: NDArrayA = np.zeros((n_cats, n_cats), dtype=dtype) _interaction_matrix(g_data, g.indices, g.indptr, cats.cat.codes.to_numpy(), output) @@ -360,7 +374,11 @@ def interaction_matrix( @njit def _interaction_matrix( - data: NDArrayA, indices: NDArrayA, indptr: NDArrayA, cats: NDArrayA, output: NDArrayA + data: NDArrayA, + indices: NDArrayA, + indptr: NDArrayA, + cats: NDArrayA, + output: NDArrayA, ) -> NDArrayA: indices_list = np.split(indices, indptr[1:-1]) data_list = np.split(data, indptr[1:-1]) diff --git a/src/squidpy/gr/_ppatterns.py b/src/squidpy/gr/_ppatterns.py index a785706d..caa7b896 100644 --- a/src/squidpy/gr/_ppatterns.py +++ b/src/squidpy/gr/_ppatterns.py @@ -164,7 +164,7 @@ def extract_obsm(adata: AnnData, ixs: int | Sequence[int] | None) -> tuple[NDArr if layer not in adata.obsm: raise KeyError(f"Key `{layer!r}` not found in `adata.obsm`.") if ixs is None: - ixs = np.arange(adata.obsm[layer].shape[1]) # type: ignore[assignment] + ixs = np.arange(adata.obsm[layer].shape[1]) # type:ignore[assignment] ixs = list(np.ravel([ixs])) return adata.obsm[layer][:, ixs].T, ixs @@ -180,7 +180,11 @@ def extract_obsm(adata: AnnData, ixs: int | Sequence[int] | None) -> tuple[NDArr mode = SpatialAutocorr(mode) # type: ignore[assignment] if TYPE_CHECKING: assert isinstance(mode, SpatialAutocorr) - params = {"mode": mode.s, "transformation": transformation, "two_tailed": two_tailed} + params = { + "mode": mode.s, + "transformation": transformation, + "two_tailed": two_tailed, + } if mode == SpatialAutocorr.MORAN: params["func"] = _morans_i @@ -425,10 +429,10 @@ def co_occurrence( n_splits = max(min(n_splits, n_obs), 1) # split array and labels - spatial_splits = tuple(s for s in np.array_split(spatial, n_splits, axis=0) if len(s)) # type: ignore[arg-type] - labs_splits = tuple(s for s in np.array_split(labs, n_splits, axis=0) if len(s)) # type: ignore[arg-type] + spatial_splits = tuple(s for s in np.array_split(spatial, n_splits, axis=0) if len(s)) # type:ignore[arg-type] + labs_splits = tuple(s for s in np.array_split(labs, n_splits, axis=0) if len(s)) # type:ignore[arg-type] # create idx array including unique combinations and self-comparison - x, y = np.triu_indices_from(np.empty((n_splits, n_splits))) # type: ignore[arg-type] + x, y = np.triu_indices_from(np.empty((n_splits, n_splits))) # type:ignore[arg-type] idx_splits = list(zip(x, y)) n_jobs = _get_n_cores(n_jobs) @@ -457,7 +461,11 @@ def co_occurrence( return out, interval _save_data( - adata, attr="uns", key=Key.uns.co_occurrence(cluster_key), data={"occ": out, "interval": interval}, time=start + adata, + attr="uns", + key=Key.uns.co_occurrence(cluster_key), + data={"occ": out, "interval": interval}, + time=start, ) @@ -570,7 +578,7 @@ def _g_moments(w: spmatrix | NDArrayA) -> tuple[float, float, float]: # s1 t = w.transpose() + w - t2 = t.multiply(t) # type: ignore[union-attr] + t2 = t.multiply(t) # type:ignore[union-attr] s1 = t2.sum() / 2.0 # s2 diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index 57a44cfd..e279cc9c 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -182,8 +182,8 @@ def _score_helper( score, sparse = [], issparse(vals) for i in ixs: - conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() # type: ignore[union-attr] - conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() # type: ignore[union-attr] + conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() # type:ignore[union-attr] + conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() # type:ignore[union-attr] time_iter = _diffusion(conc, fun, n_iter, sat, sat_idx, unsat, unsat_idx, dt=dt, thresh=thresh) score.append(dt * time_iter) @@ -286,7 +286,7 @@ def _entropy( ) -> float: """Get entropy of an array.""" xnz = xx[xx > 0] - xs = np.sum(xnz) + xs: np.float64 = np.sum(xnz) xn = xnz / xs xl = np.log(xn) return float((-xl * xn).sum()) diff --git a/src/squidpy/im/_container.py b/src/squidpy/im/_container.py index e73b89ed..a7bd5771 100644 --- a/src/squidpy/im/_container.py +++ b/src/squidpy/im/_container.py @@ -26,7 +26,7 @@ from squidpy._constants._constants import InferDimensions from squidpy._constants._pkg_constants import Key from squidpy._docs import d, inject_docs -from squidpy._utils import NDArrayA, singledispatchmethod +from squidpy._utils import NDArrayA, deprecated, singledispatchmethod from squidpy.gr._utils import ( _assert_in_range, _assert_non_empty_sequence, @@ -166,7 +166,12 @@ def concat( prep_imgs.append(prep_img) return cls._from_dataset( - xr.concat([img.data for img in prep_imgs], dim="z", combine_attrs=combine_attrs, **kwargs) + xr.concat( + [img.data for img in prep_imgs], + dim="z", + combine_attrs=combine_attrs, + **kwargs, + ) ) @classmethod @@ -519,7 +524,10 @@ def crop_corner( ymin, xmin = self.shape coords = CropCoords( - x0=min(max(x, 0), xmin), y0=min(max(y, 0), ymin), x1=min(x + xs, xmin), y1=min(y + ys, ymin) + x0=min(max(x, 0), xmin), + y0=min(max(y, 0), ymin), + x1=min(x + xs, xmin), + y1=min(y + ys, ymin), ) if not coords.dy: @@ -553,7 +561,11 @@ def crop_corner( crop.attrs[Key.img.padding] = _NULL_PADDING return self._from_dataset( self._post_process( - data=crop, scale=scale, cval=cval, mask_circle=mask_circle, preserve_dtypes=preserve_dtypes + data=crop, + scale=scale, + cval=cval, + mask_circle=mask_circle, + preserve_dtypes=preserve_dtypes, ) ) @@ -583,7 +595,11 @@ def _rescale(arr: xr.DataArray) -> xr.DataArray: shape[-1] = arr.shape[-1] shape[-2] = arr.shape[-2] return xr.DataArray( - da.from_delayed(delayed(lambda arr: scaling_fn(arr).astype(dtype))(arr), shape=shape, dtype=dtype), + da.from_delayed( + delayed(lambda arr: scaling_fn(arr).astype(dtype))(arr), + shape=shape, + dtype=dtype, + ), dims=arr.dims, ) return xr.DataArray(scaling_fn(arr).astype(dtype), dims=arr.dims) @@ -803,7 +819,10 @@ def generate_spot_crops( # get spot diameter of current obs (might be different library ids) diameter = ( Key.uns.spot_diameter( - adata, spatial_key=spatial_key, library_id=lid, spot_diameter_key=spot_diameter_key + adata, + spatial_key=spatial_key, + library_id=lid, + spot_diameter_key=spot_diameter_key, ) * scale ) @@ -893,7 +912,9 @@ def uncrop( img = crop.data[key] # get shape for this DataArray dataset[key] = xr.DataArray( - np.zeros(shape + tuple(img.shape[2:]), dtype=img.dtype), dims=img.dims, coords=img.coords + np.zeros(shape + tuple(img.shape[2:]), dtype=img.dtype), + dims=img.dims, + coords=img.coords, ) # fill data with crops for crop in crops: @@ -1050,6 +1071,9 @@ def show( @d.get_sections(base="_interactive", sections=["Parameters"]) @d.dedent + @deprecated( + reason="The squidpy napari plugin is deprecated, please use https://github.com/scverse/napari-spatialdata", + ) def interactive( self, adata: AnnData, diff --git a/src/squidpy/im/_feature_mixin.py b/src/squidpy/im/_feature_mixin.py index d327d71c..f5149b7b 100644 --- a/src/squidpy/im/_feature_mixin.py +++ b/src/squidpy/im/_feature_mixin.py @@ -172,7 +172,13 @@ def features_histogram( features = {} for c in channels: - hist, _ = np.histogram(arr[..., c].values, bins=bins, range=v_range, weights=None, density=False) + hist, _ = np.histogram( + arr[..., c].values, + bins=bins, + range=v_range, + weights=None, + density=False, + ) for i, count in enumerate(hist): features[f"{feature_name}_ch-{c}_bin-{i}"] = count @@ -185,7 +191,13 @@ def features_texture( library_id: str | None = None, feature_name: str = "texture", channels: Channel_t | None = None, - props: Sequence[str] = ("contrast", "dissimilarity", "homogeneity", "correlation", "ASM"), + props: Sequence[str] = ( + "contrast", + "dissimilarity", + "homogeneity", + "correlation", + "ASM", + ), distances: Sequence[int] = (1,), angles: Sequence[float] = (0, np.pi / 4, np.pi / 2, 3 * np.pi / 4), ) -> Feature_t: @@ -333,7 +345,8 @@ def convert_to_full_image_coordinates(x: NDArrayA, y: NDArrayA) -> NDArrayA: return np.array([[]], dtype=np.float64) # because of masking, should not happen coord = self.data.attrs.get( - Key.img.coords, CropCoords(x0=0, y0=0, x1=self.data.sizes["x"], y1=self.data.sizes["y"]) + Key.img.coords, + CropCoords(x0=0, y0=0, x1=self.data.sizes["x"], y1=self.data.sizes["y"]), ) # fall back to default (i.e no crop) coordinates padding = self.data.attrs.get(Key.img.padding, _NULL_PADDING) # fallback to no padding y_slc, x_slc = coord.to_image_coordinates(padding).slice diff --git a/src/squidpy/im/_segment.py b/src/squidpy/im/_segment.py index 88a4281f..6aa810ec 100644 --- a/src/squidpy/im/_segment.py +++ b/src/squidpy/im/_segment.py @@ -103,7 +103,12 @@ def _(self, img: NDArrayA, **kwargs: Any) -> NDArrayA: return SegmentationModel._postcondition(img) @segment.register(da.Array) - def _(self, img: da.Array, chunks: str | int | tuple[int, ...] | None = None, **kwargs: Any) -> NDArrayA: + def _( + self, + img: da.Array, + chunks: str | int | tuple[int, ...] | None = None, + **kwargs: Any, + ) -> NDArrayA: img = SegmentationModel._precondition(img) if chunks is not None: img = img.rechunk(chunks) diff --git a/src/squidpy/pl/__init__.py b/src/squidpy/pl/__init__.py index 051490c4..0bcc0023 100644 --- a/src/squidpy/pl/__init__.py +++ b/src/squidpy/pl/__init__.py @@ -9,7 +9,8 @@ nhood_enrichment, ripley, ) -from squidpy.pl._interactive import Interactive # type: ignore[attr-defined] + +# from squidpy.pl._interactive import Interactive # type: ignore[attr-defined] # deprecated from squidpy.pl._ligrec import ligrec from squidpy.pl._spatial import spatial_scatter, spatial_segment from squidpy.pl._utils import extract diff --git a/src/squidpy/pl/_interactive/__init__.py b/src/squidpy/pl/_interactive/__init__.py index 4cc6b18d..6e76150c 100644 --- a/src/squidpy/pl/_interactive/__init__.py +++ b/src/squidpy/pl/_interactive/__init__.py @@ -1,3 +1,3 @@ from __future__ import annotations -from squidpy.pl._interactive.interactive import Interactive +# from squidpy.pl._interactive.interactive import Interactive diff --git a/src/squidpy/pl/_interactive/interactive.py b/src/squidpy/pl/_interactive/interactive.py index b32067e5..78477344 100644 --- a/src/squidpy/pl/_interactive/interactive.py +++ b/src/squidpy/pl/_interactive/interactive.py @@ -1,3 +1,12 @@ +""" +interactive.py + +WARNING: This module is deprecated and will be removed in a future version. +Please use `https://github.com/scverse/napari-spatialdata` instead. +""" + +raise ImportError("The squidpy napari plugin is deprecated, please use https://github.com/scverse/napari-spatialdata") + from __future__ import annotations from typing import ( diff --git a/src/squidpy/pl/_ligrec.py b/src/squidpy/pl/_ligrec.py index c1fcd9e0..18a55476 100644 --- a/src/squidpy/pl/_ligrec.py +++ b/src/squidpy/pl/_ligrec.py @@ -89,7 +89,13 @@ def _plot_size_legend(self, size_legend_ax: Axes) -> None: zorder=100, ) ax.scatter( - [0.65], [0], s=0.33 * mult, color="white", edgecolor="black", linewidth=self.dot_edge_lw, zorder=100 + [0.65], + [0], + s=0.33 * mult, + color="white", + edgecolor="black", + linewidth=self.dot_edge_lw, + zorder=100, ) ax.set_xlim([0, 1]) ax.set_xticks([0.35, 0.65]) @@ -319,14 +325,17 @@ def get_dendrogram(adata: AnnData, linkage: str = "complete") -> Mapping[str, An adata = AnnData(pvals.values, obs={"groups": pd.Categorical(pvals.index)}, var=var) adata.obs_names = pvals.index - minn = np.nanmin(adata.X) + minn: float = np.nanmin(adata.X) delta = np.nanmax(adata.X) - minn adata.X = (adata.X - minn) / delta try: if dendrogram == DendrogramAxis.BOTH: row_order, col_order, _, _ = _dendrogram( - adata.X, method="complete", metric="correlation", optimal_ordering=adata.n_obs <= 1500 + adata.X, + method="complete", + metric="correlation", + optimal_ordering=adata.n_obs <= 1500, ) adata = adata[row_order, :][:, col_order] pvals = pvals.iloc[row_order, :].iloc[:, col_order] @@ -356,8 +365,8 @@ def get_dendrogram(adata: AnnData, linkage: str = "complete") -> Mapping[str, An dot_color_df=means, dot_size_df=pvals, title=title, - var_group_labels=None if dendrogram == DendrogramAxis.BOTH else list(label_ranges.keys()), - var_group_positions=None if dendrogram == DendrogramAxis.BOTH else list(label_ranges.values()), + var_group_labels=(None if dendrogram == DendrogramAxis.BOTH else list(label_ranges.keys())), + var_group_positions=(None if dendrogram == DendrogramAxis.BOTH else list(label_ranges.values())), standard_scale=None, figsize=figsize, ) @@ -370,7 +379,10 @@ def get_dendrogram(adata: AnnData, linkage: str = "complete") -> Mapping[str, An **_filter_kwargs(sc.pl.DotPlot.legend, kwargs), ) ) - if dendrogram in (DendrogramAxis.INTERACTING_MOLS, DendrogramAxis.INTERACTING_CLUSTERS): + if dendrogram in ( + DendrogramAxis.INTERACTING_MOLS, + DendrogramAxis.INTERACTING_CLUSTERS, + ): # ignore the warning about mismatching groups with verbosity(0): dp.add_dendrogram(size=1.6, dendrogram_key="dendrogram") diff --git a/src/squidpy/pl/_utils.py b/src/squidpy/pl/_utils.py index b30aa297..14a231ee 100644 --- a/src/squidpy/pl/_utils.py +++ b/src/squidpy/pl/_utils.py @@ -45,7 +45,13 @@ @d.dedent -def save_fig(fig: Figure, path: str | Path, make_dir: bool = True, ext: str = "png", **kwargs: Any) -> None: +def save_fig( + fig: Figure, + path: str | Path, + make_dir: bool = True, + ext: str = "png", + **kwargs: Any, +) -> None: """ Save a figure. @@ -206,11 +212,10 @@ def _min_max_norm(vec: spmatrix | NDArrayA) -> NDArrayA: if vec.ndim != 1: raise ValueError(f"Expected `1` dimension, found `{vec.ndim}`.") - maxx, minn = np.nanmax(vec), np.nanmin(vec) + maxx: np.float64 = np.nanmax(vec) + minn: np.float64 = np.nanmin(vec) - return ( # type: ignore[no-any-return] - np.ones_like(vec) if np.isclose(minn, maxx) else ((vec - minn) / (maxx - minn)) - ) + return np.ones_like(vec) if np.isclose(minn, maxx) else ((vec - minn) / (maxx - minn)) def _ensure_dense_vector(fn: Callable[..., Vector_name_t]) -> Callable[..., Vector_name_t]: @@ -438,7 +443,12 @@ def get_obsm(self, name: str, index: int | str = 0) -> tuple[NDArrayA | None, st return (res if res.ndim == 1 else res[:, index]), pretty_name - def _format_key(self, key: str | int, layer_modifier: bool = False, index: int | str | None = None) -> str: + def _format_key( + self, + key: str | int, + layer_modifier: bool = False, + index: int | str | None = None, + ) -> str: if not layer_modifier: return str(key) + (f":{index}" if index is not None else "") @@ -467,7 +477,10 @@ def _get_black_or_white(value: float, cmap: mcolors.Colormap) -> str: def _annotate_heatmap( - im: mpl.image.AxesImage, valfmt: str = "{x:.2f}", cmap: mpl.colors.Colormap | str = "viridis", **kwargs: Any + im: mpl.image.AxesImage, + valfmt: str = "{x:.2f}", + cmap: mpl.colors.Colormap | str = "viridis", + **kwargs: Any, ) -> None: # modified from matplotlib's site if isinstance(cmap, str): @@ -495,7 +508,13 @@ def _get_cmap_norm( adata: AnnData, key: str, order: tuple[list[int], list[int]] | None | None = None, -) -> tuple[mcolors.ListedColormap, mcolors.ListedColormap, mcolors.BoundaryNorm, mcolors.BoundaryNorm, int]: +) -> tuple[ + mcolors.ListedColormap, + mcolors.ListedColormap, + mcolors.BoundaryNorm, + mcolors.BoundaryNorm, + int, +]: n_cls = adata.obs[key].nunique() colors = adata.uns[Key.uns.colors(key)] @@ -551,7 +570,10 @@ def _heatmap( row_sm = mpl.cm.ScalarMappable(cmap=row_cmap, norm=row_norm) col_sm = mpl.cm.ScalarMappable(cmap=col_cmap, norm=col_norm) - norm = mpl.colors.Normalize(vmin=kwargs.pop("vmin", np.nanmin(data)), vmax=kwargs.pop("vmax", np.nanmax(data))) + norm = mpl.colors.Normalize( + vmin=kwargs.pop("vmin", np.nanmin(data)), + vmax=kwargs.pop("vmax", np.nanmax(data)), + ) if isinstance(cont_cmap, str): cont_cmap = plt.colormaps[cont_cmap] cont_cmap.set_bad(color="grey") @@ -572,7 +594,13 @@ def _heatmap( cax = divider.append_axes("right", size="1%", pad=0.1) if method is not None: # cluster rows but don't plot dendrogram col_ax = divider.append_axes("top", size="5%") - sch.dendrogram(col_link, no_labels=True, ax=col_ax, color_threshold=0, above_threshold_color="black") + sch.dendrogram( + col_link, + no_labels=True, + ax=col_ax, + color_threshold=0, + above_threshold_color="black", + ) col_ax.axis("off") _ = fig.colorbar( diff --git a/tests/conftest.py b/tests/conftest.py index 01298d6f..01dfe1e7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,12 +18,13 @@ import pytest import scanpy as sc import spatialdata as sd -import squidpy as sq from anndata import AnnData, OldFormatWarning from geopandas import GeoDataFrame from matplotlib.testing.compare import compare_images from scipy.sparse import csr_matrix from shapely import LineString, Point, Polygon, distance + +import squidpy as sq from squidpy._constants._pkg_constants import Key from squidpy.gr import spatial_neighbors from squidpy.im._container import ImageContainer diff --git a/tests/datasets/test_dataset.py b/tests/datasets/test_dataset.py index 109a27ce..4c0c958e 100644 --- a/tests/datasets/test_dataset.py +++ b/tests/datasets/test_dataset.py @@ -6,9 +6,10 @@ from types import FunctionType import pytest -import squidpy as sq from anndata import AnnData, OldFormatWarning +import squidpy as sq + class TestDatasetsImports: @pytest.mark.parametrize("func", sq.datasets._dataset.__all__ + sq.datasets._image.__all__) diff --git a/tests/datasets/test_download_visium_dataset.py b/tests/datasets/test_download_visium_dataset.py index 12a6506d..77e4b735 100644 --- a/tests/datasets/test_download_visium_dataset.py +++ b/tests/datasets/test_download_visium_dataset.py @@ -10,6 +10,7 @@ import pytest from anndata.tests.helpers import assert_adata_equal from scanpy._settings import settings + from squidpy.datasets import visium diff --git a/tests/graph/test_ligrec.py b/tests/graph/test_ligrec.py index dc087846..0145999d 100644 --- a/tests/graph/test_ligrec.py +++ b/tests/graph/test_ligrec.py @@ -14,6 +14,7 @@ from pandas.testing import assert_frame_equal from scanpy import settings as s from scanpy.datasets import blobs + from squidpy._constants._pkg_constants import Key from squidpy.gr import ligrec from squidpy.gr._ligrec import PermutationTest diff --git a/tests/graph/test_nhood.py b/tests/graph/test_nhood.py index d8448ba2..a89074b9 100644 --- a/tests/graph/test_nhood.py +++ b/tests/graph/test_nhood.py @@ -4,6 +4,7 @@ import pandas as pd import pytest from anndata import AnnData + from squidpy._constants._pkg_constants import Key from squidpy.gr import ( centrality_scores, diff --git a/tests/graph/test_ppatterns.py b/tests/graph/test_ppatterns.py index 01c26e8e..6752b154 100644 --- a/tests/graph/test_ppatterns.py +++ b/tests/graph/test_ppatterns.py @@ -6,6 +6,7 @@ import pytest from anndata import AnnData from pandas.testing import assert_frame_equal + from squidpy._constants._pkg_constants import Key from squidpy.gr import co_occurrence, spatial_autocorr from squidpy.gr._ppatterns import _find_min_max diff --git a/tests/graph/test_ripley.py b/tests/graph/test_ripley.py index f74b7349..750acbff 100644 --- a/tests/graph/test_ripley.py +++ b/tests/graph/test_ripley.py @@ -3,6 +3,7 @@ import numpy as np import pytest from anndata import AnnData + from squidpy._constants._constants import RipleyStat from squidpy.gr import ripley diff --git a/tests/graph/test_sepal.py b/tests/graph/test_sepal.py index 28c76c2b..8fb711f5 100644 --- a/tests/graph/test_sepal.py +++ b/tests/graph/test_sepal.py @@ -3,6 +3,7 @@ import numpy as np from anndata import AnnData from pandas.testing import assert_frame_equal + from squidpy.gr import sepal, spatial_neighbors UNS_KEY = "sepal_score" diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 28639f8d..af7afeb0 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -9,6 +9,7 @@ from scipy.sparse import isspmatrix_csr from shapely import Point from spatialdata.datasets import blobs + from squidpy._constants._pkg_constants import Key from squidpy.gr import mask_graph, spatial_neighbors from squidpy.gr._build import _build_connectivity diff --git a/tests/graph/test_utils.py b/tests/graph/test_utils.py index 6cbb3eef..a85f4e32 100644 --- a/tests/graph/test_utils.py +++ b/tests/graph/test_utils.py @@ -4,6 +4,7 @@ import pandas as pd import pytest from anndata import AnnData + from squidpy._constants._pkg_constants import Key from squidpy.gr._utils import _shuffle_group diff --git a/tests/image/test_container.py b/tests/image/test_container.py index 14334287..6402bd52 100644 --- a/tests/image/test_container.py +++ b/tests/image/test_container.py @@ -13,12 +13,13 @@ import imageio.v3 as iio import numpy as np import pytest -import squidpy as sq import tifffile import xarray as xr from anndata import AnnData from PIL import Image from pytest_mock import MockerFixture + +import squidpy as sq from squidpy._constants._pkg_constants import Key from squidpy.im import ImageContainer from squidpy.im._coords import _NULL_COORDS, CropCoords, CropPadding diff --git a/tests/image/test_features.py b/tests/image/test_features.py index 75a6d80e..e0e454ab 100644 --- a/tests/image/test_features.py +++ b/tests/image/test_features.py @@ -7,6 +7,7 @@ import pytest from anndata import AnnData from pytest_mock import MockerFixture + from squidpy._constants._constants import ImageFeature from squidpy.im._container import ImageContainer from squidpy.im._feature import calculate_image_features diff --git a/tests/image/test_io.py b/tests/image/test_io.py index cc63478c..2c556341 100644 --- a/tests/image/test_io.py +++ b/tests/image/test_io.py @@ -7,6 +7,7 @@ import xarray as xr from pytest_mock import MockerFixture from skimage.io import imread + from squidpy._constants._constants import InferDimensions from squidpy.im._io import _get_image_shape_dtype, _infer_dimensions, _lazy_load_image diff --git a/tests/image/test_processing.py b/tests/image/test_processing.py index b2b4d9ca..7b97b0fb 100644 --- a/tests/image/test_processing.py +++ b/tests/image/test_processing.py @@ -6,6 +6,7 @@ import numpy as np import pytest from pytest_mock import MockerFixture + from squidpy._constants._pkg_constants import Key from squidpy.im import ImageContainer, process diff --git a/tests/image/test_segmentation.py b/tests/image/test_segmentation.py index e39e89c8..4ef0c594 100644 --- a/tests/image/test_segmentation.py +++ b/tests/image/test_segmentation.py @@ -6,6 +6,7 @@ import numpy as np import pytest from pytest_mock import MockerFixture + from squidpy._constants._constants import SegmentationBackend from squidpy._constants._pkg_constants import Key from squidpy.im import ( diff --git a/tests/plotting/test_graph.py b/tests/plotting/test_graph.py index e89ce701..6e1c20f7 100644 --- a/tests/plotting/test_graph.py +++ b/tests/plotting/test_graph.py @@ -9,8 +9,8 @@ import pytest import scanpy as sc from anndata import AnnData -from squidpy import gr, pl +from squidpy import gr, pl from tests.conftest import DPI, PlotTester, PlotTesterMeta C_KEY = "leiden" diff --git a/tests/plotting/test_image.py b/tests/plotting/test_image.py index 1f53504f..2702b885 100644 --- a/tests/plotting/test_image.py +++ b/tests/plotting/test_image.py @@ -5,11 +5,11 @@ import pandas as pd import pytest import scanpy as sc -import squidpy as sq from anndata import AnnData from matplotlib.testing.compare import compare_images -from squidpy.im import ImageContainer +import squidpy as sq +from squidpy.im import ImageContainer from tests.conftest import ACTUAL, DPI, EXPECTED, TOL, PlotTester, PlotTesterMeta diff --git a/tests/plotting/test_interactive.py b/tests/plotting/test_interactive.py index 94807a11..9d1549f9 100644 --- a/tests/plotting/test_interactive.py +++ b/tests/plotting/test_interactive.py @@ -10,8 +10,8 @@ from matplotlib.testing.compare import compare_images from scanpy import settings as s from scipy.sparse import issparse -from squidpy.im import ImageContainer +from squidpy.im import ImageContainer from tests.conftest import ACTUAL, DPI, EXPECTED, TOL, PlotTester, PlotTesterMeta diff --git a/tests/plotting/test_spatial_static.py b/tests/plotting/test_spatial_static.py index 04be1be1..0f8267b8 100644 --- a/tests/plotting/test_spatial_static.py +++ b/tests/plotting/test_spatial_static.py @@ -11,11 +11,11 @@ import scanpy as sc from anndata import AnnData from matplotlib.colors import ListedColormap + from squidpy import pl from squidpy._constants._pkg_constants import Key from squidpy.gr import spatial_neighbors from squidpy.pl._spatial_utils import _get_library_id - from tests.conftest import PlotTester, PlotTesterMeta sc.set_figure_params(dpi=40, color_map="viridis") diff --git a/tests/plotting/test_var_by_distance_plot.py b/tests/plotting/test_var_by_distance_plot.py index 0ff18d88..f0ff1492 100644 --- a/tests/plotting/test_var_by_distance_plot.py +++ b/tests/plotting/test_var_by_distance_plot.py @@ -2,8 +2,8 @@ import scanpy as sc from anndata import AnnData -from squidpy import pl, tl +from squidpy import pl, tl from tests.conftest import PlotTester, PlotTesterMeta sc.pl.set_rcParams_defaults() diff --git a/tests/read/test_visium.py b/tests/read/test_visium.py index a92b488a..fb063e64 100644 --- a/tests/read/test_visium.py +++ b/tests/read/test_visium.py @@ -1,6 +1,7 @@ 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 ca039293..f089747e 100644 --- a/tests/tools/test_var_by_distance.py +++ b/tests/tools/test_var_by_distance.py @@ -2,6 +2,7 @@ import pytest from anndata import AnnData + from squidpy.tl import var_by_distance from squidpy.tl._var_by_distance import _normalize_distances diff --git a/tox.ini b/tox.ini index 01368f21..b517e846 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,9 @@ python_files = test_*.py testpaths = tests/ xfail_strict = true -qt_api=pyqt5 +; qt_api=pyqt5 +addopts = + --ignore=tests/plotting/test_interactive.py filterwarnings = ignore::UserWarning @@ -68,7 +70,7 @@ deps = pytest pytest-xdist pytest-cov - pytest-qt + ; pytest-qt pytest-mock pytest-timeout # see: https://github.com/numba/llvmlite/issues/669 From 1405d49f154e9034616dff5c82f7330adf442bb0 Mon Sep 17 00:00:00 2001 From: Giovanni Palla <25887487+giovp@users.noreply.github.com> Date: Sat, 31 Aug 2024 01:02:50 +0200 Subject: [PATCH 2/5] fix precommit (#881) * update * update * update * update * remove 3.9 from test * remove py39 from lint * add release note --- .github/workflows/lint.yml | 4 ++-- .github/workflows/test.yml | 2 +- .pre-commit-config.yaml | 4 ++-- docs/release/notes-1.6.1.rst | 3 ++- pyproject.toml | 3 ++- src/squidpy/gr/_build.py | 2 +- src/squidpy/gr/_ppatterns.py | 10 +++++----- src/squidpy/gr/_sepal.py | 4 ++-- src/squidpy/im/_container.py | 4 ++-- src/squidpy/im/_feature_mixin.py | 6 +++--- src/squidpy/im/_segment.py | 2 +- src/squidpy/pl/_ligrec.py | 2 +- src/squidpy/pl/_spatial_utils.py | 8 ++++---- tox.ini | 8 ++++---- 14 files changed, 32 insertions(+), 30 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index af7beb07..a6923c67 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,10 +15,10 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Set up Python 3.9 + - name: Set up Python 3.11 uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.11 - uses: actions/cache@v3 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a7d9bc14..0ca3fc20 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - python: [3.9, "3.10", "3.11"] + python: ["3.10", "3.11", "3.12"] os: [ubuntu-latest] include: - python: "3.12" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6318f0fa..9eaf91ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ default_stages: minimum_pre_commit_version: 2.9.3 repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.2 + rev: v0.6.3 hooks: - id: ruff types_or: [python, pyi, jupyter] @@ -19,7 +19,7 @@ repos: hooks: - id: blacken-docs - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.11.2 hooks: - id: mypy additional_dependencies: [numpy, pandas, types-requests] diff --git a/docs/release/notes-1.6.1.rst b/docs/release/notes-1.6.1.rst index 94cb64a2..533e3229 100644 --- a/docs/release/notes-1.6.1.rst +++ b/docs/release/notes-1.6.1.rst @@ -6,4 +6,5 @@ Maintenance - The Squidpy `interactive` module has been removed. Please use the ``napari-spatialdata`` plugin instead. You can find it `here `__ `@giovp `__ `#877 `__ - +- Drop support for Python 3.9 `@giovp `__ + `#881 `__ diff --git a/pyproject.toml b/pyproject.toml index c340b882..8cc85ffd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,8 +18,9 @@ classifiers = [ "Operating System :: MacOS :: MacOS X", "Typing :: Typed", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Environment :: Console", "Framework :: Jupyter", "Intended Audience :: Science/Research", diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index aec37ce4..e607823c 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -237,7 +237,7 @@ def spatial_neighbors( for lib in libs: ixs.extend(np.where(adata.obs[library_key] == lib)[0]) mats.append(_build_fun(adata[adata.obs[library_key] == lib])) - ixs = np.argsort(ixs) # type: ignore[assignment] # invert + ixs = np.argsort(ixs) # invert Adj = block_diag([m[0] for m in mats], format="csr")[ixs, :][:, ixs] Dst = block_diag([m[1] for m in mats], format="csr")[ixs, :][:, ixs] else: diff --git a/src/squidpy/gr/_ppatterns.py b/src/squidpy/gr/_ppatterns.py index caa7b896..ed36a8bc 100644 --- a/src/squidpy/gr/_ppatterns.py +++ b/src/squidpy/gr/_ppatterns.py @@ -164,7 +164,7 @@ def extract_obsm(adata: AnnData, ixs: int | Sequence[int] | None) -> tuple[NDArr if layer not in adata.obsm: raise KeyError(f"Key `{layer!r}` not found in `adata.obsm`.") if ixs is None: - ixs = np.arange(adata.obsm[layer].shape[1]) # type:ignore[assignment] + ixs = np.arange(adata.obsm[layer].shape[1]) ixs = list(np.ravel([ixs])) return adata.obsm[layer][:, ixs].T, ixs @@ -429,10 +429,10 @@ def co_occurrence( n_splits = max(min(n_splits, n_obs), 1) # split array and labels - spatial_splits = tuple(s for s in np.array_split(spatial, n_splits, axis=0) if len(s)) # type:ignore[arg-type] - labs_splits = tuple(s for s in np.array_split(labs, n_splits, axis=0) if len(s)) # type:ignore[arg-type] + spatial_splits = tuple(s for s in np.array_split(spatial, n_splits, axis=0) if len(s)) + labs_splits = tuple(s for s in np.array_split(labs, n_splits, axis=0) if len(s)) # create idx array including unique combinations and self-comparison - x, y = np.triu_indices_from(np.empty((n_splits, n_splits))) # type:ignore[arg-type] + x, y = np.triu_indices_from(np.empty((n_splits, n_splits))) idx_splits = list(zip(x, y)) n_jobs = _get_n_cores(n_jobs) @@ -578,7 +578,7 @@ def _g_moments(w: spmatrix | NDArrayA) -> tuple[float, float, float]: # s1 t = w.transpose() + w - t2 = t.multiply(t) # type:ignore[union-attr] + t2 = t.multiply(t) s1 = t2.sum() / 2.0 # s2 diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index e279cc9c..214ab47a 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -182,8 +182,8 @@ def _score_helper( score, sparse = [], issparse(vals) for i in ixs: - conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() # type:ignore[union-attr] - conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() # type:ignore[union-attr] + conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() + conc = vals[:, i].toarray().flatten() if sparse else vals[:, i].copy() time_iter = _diffusion(conc, fun, n_iter, sat, sat_idx, unsat, unsat_idx, dt=dt, thresh=thresh) score.append(dt * time_iter) diff --git a/src/squidpy/im/_container.py b/src/squidpy/im/_container.py index a7bd5771..13db6141 100644 --- a/src/squidpy/im/_container.py +++ b/src/squidpy/im/_container.py @@ -983,12 +983,12 @@ def show( arr = arr.sel(z=library_ids) if channel is not None: - channel = np.asarray([channel]).ravel() # type: ignore[assignment] + channel = np.asarray([channel]).ravel() if not len(channel): # type: ignore[arg-type] raise ValueError("No channels have been selected.") arr = arr[{arr.dims[-1]: channel}] else: - channel = np.arange(arr.shape[-1]) # type: ignore[assignment] + channel = np.arange(arr.shape[-1]) if TYPE_CHECKING: assert isinstance(channel, Sequence) diff --git a/src/squidpy/im/_feature_mixin.py b/src/squidpy/im/_feature_mixin.py index f5149b7b..fb75c7d2 100644 --- a/src/squidpy/im/_feature_mixin.py +++ b/src/squidpy/im/_feature_mixin.py @@ -352,14 +352,14 @@ def convert_to_full_image_coordinates(x: NDArrayA, y: NDArrayA) -> NDArrayA: y_slc, x_slc = coord.to_image_coordinates(padding).slice # relative coordinates - y = (y - np.min(y)) / (np.max(y) - np.min(y)) - x = (x - np.min(x)) / (np.max(x) - np.min(x)) + y = (y - np.min(y)) / (np.max(y) - np.min(y)) # type:ignore[operator] + x = (x - np.min(x)) / (np.max(x) - np.min(x)) # type:ignore[operator] # coordinates in the uncropped image y = coord.slice[0].start + (y_slc.stop - y_slc.start) * y x = coord.slice[1].start + (x_slc.stop - x_slc.start) * x - return np.c_[x, y] # type: ignore[no-any-return] + return np.c_[x, y] label_layer = self._get_layer(label_layer) library_id = self._get_library_id(library_id) diff --git a/src/squidpy/im/_segment.py b/src/squidpy/im/_segment.py index 6aa810ec..7af32f45 100644 --- a/src/squidpy/im/_segment.py +++ b/src/squidpy/im/_segment.py @@ -96,7 +96,7 @@ def _postcondition(img: NDArrayA | da.Array) -> NDArrayA | da.Array: def _(self, img: NDArrayA, **kwargs: Any) -> NDArrayA: chunks = kwargs.pop("chunks", None) if chunks is not None: - return self.segment(da.asarray(img).rechunk(chunks), **kwargs) # type: ignore[no-any-return] + return self.segment(da.asarray(img).rechunk(chunks), **kwargs) img = SegmentationModel._precondition(img) img = self._segment(img, **kwargs) diff --git a/src/squidpy/pl/_ligrec.py b/src/squidpy/pl/_ligrec.py index 18a55476..e6ed8c5c 100644 --- a/src/squidpy/pl/_ligrec.py +++ b/src/squidpy/pl/_ligrec.py @@ -42,7 +42,7 @@ def _plot_size_legend(self, size_legend_ax: Axes) -> None: y = self.BASE ** -((self.dot_max * self._delta) + self._minn) x = self.BASE ** -((self.dot_min * self._delta) + self._minn) size_range = -(np.logspace(x, y, self.DEFAULT_NUM_LEGEND_DOTS + 1, base=10).astype(np.float64)) - size_range = (size_range - np.min(size_range)) / (np.max(size_range) - np.min(size_range)) + size_range = (size_range - np.min(size_range)) / (np.max(size_range) - np.min(size_range)) # type:ignore[operator] # no point in showing dot of size 0 size_range = size_range[1:] diff --git a/src/squidpy/pl/_spatial_utils.py b/src/squidpy/pl/_spatial_utils.py index 2e98cbac..63927e07 100644 --- a/src/squidpy/pl/_spatial_utils.py +++ b/src/squidpy/pl/_spatial_utils.py @@ -711,8 +711,8 @@ def _map_color_seg( if isinstance(color_vector.dtype, CategoricalDtype): if isinstance(na_color, tuple) and len(na_color) == 4 and np.any(color_source_vector.isna()): cell_id[color_source_vector.isna()] = 0 - val_im: NDArrayA = map_array(seg, cell_id, color_vector.codes + 1) # type: ignore[union-attr] - cols = colors.to_rgba_array(color_vector.categories) # type: ignore[union-attr] + val_im: NDArrayA = map_array(seg, cell_id, color_vector.codes + 1) + cols = colors.to_rgba_array(color_vector.categories) else: val_im = map_array(seg, cell_id, cell_id) # replace with same seg id to remove missing segs try: @@ -960,7 +960,7 @@ def _plot_scatter( coords[:, 0], coords[:, 1], s=outline_params.bg_size, - c=outline_params.bg_color, # type: ignore[arg-type] + c=outline_params.bg_color, rasterized=sc_settings._vector_friendly, cmap=cmap_params.cmap, norm=norm, @@ -971,7 +971,7 @@ def _plot_scatter( coords[:, 0], coords[:, 1], s=outline_params.gap_size, - c=outline_params.gap_color, # type: ignore[arg-type] + c=outline_params.gap_color, rasterized=sc_settings._vector_friendly, cmap=cmap_params.cmap, norm=norm, diff --git a/tox.ini b/tox.ini index b517e846..c50f43b9 100644 --- a/tox.ini +++ b/tox.ini @@ -41,9 +41,9 @@ sort = Miss [gh-actions] python = - 3.8: py38 - 3.9: py39 3.10: py310 + 3.11: py311 + 3.12: py312 [gh-actions:env] PLATFORM = @@ -55,7 +55,7 @@ isolated_build = True envlist = covclean lint - py{3.9,3.10}-{linux,macos} + py{3.10,3.11}-{linux,macos} coverage readme check-docs @@ -93,7 +93,7 @@ deps = coverage diff_cover skip_install = true -depends = py{38,39,310}-{linux,macos} +depends = py{310,311,312}-{linux,macos} parallel_show_output = True commands = coverage report --omit="tox/*" From 7e008aa7ad4ce64ebd791de4b4e8698a742cc3e9 Mon Sep 17 00:00:00 2001 From: Giovanni Palla <25887487+giovp@users.noreply.github.com> Date: Mon, 2 Sep 2024 09:17:25 -0700 Subject: [PATCH 3/5] remove 3.9 support, fix precommit, release note (#882) * update * update * update * update * remove 3.9 from test * remove py39 from lint * add release note * update notebooks --- docs/notebooks | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/notebooks b/docs/notebooks index 08e914f1..45e35859 160000 --- a/docs/notebooks +++ b/docs/notebooks @@ -1 +1 @@ -Subproject commit 08e914f17acb0260c9e6b54d227f35072420f7a3 +Subproject commit 45e35859380e46a10f9aa45aac477c08d2be9df8 From 316297431056d5394cf9225188406e9919a951db Mon Sep 17 00:00:00 2001 From: Giovanni Palla <25887487+giovp@users.noreply.github.com> Date: Mon, 2 Sep 2024 09:36:30 -0700 Subject: [PATCH 4/5] add release note (#883) * add release note * update notebooks --- docs/notebooks | 2 +- docs/release/notes-1.6.1.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/notebooks b/docs/notebooks index 45e35859..79257ebb 160000 --- a/docs/notebooks +++ b/docs/notebooks @@ -1 +1 @@ -Subproject commit 45e35859380e46a10f9aa45aac477c08d2be9df8 +Subproject commit 79257ebbcff71260d752eae9a00a5ea36b8542ee diff --git a/docs/release/notes-1.6.1.rst b/docs/release/notes-1.6.1.rst index 533e3229..8b810563 100644 --- a/docs/release/notes-1.6.1.rst +++ b/docs/release/notes-1.6.1.rst @@ -8,3 +8,5 @@ Maintenance `#877 `__ - Drop support for Python 3.9 `@giovp `__ `#881 `__ +- Add Xenium tutorial with spatialdata `@LLehner `__ + `#125 `__ From b7157302407c38f741e3ce52923024cb128bd232 Mon Sep 17 00:00:00 2001 From: giovp Date: Mon, 2 Sep 2024 16:42:05 +0000 Subject: [PATCH 5/5] [auto][ci skip] Release v1.6.1 --- .bumpversion.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8d239610..d29a8ce7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.6.0 +current_version = 1.6.1 commit = True tag = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)