From 80b56f94d8042ac83e226425ba107f63e2dda9d7 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Sat, 13 Jun 2026 01:57:21 +0200 Subject: [PATCH] Skip hidden offset text when positioning Axes titles The y-axis offset text of an inner subplot that shares its y axis is hidden but still carries text. _update_title_position only checked get_text(), so since #31285 (which makes a hidden text report a null, non-finite tight bbox) the offset text pushed the title -- and the subplot position computed by tight_layout -- to inf/NaN. Drawing then raised "cannot convert float NaN to integer". Only consider the offset text for title placement when it is visible. --- lib/matplotlib/axes/_base.py | 7 ++++++- lib/matplotlib/tests/test_axes.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index c3371a55f77d..c9f99241159c 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3261,7 +3261,12 @@ def _update_title_position(self, renderer): if title.get_text(): for ax in axs: ax.yaxis.get_tightbbox(renderer) # update offsetText - if ax.yaxis.offsetText.get_text(): + # A hidden offset text (e.g. on the shared y axis of an + # inner subplot) is not drawn, so it must not move the + # title: its tight bbox is non-finite and would otherwise + # push the title to infinity. + if (ax.yaxis.offsetText.get_visible() + and ax.yaxis.offsetText.get_text()): bb = ax.yaxis.offsetText.get_tightbbox(renderer) if bb.intersection(title.get_tightbbox(renderer), bb): top = bb.ymax diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 2e3abec8363e..7fedb1828cad 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -8009,6 +8009,22 @@ def test_title_above_offset(left, center): assert ycenter == yleft +def test_title_above_hidden_offset(): + # On an inner subplot with a shared y axis the offset text is hidden, but + # it still carries text. It must not be considered when positioning the + # title: its tight bbox is non-finite and used to push the title (and the + # subplot position computed by tight_layout) to NaN/inf. See #31881. + mpl.rcParams['axes.titley'] = None + fig, axs = plt.subplots(1, 2, sharey=True, tight_layout=True) + for i, ax in enumerate(axs): + ax.set_title(f'Subplot {i}') + ax.plot(range(10), [1e53] * 10) + fig.draw_without_rendering() # used to raise ValueError (NaN -> int) + for ax in axs: + assert np.isfinite(ax.title.get_window_extent().ymin) + assert np.all(np.isfinite(ax.get_position().bounds)) + + def test_title_no_move_off_page(): # If an Axes is off the figure (ie. if it is cropped during a save) # make sure that the automatic title repositioning does not get done.