From d14fea125a4f5713d2e51902777c0955c9545db3 Mon Sep 17 00:00:00 2001 From: leakyH <41351449+leakyH@users.noreply.github.com> Date: Sat, 19 Jul 2025 15:33:29 +0000 Subject: [PATCH 1/6] extract a _get_bounds_or_viewLim function, which is used both in set_bounds and _adjust_location --- lib/matplotlib/spines.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index 9732a2f3347a..e513264edcec 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -232,12 +232,7 @@ def _clear(self): """ self._position = None # clear position - def _adjust_location(self): - """Automatically set spine bounds to the view interval.""" - - if self.spine_type == 'circle': - return - + def _get_bounds_or_viewLim(self): if self._bounds is not None: low, high = self._bounds elif self.spine_type in ('left', 'right'): @@ -246,6 +241,15 @@ def _adjust_location(self): low, high = self.axes.viewLim.intervalx else: raise ValueError(f'unknown spine spine_type: {self.spine_type}') + return low, high + + def _adjust_location(self): + """Automatically set spine bounds to the view interval.""" + + if self.spine_type == 'circle': + return + + low, high = self._get_bounds_or_viewLim() if self._patch_type == 'arc': if self.spine_type in ('bottom', 'top'): @@ -424,7 +428,7 @@ def set_bounds(self, low=None, high=None): 'set_bounds() method incompatible with circular spines') if high is None and np.iterable(low): low, high = low - old_low, old_high = self.get_bounds() or (None, None) + old_low, old_high = self._get_bounds_or_viewLim() if low is None: low = old_low if high is None: From 07cfd9e2dcd82ccf1480cc76ef5b4f6699ad9033 Mon Sep 17 00:00:00 2001 From: leakyH <41351449+leakyH@users.noreply.github.com> Date: Sat, 19 Jul 2025 19:08:11 +0000 Subject: [PATCH 2/6] add tests --- lib/matplotlib/tests/test_spines.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index d6ddcabb6878..93d8354d5486 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -166,3 +166,32 @@ def test_arc_spine_inner_no_axis(): assert ax.spines["inner"].axis is None fig.draw_without_rendering() + + +def test_spine_set_bounds_with_none(): + """Test that set_bounds(None, ...) uses original axis view limits.""" + fig, ax = plt.subplots() + + # Plot some data to set axis limits + x = np.linspace(0, 10, 100) + y = np.sin(x) + ax.plot(x, y) + + xlim = ax.viewLim.intervalx + ylim = ax.viewLim.intervaly + # Use modified set_bounds with None + ax.spines['bottom'].set_bounds(0, None) + ax.spines['left'].set_bounds(None, None) + + # Check that get_bounds returns correct numeric values + bottom_bound = ax.spines['bottom'].get_bounds() + left_bound = ax.spines['left'].get_bounds() + left_bounds_not_None = (left_bound[0] is not None) and (left_bound[1] is not None) + + assert bottom_bound[1] is not None, "Higher bound should be numeric" + assert left_bounds_not_None, "left bound should be numeric" + assert np.isclose(bottom_bound[0], 0), "Lower bound should match provided value" + assert np.isclose(bottom_bound[1], + xlim[1]), "Upper bound should match original value" + assert np.isclose(left_bound[0], ylim[0]), "Lower bound should match original value" + assert np.isclose(left_bound[1], ylim[1]), "Upper bound should match original value" From 179dd47ccbffbfc7e6fa1faf2fadb00162cc0ae0 Mon Sep 17 00:00:00 2001 From: leakyH <41351449+leakyH@users.noreply.github.com> Date: Sat, 19 Jul 2025 19:53:45 +0000 Subject: [PATCH 3/6] add doc str for _get_bounds_or_viewLim --- lib/matplotlib/spines.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index e513264edcec..31769b2aaca4 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -233,6 +233,12 @@ def _clear(self): self._position = None # clear position def _get_bounds_or_viewLim(self): + """ + Get the bounds of the spine. + + If self._bounds is None, return self.axes.viewLim.intervalx + or self.axes.viewLim.intervaly based on self.spine_type + """ if self._bounds is not None: low, high = self._bounds elif self.spine_type in ('left', 'right'): @@ -240,7 +246,8 @@ def _get_bounds_or_viewLim(self): elif self.spine_type in ('top', 'bottom'): low, high = self.axes.viewLim.intervalx else: - raise ValueError(f'unknown spine spine_type: {self.spine_type}') + raise ValueError(f'spine_type: {self.spine_type} not supported') + # circle not supported return low, high def _adjust_location(self): From 995d69df5494eda59fe7a11e34d438bbda420f04 Mon Sep 17 00:00:00 2001 From: leakyH <41351449+leakyH@users.noreply.github.com> Date: Tue, 22 Jul 2025 01:32:39 +0800 Subject: [PATCH 4/6] Update lib/matplotlib/tests/test_spines.py Set appropriate bottom_bound in the test. Co-authored-by: Ruth Comer <10599679+rcomer@users.noreply.github.com> --- lib/matplotlib/tests/test_spines.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index 93d8354d5486..01b6ee1df948 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -180,7 +180,7 @@ def test_spine_set_bounds_with_none(): xlim = ax.viewLim.intervalx ylim = ax.viewLim.intervaly # Use modified set_bounds with None - ax.spines['bottom'].set_bounds(0, None) + ax.spines['bottom'].set_bounds(2, None) ax.spines['left'].set_bounds(None, None) # Check that get_bounds returns correct numeric values @@ -190,7 +190,7 @@ def test_spine_set_bounds_with_none(): assert bottom_bound[1] is not None, "Higher bound should be numeric" assert left_bounds_not_None, "left bound should be numeric" - assert np.isclose(bottom_bound[0], 0), "Lower bound should match provided value" + assert np.isclose(bottom_bound[0], 2), "Lower bound should match provided value" assert np.isclose(bottom_bound[1], xlim[1]), "Upper bound should match original value" assert np.isclose(left_bound[0], ylim[0]), "Lower bound should match original value" From a8e5b061763d39276b5ac8263836e847afb4918a Mon Sep 17 00:00:00 2001 From: leakyH <41351449+leakyH@users.noreply.github.com> Date: Tue, 22 Jul 2025 01:35:07 +0800 Subject: [PATCH 5/6] Update lib/matplotlib/spines.py The error is mainly raised for user- or third party-defined spines. 'circle' is already handled in two function callers. Co-authored-by: Ruth Comer <10599679+rcomer@users.noreply.github.com> --- lib/matplotlib/spines.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index 31769b2aaca4..741491b3dc58 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -247,7 +247,6 @@ def _get_bounds_or_viewLim(self): low, high = self.axes.viewLim.intervalx else: raise ValueError(f'spine_type: {self.spine_type} not supported') - # circle not supported return low, high def _adjust_location(self): From 0b10b6980e8e326fe8f895fdb43c11c979cfdcdc Mon Sep 17 00:00:00 2001 From: leakyH <41351449+leakyH@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:50:03 +0000 Subject: [PATCH 6/6] regroup spine tests --- lib/matplotlib/tests/test_spines.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index 01b6ee1df948..5aecf6c2ad55 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -185,13 +185,13 @@ def test_spine_set_bounds_with_none(): # Check that get_bounds returns correct numeric values bottom_bound = ax.spines['bottom'].get_bounds() - left_bound = ax.spines['left'].get_bounds() - left_bounds_not_None = (left_bound[0] is not None) and (left_bound[1] is not None) - assert bottom_bound[1] is not None, "Higher bound should be numeric" - assert left_bounds_not_None, "left bound should be numeric" assert np.isclose(bottom_bound[0], 2), "Lower bound should match provided value" assert np.isclose(bottom_bound[1], xlim[1]), "Upper bound should match original value" + + left_bound = ax.spines['left'].get_bounds() + assert (left_bound[0] is not None) and (left_bound[1] is not None), \ + "left bound should be numeric" assert np.isclose(left_bound[0], ylim[0]), "Lower bound should match original value" assert np.isclose(left_bound[1], ylim[1]), "Upper bound should match original value"