Skip to content

Commit 18069cb

Browse files
committed
fix: full scanpy 1.13 compatibility layer
scanpy 1.13 reorganized its plotting internals, breaking spatialdata-plot on pre-release scanpy. Consolidate every reliance on scanpy internals into one version-tolerant module, _scanpy_compat, that imports each symbol from the new location with a fallback to the old: - palettes default_20/28/102 scanpy.plotting.palettes -> .legacy.palettes - _add_categorical_legend scanpy.plotting._tools... -> .legacy._tools... - add_colors_for_categorical_... scanpy.plotting._utils -> .legacy._utils - vector_friendly() settings._vector_friendly removed in 1.13; default False Route all call sites (render.py, utils.py, _color.py, _palette.py) through _scanpy_compat. Behaviour is unchanged: the relocated palettes/helpers are frozen copies (identical values and signatures), and vector_friendly keeps honouring sc.set_figure_params on older scanpy. scanpy public API still used directly (sc.get.obs_df, settings.figdir, set_figure_params) is unaffected. Verified: the full non-visual suite passes and no scanpy AttributeError/ ImportError occurs when resolved against scanpy 1.13.0a1.
1 parent 5627c9b commit 18069cb

6 files changed

Lines changed: 60 additions & 26 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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
11+
from scanpy._settings import settings as _sc_settings
12+
13+
try: # scanpy >= 1.13
14+
from scanpy.plotting.legacy.palettes import default_20, default_28, default_102
15+
except ImportError: # scanpy < 1.13
16+
from scanpy.plotting.palettes import default_20, default_28, default_102
17+
18+
try: # scanpy >= 1.13
19+
from scanpy.plotting.legacy._tools.scatterplots import _add_categorical_legend
20+
except ImportError: # scanpy < 1.13
21+
from scanpy.plotting._tools.scatterplots import _add_categorical_legend
22+
23+
try: # scanpy >= 1.13
24+
from scanpy.plotting.legacy._utils import add_colors_for_categorical_sample_annotation
25+
except ImportError: # scanpy < 1.13
26+
from scanpy.plotting._utils import add_colors_for_categorical_sample_annotation
27+
28+
def vector_friendly() -> bool:
29+
"""scanpy's rasterize-for-vector-output flag, read dynamically.
30+
31+
Controls whether scatter/image artists are rasterized (so vector output stays small). scanpy
32+
1.13 removed the ``settings._vector_friendly`` attribute; default to ``False`` there (scanpy's
33+
historical default), while still honouring ``sc.set_figure_params(vector_friendly=...)`` on
34+
older scanpy.
35+
"""
36+
return bool(getattr(_sc_settings, "_vector_friendly", False))
37+
38+
39+
__all__ = [
40+
"_add_categorical_legend",
41+
"add_colors_for_categorical_sample_annotation",
42+
"default_20",
43+
"default_28",
44+
"default_102",
45+
"vector_friendly",
46+
]

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,

0 commit comments

Comments
 (0)