Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/spatialdata_plot/pl/_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from numpy.random import default_rng
from pandas.api.types import CategoricalDtype, is_bool_dtype, is_numeric_dtype, is_string_dtype
from pandas.core.arrays.categorical import Categorical
from scanpy.plotting._utils import add_colors_for_categorical_sample_annotation
from skimage.color import label2rgb
from skimage.morphology import erosion, footprint_rectangle
from skimage.util import map_array
Expand All @@ -42,7 +41,12 @@
)

from spatialdata_plot._logging import logger
from spatialdata_plot.pl._scanpy_palettes import default_20, default_28, default_102
from spatialdata_plot.pl._scanpy_compat import (
add_colors_for_categorical_sample_annotation,
default_20,
default_28,
default_102,
)
from spatialdata_plot.pl.render_params import (
CmapParams,
Color,
Expand Down
2 changes: 1 addition & 1 deletion src/spatialdata_plot/pl/_palette.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from matplotlib.colors import ListedColormap, to_hex, to_rgb
from matplotlib.pyplot import colormaps as mpl_colormaps

from spatialdata_plot.pl._scanpy_palettes import default_20, default_28, default_102
from spatialdata_plot.pl._scanpy_compat import default_20, default_28, default_102

if TYPE_CHECKING:
import spatialdata as sd
Expand Down
52 changes: 52 additions & 0 deletions src/spatialdata_plot/pl/_scanpy_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Version-tolerant access to scanpy internals used by spatialdata-plot.

scanpy 1.13 relocated the default palettes and several private plotting helpers from
``scanpy.plotting.{palettes,_tools,_utils}`` to ``scanpy.plotting.legacy.*``, and dropped the
``settings._vector_friendly`` flag. The values and behaviour are unchanged, so we import from
whichever path the installed scanpy exposes and re-export from a single place. This keeps
spatialdata-plot working on scanpy both < and >= 1.13 and confines the reliance on scanpy
internals to one module.

The fallbacks catch ``ModuleNotFoundError`` (not the broader ``ImportError``) so that a legacy
module which exists but fails to import for an unrelated reason surfaces instead of being masked
by a silent fall-back to the old path.
"""

from scanpy import settings as _sc_settings

try: # scanpy >= 1.13
from scanpy.plotting.legacy.palettes import default_20, default_28, default_102
except ModuleNotFoundError: # scanpy < 1.13
from scanpy.plotting.palettes import default_20, default_28, default_102

try: # scanpy >= 1.13
from scanpy.plotting.legacy._tools.scatterplots import _add_categorical_legend
except ModuleNotFoundError: # scanpy < 1.13
from scanpy.plotting._tools.scatterplots import _add_categorical_legend

try: # scanpy >= 1.13
from scanpy.plotting.legacy._utils import add_colors_for_categorical_sample_annotation
except ModuleNotFoundError: # scanpy < 1.13
from scanpy.plotting._utils import add_colors_for_categorical_sample_annotation


def vector_friendly() -> bool:
"""Scanpy's rasterize-for-vector-output flag, read dynamically.

Controls whether scatter/image artists are rasterized (so vector output stays small). scanpy
1.13 removed the ``settings._vector_friendly`` attribute, so on scanpy >= 1.13 this always
returns ``False`` (scanpy's unconfigured default): users who had enabled it via
``sc.set_figure_params(vector_friendly=True)`` lose rasterization there — unavoidable, as the
flag no longer exists. On older scanpy the configured value is still honoured.
"""
return bool(getattr(_sc_settings, "_vector_friendly", False))


__all__ = [
"_add_categorical_legend",
"add_colors_for_categorical_sample_annotation",
"default_20",
"default_28",
"default_102",
"vector_friendly",
]
14 changes: 0 additions & 14 deletions src/spatialdata_plot/pl/_scanpy_palettes.py

This file was deleted.

13 changes: 6 additions & 7 deletions src/spatialdata_plot/pl/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
from matplotlib import patheffects
from matplotlib.cm import ScalarMappable
from matplotlib.colors import BoundaryNorm, Colormap, ListedColormap, Normalize, to_rgba_array
from scanpy._settings import settings as sc_settings
from scanpy.plotting._tools.scatterplots import _add_categorical_legend
from spatialdata import get_extent, get_values
from spatialdata.models import PointsModel, ShapesModel, get_table_keys
from spatialdata.transformations import set_transformation
Expand Down Expand Up @@ -60,6 +58,7 @@
_scale_geometries,
_validate_polygons,
)
from spatialdata_plot.pl._scanpy_compat import _add_categorical_legend, vector_friendly
from spatialdata_plot.pl._validate import (
_check_obs_var_shadow,
)
Expand Down Expand Up @@ -933,7 +932,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
s=render_params.scale,
c=np.array(["white"]), # hack, will be invisible bc fill_alpha=0
render_params=render_params,
rasterized=sc_settings._vector_friendly,
rasterized=vector_friendly(),
cmap=None,
fill_alpha=0.0,
outline_alpha=render_params.outline_alpha[0],
Expand All @@ -949,7 +948,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
s=render_params.scale,
c=np.array(["white"]), # hack, will be invisible bc fill_alpha=0
render_params=render_params,
rasterized=sc_settings._vector_friendly,
rasterized=vector_friendly(),
cmap=None,
fill_alpha=0.0,
outline_alpha=render_params.outline_alpha[0],
Expand All @@ -966,7 +965,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
s=render_params.scale,
c=np.array(["white"]), # hack, will be invisible bc fill_alpha=0
render_params=render_params,
rasterized=sc_settings._vector_friendly,
rasterized=vector_friendly(),
cmap=None,
fill_alpha=0.0,
outline_alpha=render_params.outline_alpha[1],
Expand All @@ -984,7 +983,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
c=color_spec.to_rgba(render_params.cmap_params),
prebuilt_paths=prebuilt_paths,
render_params=render_params,
rasterized=sc_settings._vector_friendly,
rasterized=vector_friendly(),
cmap=render_params.cmap_params.cmap,
fill_alpha=render_params.fill_alpha,
outline_alpha=0.0,
Expand Down Expand Up @@ -1072,7 +1071,7 @@ def _scatter_points(
# `size` at high dpi) only that ring survives, rendering markers as hollow outlines.
# linewidths=0 keeps them solid fills whose radius honours `size`.
linewidths=0,
rasterized=sc_settings._vector_friendly,
rasterized=vector_friendly(),
alpha=alpha,
transform=trans_data,
zorder=zorder,
Expand Down
3 changes: 1 addition & 2 deletions src/spatialdata_plot/pl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from pandas.api.types import CategoricalDtype, is_numeric_dtype
from pandas.core.arrays.categorical import Categorical
from scanpy import settings
from scanpy.plotting._tools.scatterplots import _add_categorical_legend
from spatialdata import (
SpatialData,
get_element_annotators,
Expand All @@ -56,7 +55,7 @@
from xarray import DataArray, DataTree

from spatialdata_plot._logging import logger
from spatialdata_plot.pl._scanpy_palettes import default_102
from spatialdata_plot.pl._scanpy_compat import _add_categorical_legend, default_102
from spatialdata_plot.pl.render_params import (
Color,
ColorbarSpec,
Expand Down
4 changes: 3 additions & 1 deletion tests/pl/test_palette.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ def test_default_returns_dict(self, clustered_sdata: SpatialData):
assert all(v.startswith("#") for v in result.values())

def test_default_matches_scanpy_order(self, clustered_sdata: SpatialData):
from scanpy.plotting.palettes import default_20
# Import via the compat shim so this resolves across scanpy versions (1.13 moved the
# palettes to scanpy.plotting.legacy.palettes); it's the same source make_palette uses.
from spatialdata_plot.pl._scanpy_compat import default_20

result = make_palette_from_data(clustered_sdata, "cells", "cell_type")
for i, cat in enumerate(sorted(result.keys())):
Expand Down
Loading