diff --git a/src/spatialdata_plot/pl/basic.py b/src/spatialdata_plot/pl/basic.py index f864482c..7bb6b4b4 100644 --- a/src/spatialdata_plot/pl/basic.py +++ b/src/spatialdata_plot/pl/basic.py @@ -1519,6 +1519,14 @@ def show( panels = [(cs, None) for cs in coordinate_systems] num_panels = len(panels) + # Titles are panel-level: require one title (broadcast to all panels) or exactly one + # per panel. Validating up front surfaces the error before any drawing, and also rejects + # an over-long list (previously silently truncated). + if title is not None and len(title) not in (1, num_panels): + raise ValueError( + f"The number of titles ({len(title)}) must be 1 or match the number of panels ({num_panels})." + ) + if ax is not None: n_ax = 1 if isinstance(ax, Axes) else len(ax) if num_panels != n_ax: @@ -1794,19 +1802,18 @@ def _draw_colorbar( colorbar_requests=axis_colorbar_requests, ) - if title is None: - t = panel_key if panel_key is not None else cs - elif len(title) == 1: - t = title[0] - else: - try: - t = title[i] - except IndexError as e: - raise IndexError("The number of titles must match the number of panels.") from e - ax.set_title(t) - ax.set_aspect("equal") - if fig_params.frameon is False: - ax.axis("off") + # Title/aspect/frameon are panel-level: set once per panel. + if title is None: + t = panel_key if panel_key is not None else cs + elif len(title) == 1: + t = title[0] + else: + # len(title) == num_panels is guaranteed by the up-front check above. + t = title[i] + ax.set_title(t) + ax.set_aspect("equal") + if fig_params.frameon is False: + ax.axis("off") if has_shapes and wants_shapes: empty_shape_elements = [ diff --git a/tests/pl/test_show.py b/tests/pl/test_show.py index 949a9237..47fade9a 100644 --- a/tests/pl/test_show.py +++ b/tests/pl/test_show.py @@ -136,6 +136,24 @@ def test_fig_parameter_default_no_warning(sdata_blobs: SpatialData): plt.close("all") +def test_title_count_validation(sdata_blobs: SpatialData): + """title must be length 1 or one-per-panel; mismatches raise up front (regression for #695).""" + base = sdata_blobs.pl.render_images(element="blobs_image") + with pytest.raises(ValueError, match="number of titles"): # single panel, too many + base.pl.show(title=["a", "b"], show=False) + plt.close("all") + + set_transformation(sdata_blobs["blobs_image"], Identity(), "second_cs") + base2 = sdata_blobs.pl.render_images(element="blobs_image") + with pytest.raises(ValueError, match="number of titles"): # 2 panels, too many (was silently truncated) + base2.pl.show(title=["a", "b", "c"], show=False) + plt.close("all") + + axs = base2.pl.show(title=["left", "right"], return_ax=True, show=False) # one per panel -> applied + assert sorted(a.get_title() for a in axs) == ["left", "right"] + plt.close("all") + + def test_fig_parameter_warns_with_ax_list(sdata_blobs: SpatialData): """Passing fig= alongside a list of axes should also emit the deprecation (regression for #625).""" set_transformation(sdata_blobs["blobs_image"], Identity(), "second_cs")