From 8b41cead9cc2267d8dc07c9c6c2d1e668f882285 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Wed, 5 Aug 2026 11:04:49 +0200 Subject: [PATCH 1/6] fix: update compatibility with Scanpy 1.13 plotting internals --- src/squidpy/_compat.py | 33 +++++++++++++++++++++++++-------- src/squidpy/im/_container.py | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/squidpy/_compat.py b/src/squidpy/_compat.py index 3bf141706..03ef70378 100644 --- a/src/squidpy/_compat.py +++ b/src/squidpy/_compat.py @@ -3,9 +3,6 @@ from importlib.metadata import version from packaging.version import Version -from scanpy.plotting._tools.scatterplots import _add_categorical_legend as add_categorical_legend -from scanpy.plotting._tools.scatterplots import _panel_grid as panel_grid -from scanpy.plotting._utils import add_colors_for_categorical_sample_annotation __all__ = [ # scanpy @@ -13,19 +10,39 @@ "add_categorical_legend", "panel_grid", "add_colors_for_categorical_sample_annotation", + "default_palette", # anndata "ArrayView", "SparseCSCView", "SparseCSRView", ] -# See https://github.com/scverse/squidpy/issues/1061 for more details. -# Scanpy 0.11.x-0.12.x renamed set_default_colors_for_categorical_obs to _set_default_colors_for_categorical_obs -# and then changed it back. Try underscore version first, fall back to non-underscore. +# Scanpy 1.13 moved the pre-v2 plotting internals under ``scanpy.plotting.legacy``. +# ``scanpy.plotting.__getattr__`` forwards attribute access there, but submodule +# imports such as ``scanpy.plotting.palettes`` are not covered by it. try: - from scanpy.plotting._utils import _set_default_colors_for_categorical_obs as set_default_colors_for_categorical_obs + from scanpy.plotting.legacy._tools.scatterplots import _add_categorical_legend as add_categorical_legend + from scanpy.plotting.legacy._tools.scatterplots import _panel_grid as panel_grid + from scanpy.plotting.legacy._utils import ( + add_colors_for_categorical_sample_annotation, + set_default_colors_for_categorical_obs, + ) + from scanpy.plotting.legacy.palettes import default_102 as default_palette except ImportError: - from scanpy.plotting._utils import set_default_colors_for_categorical_obs + from scanpy.plotting._tools.scatterplots import _add_categorical_legend as add_categorical_legend + from scanpy.plotting._tools.scatterplots import _panel_grid as panel_grid + from scanpy.plotting._utils import add_colors_for_categorical_sample_annotation + from scanpy.plotting.palettes import default_102 as default_palette + + # See https://github.com/scverse/squidpy/issues/1061 for more details. + # Scanpy 0.11.x-0.12.x renamed set_default_colors_for_categorical_obs to _set_default_colors_for_categorical_obs + # and then changed it back. Try underscore version first, fall back to non-underscore. + try: + from scanpy.plotting._utils import ( + _set_default_colors_for_categorical_obs as set_default_colors_for_categorical_obs, + ) + except ImportError: + from scanpy.plotting._utils import set_default_colors_for_categorical_obs CAN_USE_SPARSE_ARRAY = Version(version("anndata")) >= Version("0.11.0rc1") diff --git a/src/squidpy/im/_container.py b/src/squidpy/im/_container.py index 7b439a0ec..1656c550d 100644 --- a/src/squidpy/im/_container.py +++ b/src/squidpy/im/_container.py @@ -19,10 +19,10 @@ from dask import delayed from matplotlib.colors import ListedColormap from scanpy import logging as logg -from scanpy.plotting.palettes import default_102 as default_palette from skimage.transform import rescale from skimage.util import img_as_float +from squidpy._compat import default_palette from squidpy._constants._constants import InferDimensions from squidpy._constants._pkg_constants import Key from squidpy._docs import d, inject_docs From cbe4d4fbb3eeb9c2da3f1250830f340bc57a9a17 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Wed, 5 Aug 2026 11:21:25 +0200 Subject: [PATCH 2/6] put the spatialdata plot usage in a new job? --- tests/experimental/plotting/__init__.py | 0 .../{ => plotting}/test_detect_tissue.py | 0 .../{ => plotting}/test_make_tiles.py | 0 .../{ => plotting}/test_qc_image.py | 0 .../plotting/test_stain_normalize.py | 37 +++++++++++++++++++ tests/experimental/test_stain_normalize.py | 29 --------------- 6 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 tests/experimental/plotting/__init__.py rename tests/experimental/{ => plotting}/test_detect_tissue.py (100%) rename tests/experimental/{ => plotting}/test_make_tiles.py (100%) rename tests/experimental/{ => plotting}/test_qc_image.py (100%) create mode 100644 tests/experimental/plotting/test_stain_normalize.py diff --git a/tests/experimental/plotting/__init__.py b/tests/experimental/plotting/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/experimental/test_detect_tissue.py b/tests/experimental/plotting/test_detect_tissue.py similarity index 100% rename from tests/experimental/test_detect_tissue.py rename to tests/experimental/plotting/test_detect_tissue.py diff --git a/tests/experimental/test_make_tiles.py b/tests/experimental/plotting/test_make_tiles.py similarity index 100% rename from tests/experimental/test_make_tiles.py rename to tests/experimental/plotting/test_make_tiles.py diff --git a/tests/experimental/test_qc_image.py b/tests/experimental/plotting/test_qc_image.py similarity index 100% rename from tests/experimental/test_qc_image.py rename to tests/experimental/plotting/test_qc_image.py diff --git a/tests/experimental/plotting/test_stain_normalize.py b/tests/experimental/plotting/test_stain_normalize.py new file mode 100644 index 000000000..40f730c29 --- /dev/null +++ b/tests/experimental/plotting/test_stain_normalize.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import matplotlib.pyplot as plt +import spatialdata_plot as sdp +import xarray as xr +from spatialdata.models import Image2DModel + +import squidpy as sq +from squidpy.experimental.im import fit_stain_reference, normalize_stains +from squidpy.experimental.im._utils import get_element_data +from tests.conftest import PlotTester, PlotTesterMeta + +_ = sdp # registers the `.pl` spatialdata accessor + + +class TestStainNormalizationVisual(PlotTester, metaclass=PlotTesterMeta): + def test_plot_reinhard_before_after(self, sdata_hne) -> None: + """Visual: a re-stained source (left) normalized back to the H&E reference (right).""" + image_key = next(iter(sdata_hne.images)) + sq.experimental.im.detect_tissue(sdata_hne, image_key) + reference = fit_stain_reference(sdata_hne, image_key, method="reinhard") + + # Deterministically warm/cool the channels to simulate a different + # staining batch, so the before/after panels are visibly distinct. + da_rgb = get_element_data(sdata_hne.images[image_key], "auto", "image", image_key).astype("float32") + weights = xr.DataArray([1.4, 1.0, 0.6], dims="c", coords={"c": da_rgb.coords["c"]}) + shifted = (da_rgb * weights).clip(0, 255).astype("uint8") + sdata_hne.images["hne_shifted"] = Image2DModel.parse(shifted.data, dims=shifted.dims) + + # `hne_shifted` shares geometry with `image_key`; reuse its tissue mask. + normalize_stains( + sdata_hne, "hne_shifted", reference, image_key_added="hne_normalized", tissue_mask_key=f"{image_key}_tissue" + ) + + _, axes = plt.subplots(1, 2, figsize=(8, 4)) + sdata_hne.pl.render_images("hne_shifted").pl.show(ax=axes[0], title="before") + sdata_hne.pl.render_images("hne_normalized").pl.show(ax=axes[1], title="after") diff --git a/tests/experimental/test_stain_normalize.py b/tests/experimental/test_stain_normalize.py index e09c910ba..11156bd35 100644 --- a/tests/experimental/test_stain_normalize.py +++ b/tests/experimental/test_stain_normalize.py @@ -1,11 +1,9 @@ from __future__ import annotations import dask.array as da -import matplotlib.pyplot as plt import numpy as np import pytest import spatialdata as sd -import spatialdata_plot as sdp import xarray as xr from spatialdata.models import Image2DModel, Labels2DModel from spatialdata.transformations import Scale, get_transformation, set_transformation @@ -18,9 +16,6 @@ normalize_stains, ) from squidpy.experimental.im._utils import get_element_data -from tests.conftest import PlotTester, PlotTesterMeta - -_ = sdp # registers the `.pl` spatialdata accessor def _make_sdata( @@ -227,27 +222,3 @@ def test_fit_apply_smoke(self, sdata_hne) -> None: out = sq.experimental.im.normalize_stains(sdata_hne, image_key, ref, inplace=False) assert "c" in out.dims assert out.sizes["c"] == 3 - - -class TestStainNormalizationVisual(PlotTester, metaclass=PlotTesterMeta): - def test_plot_reinhard_before_after(self, sdata_hne) -> None: - """Visual: a re-stained source (left) normalized back to the H&E reference (right).""" - image_key = next(iter(sdata_hne.images)) - sq.experimental.im.detect_tissue(sdata_hne, image_key) - reference = fit_stain_reference(sdata_hne, image_key, method="reinhard") - - # Deterministically warm/cool the channels to simulate a different - # staining batch, so the before/after panels are visibly distinct. - da_rgb = get_element_data(sdata_hne.images[image_key], "auto", "image", image_key).astype("float32") - weights = xr.DataArray([1.4, 1.0, 0.6], dims="c", coords={"c": da_rgb.coords["c"]}) - shifted = (da_rgb * weights).clip(0, 255).astype("uint8") - sdata_hne.images["hne_shifted"] = Image2DModel.parse(shifted.data, dims=shifted.dims) - - # `hne_shifted` shares geometry with `image_key`; reuse its tissue mask. - normalize_stains( - sdata_hne, "hne_shifted", reference, image_key_added="hne_normalized", tissue_mask_key=f"{image_key}_tissue" - ) - - _, axes = plt.subplots(1, 2, figsize=(8, 4)) - sdata_hne.pl.render_images("hne_shifted").pl.show(ax=axes[0], title="before") - sdata_hne.pl.render_images("hne_normalized").pl.show(ax=axes[1], title="after") From db44b48dc06e4a07281194630192c51982301daa Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Wed, 5 Aug 2026 11:21:29 +0200 Subject: [PATCH 3/6] hatch --- hatch.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hatch.toml b/hatch.toml index 1614be834..a529db5c0 100644 --- a/hatch.toml +++ b/hatch.toml @@ -20,6 +20,10 @@ matrix = [ ] overrides.matrix.deps.env-vars = [ { key = "UV_PRERELEASE", value = "allow", if = ["pre"] }, + # tests/experimental/plotting needs `spatialdata-plot`, whose own pre-release + # compatibility we do not control. Its breakage would mask squidpy's, so the + # pre-release env skips it; the stable envs still run it. + { key = "HATCH_TEST_ARGS", value = " --ignore=tests/experimental/plotting", if = ["pre"] }, ] # default commands (only `cov-report` is overridden) scripts.run = "pytest{env:HATCH_TEST_ARGS:} -p no:cov {args}" From 4dd5b75143f1a6b1c602174c13c5e8a8bee1f682 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Wed, 5 Aug 2026 12:24:04 +0200 Subject: [PATCH 4/6] separate jobs for spatialdaa plots --- .github/workflows/test.yaml | 78 ++++++++++++++++++++++++++++++++++++- hatch.toml | 7 ++-- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a38c81478..74b049737 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -153,7 +153,8 @@ jobs: env: PLATFORM: ${{ matrix.os }} DISPLAY: :42 - run: uvx hatch run ${{ matrix.env.name }}:run -v --color=yes -n auto + # tests/experimental/plotting has its own job, see `spatialdata-plot` below. + run: uvx hatch run ${{ matrix.env.name }}:run -v --color=yes -n auto --ignore=tests/experimental/plotting - name: Archive figures generated during testing if: always() @@ -207,9 +208,10 @@ jobs: env: PLATFORM: ubuntu-latest DISPLAY: :42 + # tests/experimental/plotting has its own job, see `spatialdata-plot` below. run: | uvx hatch run hatch-test.py3.14-stable:cov-erase - uvx hatch run hatch-test.py3.14-stable:run-cov -v --color=yes + uvx hatch run hatch-test.py3.14-stable:run-cov -v --color=yes --ignore=tests/experimental/plotting uvx hatch run hatch-test.py3.14-stable:cov-combine uvx hatch run hatch-test.py3.14-stable:cov-report @@ -219,6 +221,77 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true + # `tests/experimental/plotting` renders through `spatialdata-plot`. It gets its own + # job so that breakage in that stack is attributable at a glance instead of turning + # every environment in the matrix above red -- most importantly the pre-release one, + # where spatialdata-plot's own compatibility is out of our hands and its collection + # errors would mask squidpy's. + spatialdata-plot: + name: spatialdata-plot tests (ubuntu-latest, hatch-test.py3.14-stable) + needs: [ensure-data-is-cached] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + filter: blob:none + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.14" + cache-dependency-glob: pyproject.toml + + - name: Ensure figure directory exists + run: mkdir -p "$GITHUB_WORKSPACE/tests/figures" + + - name: Restore data cache + id: data-cache + uses: actions/cache@v4 + with: + path: data # IMPORTANT: this will fail if scanpy.settings.datasetdir default changes + key: data-${{ hashFiles('**/download_data.py') }} + restore-keys: | + data- + enableCrossOsArchive: true + + - name: System dependencies (Linux) + run: | + sudo apt-get update -y + sudo apt-get install automake -y + + # PyQt5 related + sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 -y + sudo Xvfb :42 -screen 0 1920x1080x24 -ac +extension GLX Date: Wed, 5 Aug 2026 12:36:15 +0200 Subject: [PATCH 5/6] make those tests pre for spatialdata plot --- .github/workflows/test.yaml | 43 ++++++++++++++++--------------------- hatch.toml | 3 --- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 74b049737..492c87a86 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -153,8 +153,11 @@ jobs: env: PLATFORM: ${{ matrix.os }} DISPLAY: :42 - # tests/experimental/plotting has its own job, see `spatialdata-plot` below. - run: uvx hatch run ${{ matrix.env.name }}:run -v --color=yes -n auto --ignore=tests/experimental/plotting + # The stable environments run tests/experimental/plotting as part of the suite. + # The pre-release one skips it and gets the dedicated `spatialdata-plot` job below. + run: | + uvx hatch run ${{ matrix.env.name }}:run -v --color=yes -n auto \ + ${{ contains(matrix.env.name, 'pre') && '--ignore=tests/experimental/plotting' || '' }} - name: Archive figures generated during testing if: always() @@ -208,10 +211,9 @@ jobs: env: PLATFORM: ubuntu-latest DISPLAY: :42 - # tests/experimental/plotting has its own job, see `spatialdata-plot` below. run: | uvx hatch run hatch-test.py3.14-stable:cov-erase - uvx hatch run hatch-test.py3.14-stable:run-cov -v --color=yes --ignore=tests/experimental/plotting + uvx hatch run hatch-test.py3.14-stable:run-cov -v --color=yes uvx hatch run hatch-test.py3.14-stable:cov-combine uvx hatch run hatch-test.py3.14-stable:cov-report @@ -221,15 +223,17 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true - # `tests/experimental/plotting` renders through `spatialdata-plot`. It gets its own - # job so that breakage in that stack is attributable at a glance instead of turning - # every environment in the matrix above red -- most importantly the pre-release one, - # where spatialdata-plot's own compatibility is out of our hands and its collection - # errors would mask squidpy's. + # `tests/experimental/plotting` renders through `spatialdata-plot`, whose own + # pre-release compatibility is out of our hands -- its collection errors would mask + # squidpy's in the pre-release environment. So the pre-release run of those tests is + # carved out here: still visible (and still failing) on its own line, without turning + # the whole pre-release environment red. The stable environments keep running these + # tests as part of the suite in the `test` and `coverage` jobs above. spatialdata-plot: - name: spatialdata-plot tests (ubuntu-latest, hatch-test.py3.14-stable) + name: spatialdata-plot tests (PRE-RELEASE DEPENDENCIES) (ubuntu-latest) needs: [ensure-data-is-cached] runs-on: ubuntu-latest + continue-on-error: true steps: - uses: actions/checkout@v5 with: @@ -265,31 +269,20 @@ jobs: sudo Xvfb :42 -screen 0 1920x1080x24 -ac +extension GLX Date: Wed, 5 Aug 2026 14:29:17 +0200 Subject: [PATCH 6/6] scanpy 1.13 compat and markers --- .github/workflows/test.yaml | 28 ++++++++++++++++--------- pyproject.toml | 1 + src/squidpy/_compat.py | 25 ++++++++++++++++++++++ src/squidpy/pl/_spatial_utils.py | 13 ++++++------ tests/experimental/plotting/conftest.py | 17 +++++++++++++++ tests/experimental/test_tiling_qc.py | 3 +++ 6 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 tests/experimental/plotting/conftest.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 492c87a86..9b4f4b86c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -153,11 +153,16 @@ jobs: env: PLATFORM: ${{ matrix.os }} DISPLAY: :42 - # The stable environments run tests/experimental/plotting as part of the suite. - # The pre-release one skips it and gets the dedicated `spatialdata-plot` job below. + # The stable environments run the spatialdata-plot tests as part of the suite. + # The pre-release one leaves them to the dedicated `spatialdata-plot` job below. + # Two mechanisms are needed: `-m` deselects after collection, which is too late + # for tests/experimental/plotting, whose modules import spatialdata-plot at module + # level and so fail during collection; `--ignore` keeps them from being imported + # at all. The marker then covers tests that only reach spatialdata-plot at call + # time, through squidpy's own lazy import. run: | uvx hatch run ${{ matrix.env.name }}:run -v --color=yes -n auto \ - ${{ contains(matrix.env.name, 'pre') && '--ignore=tests/experimental/plotting' || '' }} + ${{ contains(matrix.env.name, 'pre') && '--ignore=tests/experimental/plotting -m "not spatialdata_plot"' || '' }} - name: Archive figures generated during testing if: always() @@ -223,12 +228,12 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true - # `tests/experimental/plotting` renders through `spatialdata-plot`, whose own - # pre-release compatibility is out of our hands -- its collection errors would mask - # squidpy's in the pre-release environment. So the pre-release run of those tests is - # carved out here: still visible (and still failing) on its own line, without turning - # the whole pre-release environment red. The stable environments keep running these - # tests as part of the suite in the `test` and `coverage` jobs above. + # Everything marked `spatialdata_plot` renders through `spatialdata-plot`, whose own + # pre-release compatibility is out of our hands -- its failures would mask squidpy's in + # the pre-release environment. So the pre-release run of those tests is carved out here: + # still visible (and still failing) on its own line, without turning the whole + # pre-release environment red. The stable environments keep running these tests as part + # of the suite in the `test` and `coverage` jobs above. spatialdata-plot: name: spatialdata-plot tests (PRE-RELEASE DEPENDENCIES) (ubuntu-latest) needs: [ensure-data-is-cached] @@ -276,7 +281,10 @@ jobs: PLATFORM: ubuntu-latest DISPLAY: :42 # No coverage upload: the stable `coverage` job above already covers these tests. - run: uvx hatch run hatch-test.py3.14-pre:run -v --color=yes -n auto tests/experimental/plotting + # `--continue-on-collection-errors` so the marked tests still run and report even + # though tests/experimental/plotting fails to import -- both halves of the picture + # in one place. This job is expected to be red whenever the stack is broken. + run: uvx hatch run hatch-test.py3.14-pre:run -v --color=yes -n auto -m spatialdata_plot --continue-on-collection-errors - name: Archive figures generated during testing if: always() diff --git a/pyproject.toml b/pyproject.toml index 2a9967bb3..be968b5a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -279,6 +279,7 @@ addopts = [ ] markers = [ "internet: tests that require internet", + "spatialdata_plot: tests that render through spatialdata-plot", # the below markers are just there because we import from anndata.tests.helpers, # which means pytest sees them despite us not using them. "gpu: tests that require GPU", diff --git a/src/squidpy/_compat.py b/src/squidpy/_compat.py index 03ef70378..aeed3d7cc 100644 --- a/src/squidpy/_compat.py +++ b/src/squidpy/_compat.py @@ -11,6 +11,8 @@ "panel_grid", "add_colors_for_categorical_sample_annotation", "default_palette", + "default_frameon", + "vector_friendly", # anndata "ArrayView", "SparseCSCView", @@ -45,6 +47,29 @@ from scanpy.plotting._utils import set_default_colors_for_categorical_obs +# Scanpy 1.13 also moved these two plotting defaults off ``Settings``, where they were the +# private ``_frameon`` / ``_vector_friendly`` class attributes, and onto module-level +# globals in ``scanpy.plotting.legacy.mpl_settings``. ``scanpy.set_figure_params`` rebinds +# them in either layout, so they must be read at call time rather than imported once. +try: + from scanpy.plotting.legacy import mpl_settings as _mpl_settings + + def default_frameon() -> bool: + return _mpl_settings.FRAMEON + + def vector_friendly() -> bool: + return _mpl_settings.VECTOR_FRIENDLY + +except ImportError: + from scanpy import settings as _sc_settings + + def default_frameon() -> bool: + return _sc_settings._frameon + + def vector_friendly() -> bool: + return _sc_settings._vector_friendly + + CAN_USE_SPARSE_ARRAY = Version(version("anndata")) >= Version("0.11.0rc1") if CAN_USE_SPARSE_ARRAY: from anndata._core.views import ArrayView diff --git a/src/squidpy/pl/_spatial_utils.py b/src/squidpy/pl/_spatial_utils.py index 9cf5a30e8..d125ddc8b 100644 --- a/src/squidpy/pl/_spatial_utils.py +++ b/src/squidpy/pl/_spatial_utils.py @@ -29,13 +29,12 @@ from matplotlib_scalebar.scalebar import ScaleBar from pandas import CategoricalDtype from scanpy import logging as logg -from scanpy import settings as sc_settings from skimage.color import label2rgb from skimage.morphology import erosion, square from skimage.segmentation import find_boundaries from skimage.util import map_array -from squidpy._compat import add_categorical_legend +from squidpy._compat import add_categorical_legend, default_frameon, vector_friendly from squidpy._constants._constants import ScatterShape from squidpy._constants._pkg_constants import Key from squidpy._utils import NDArrayA @@ -569,7 +568,7 @@ def _plot_edges( ax=ax, **kwargs, ) - edge_collection.set_rasterized(sc_settings._vector_friendly) + edge_collection.set_rasterized(vector_friendly()) ax.add_collection(edge_collection) @@ -904,7 +903,7 @@ def _panel_grid( def _set_ax_title(fig_params: FigParams, count: int, value_to_plot: str | None = None) -> Axes: ax = fig_params.axs[count] if fig_params.axs is not None else fig_params.ax - if not (sc_settings._frameon if fig_params.frameon is None else fig_params.frameon): + if not (default_frameon() if fig_params.frameon is None else fig_params.frameon): ax.axis("off") if fig_params.title is None: @@ -959,7 +958,7 @@ def _plot_scatter( coords[:, 1], s=outline_params.bg_size, c=outline_params.bg_color, - rasterized=sc_settings._vector_friendly, + rasterized=vector_friendly(), cmap=cmap_params.cmap, norm=norm, **kwargs, @@ -970,7 +969,7 @@ def _plot_scatter( coords[:, 1], s=outline_params.gap_size, c=outline_params.gap_color, - rasterized=sc_settings._vector_friendly, + rasterized=vector_friendly(), cmap=cmap_params.cmap, norm=norm, **kwargs, @@ -981,7 +980,7 @@ def _plot_scatter( coords[:, 1], c=np.array(color_vector), s=size, - rasterized=sc_settings._vector_friendly, + rasterized=vector_friendly(), cmap=cmap_params.cmap, norm=norm, **kwargs, diff --git a/tests/experimental/plotting/conftest.py b/tests/experimental/plotting/conftest.py new file mode 100644 index 000000000..1a04471f5 --- /dev/null +++ b/tests/experimental/plotting/conftest.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from pathlib import Path + +HERE = Path(__file__).parent + + +def pytest_collection_modifyitems(items): + """Mark everything in this directory as requiring spatialdata-plot. + + Every module here imports ``spatialdata_plot`` at module level, so applying the + marker from the directory keeps it in one place and covers files added later. + The hook is session-wide even though this conftest is not, hence the path filter. + """ + for item in items: + if item.path.is_relative_to(HERE): + item.add_marker("spatialdata_plot") diff --git a/tests/experimental/test_tiling_qc.py b/tests/experimental/test_tiling_qc.py index b0ec4adcb..93f0b6a58 100644 --- a/tests/experimental/test_tiling_qc.py +++ b/tests/experimental/test_tiling_qc.py @@ -366,6 +366,9 @@ def sdata_with_qc(sdata_tile_boundary): return sdata +# `sq.experimental.pl.tiling_qc` imports spatialdata-plot lazily, so this module +# imports cleanly and only these tests need excluding when that stack is broken. +@pytest.mark.spatialdata_plot class TestTilingQCVisual(PlotTester, metaclass=PlotTesterMeta): def test_plot_tiling_qc_cut_score(self, sdata_with_qc): """Visual: labels coloured by cut_score."""