Skip to content

Commit e9b3122

Browse files
authored
fix: full scanpy 1.13 compatibility layer (#760)
1 parent 5627c9b commit e9b3122

7 files changed

Lines changed: 69 additions & 27 deletions

File tree

src/spatialdata_plot/pl/_color.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from numpy.random import default_rng
2929
from pandas.api.types import CategoricalDtype, is_bool_dtype, is_numeric_dtype, is_string_dtype
3030
from pandas.core.arrays.categorical import Categorical
31-
from scanpy.plotting._utils import add_colors_for_categorical_sample_annotation
3231
from skimage.color import label2rgb
3332
from skimage.morphology import erosion, footprint_rectangle
3433
from skimage.util import map_array
@@ -42,7 +41,12 @@
4241
)
4342

4443
from spatialdata_plot._logging import logger
45-
from spatialdata_plot.pl._scanpy_palettes import default_20, default_28, default_102
44+
from spatialdata_plot.pl._scanpy_compat import (
45+
add_colors_for_categorical_sample_annotation,
46+
default_20,
47+
default_28,
48+
default_102,
49+
)
4650
from spatialdata_plot.pl.render_params import (
4751
CmapParams,
4852
Color,

src/spatialdata_plot/pl/_palette.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from matplotlib.colors import ListedColormap, to_hex, to_rgb
2222
from matplotlib.pyplot import colormaps as mpl_colormaps
2323

24-
from spatialdata_plot.pl._scanpy_palettes import default_20, default_28, default_102
24+
from spatialdata_plot.pl._scanpy_compat import default_20, default_28, default_102
2525

2626
if TYPE_CHECKING:
2727
import spatialdata as sd
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Version-tolerant access to scanpy internals used by spatialdata-plot.
2+
3+
scanpy 1.13 relocated the default palettes and several private plotting helpers from
4+
``scanpy.plotting.{palettes,_tools,_utils}`` to ``scanpy.plotting.legacy.*``, and dropped the
5+
``settings._vector_friendly`` flag. The values and behaviour are unchanged, so we import from
6+
whichever path the installed scanpy exposes and re-export from a single place. This keeps
7+
spatialdata-plot working on scanpy both < and >= 1.13 and confines the reliance on scanpy
8+
internals to one module.
9+
10+
The fallbacks catch ``ModuleNotFoundError`` (not the broader ``ImportError``) so that a legacy
11+
module which exists but fails to import for an unrelated reason surfaces instead of being masked
12+
by a silent fall-back to the old path.
13+
"""
14+
15+
from scanpy import settings as _sc_settings
16+
17+
try: # scanpy >= 1.13
18+
from scanpy.plotting.legacy.palettes import default_20, default_28, default_102
19+
except ModuleNotFoundError: # scanpy < 1.13
20+
from scanpy.plotting.palettes import default_20, default_28, default_102
21+
22+
try: # scanpy >= 1.13
23+
from scanpy.plotting.legacy._tools.scatterplots import _add_categorical_legend
24+
except ModuleNotFoundError: # scanpy < 1.13
25+
from scanpy.plotting._tools.scatterplots import _add_categorical_legend
26+
27+
try: # scanpy >= 1.13
28+
from scanpy.plotting.legacy._utils import add_colors_for_categorical_sample_annotation
29+
except ModuleNotFoundError: # scanpy < 1.13
30+
from scanpy.plotting._utils import add_colors_for_categorical_sample_annotation
31+
32+
33+
def vector_friendly() -> bool:
34+
"""Scanpy's rasterize-for-vector-output flag, read dynamically.
35+
36+
Controls whether scatter/image artists are rasterized (so vector output stays small). scanpy
37+
1.13 removed the ``settings._vector_friendly`` attribute, so on scanpy >= 1.13 this always
38+
returns ``False`` (scanpy's unconfigured default): users who had enabled it via
39+
``sc.set_figure_params(vector_friendly=True)`` lose rasterization there — unavoidable, as the
40+
flag no longer exists. On older scanpy the configured value is still honoured.
41+
"""
42+
return bool(getattr(_sc_settings, "_vector_friendly", False))
43+
44+
45+
__all__ = [
46+
"_add_categorical_legend",
47+
"add_colors_for_categorical_sample_annotation",
48+
"default_20",
49+
"default_28",
50+
"default_102",
51+
"vector_friendly",
52+
]

src/spatialdata_plot/pl/_scanpy_palettes.py

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/spatialdata_plot/pl/render.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
from matplotlib import patheffects
2121
from matplotlib.cm import ScalarMappable
2222
from matplotlib.colors import BoundaryNorm, Colormap, ListedColormap, Normalize, to_rgba_array
23-
from scanpy._settings import settings as sc_settings
24-
from scanpy.plotting._tools.scatterplots import _add_categorical_legend
2523
from spatialdata import get_extent, get_values
2624
from spatialdata.models import PointsModel, ShapesModel, get_table_keys
2725
from spatialdata.transformations import set_transformation
@@ -60,6 +58,7 @@
6058
_scale_geometries,
6159
_validate_polygons,
6260
)
61+
from spatialdata_plot.pl._scanpy_compat import _add_categorical_legend, vector_friendly
6362
from spatialdata_plot.pl._validate import (
6463
_check_obs_var_shadow,
6564
)
@@ -933,7 +932,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
933932
s=render_params.scale,
934933
c=np.array(["white"]), # hack, will be invisible bc fill_alpha=0
935934
render_params=render_params,
936-
rasterized=sc_settings._vector_friendly,
935+
rasterized=vector_friendly(),
937936
cmap=None,
938937
fill_alpha=0.0,
939938
outline_alpha=render_params.outline_alpha[0],
@@ -949,7 +948,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
949948
s=render_params.scale,
950949
c=np.array(["white"]), # hack, will be invisible bc fill_alpha=0
951950
render_params=render_params,
952-
rasterized=sc_settings._vector_friendly,
951+
rasterized=vector_friendly(),
953952
cmap=None,
954953
fill_alpha=0.0,
955954
outline_alpha=render_params.outline_alpha[0],
@@ -966,7 +965,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
966965
s=render_params.scale,
967966
c=np.array(["white"]), # hack, will be invisible bc fill_alpha=0
968967
render_params=render_params,
969-
rasterized=sc_settings._vector_friendly,
968+
rasterized=vector_friendly(),
970969
cmap=None,
971970
fill_alpha=0.0,
972971
outline_alpha=render_params.outline_alpha[1],
@@ -984,7 +983,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
984983
c=color_spec.to_rgba(render_params.cmap_params),
985984
prebuilt_paths=prebuilt_paths,
986985
render_params=render_params,
987-
rasterized=sc_settings._vector_friendly,
986+
rasterized=vector_friendly(),
988987
cmap=render_params.cmap_params.cmap,
989988
fill_alpha=render_params.fill_alpha,
990989
outline_alpha=0.0,
@@ -1072,7 +1071,7 @@ def _scatter_points(
10721071
# `size` at high dpi) only that ring survives, rendering markers as hollow outlines.
10731072
# linewidths=0 keeps them solid fills whose radius honours `size`.
10741073
linewidths=0,
1075-
rasterized=sc_settings._vector_friendly,
1074+
rasterized=vector_friendly(),
10761075
alpha=alpha,
10771076
transform=trans_data,
10781077
zorder=zorder,

src/spatialdata_plot/pl/utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
from pandas.api.types import CategoricalDtype, is_numeric_dtype
3232
from pandas.core.arrays.categorical import Categorical
3333
from scanpy import settings
34-
from scanpy.plotting._tools.scatterplots import _add_categorical_legend
3534
from spatialdata import (
3635
SpatialData,
3736
get_element_annotators,
@@ -56,7 +55,7 @@
5655
from xarray import DataArray, DataTree
5756

5857
from spatialdata_plot._logging import logger
59-
from spatialdata_plot.pl._scanpy_palettes import default_102
58+
from spatialdata_plot.pl._scanpy_compat import _add_categorical_legend, default_102
6059
from spatialdata_plot.pl.render_params import (
6160
Color,
6261
ColorbarSpec,

tests/pl/test_palette.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@ def test_default_returns_dict(self, clustered_sdata: SpatialData):
197197
assert all(v.startswith("#") for v in result.values())
198198

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

202204
result = make_palette_from_data(clustered_sdata, "cells", "cell_type")
203205
for i, cat in enumerate(sorted(result.keys())):

0 commit comments

Comments
 (0)