Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ffb2e6e

Browse files
authored
Merge pull request #31230 from Aaratrika-Shelly/fix/subplots-reuse-error
API: Raise ValueError in subplots if num refers to existing figure
2 parents 8095b72 + 904d093 commit ffb2e6e

3 files changed

Lines changed: 92 additions & 0 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
``pyplot.subplot`` and ``pyplot.subplot_mosaic`` raise *ValueError* on existing figures
2+
----------------------------------------------------------------------------------------
3+
4+
Passing a *num* argument to `~.pyplot.subplots` or `~.pyplot.subplot_mosaic`
5+
that refers to an existing figure or is a ``Figure`` instance now raises a
6+
`ValueError`.
7+
8+
These utility functions are intended strictly for the creation of new figures and
9+
subplots. Previously, they accidentally allowed the reuse of existing figures because
10+
they internally called `~.pyplot.figure`. This change ensures that these functions
11+
strictly follow their documented purpose of creating new figures.
12+
13+
To reuse an existing figure, clear it first using ``clear=True``:
14+
15+
.. code-block:: python
16+
17+
fig, axs = plt.subplots(num=1, clear=True)
18+
# or
19+
fig, axd = plt.subplot_mosaic([['A', 'B']], num=1, clear=True)
20+
21+
If you have a ``Figure`` instance and want to add subplots to it, use the
22+
object-oriented API:
23+
24+
.. code-block:: python
25+
26+
fig.subplots(nrows=2, ncols=2)
27+
# or
28+
fig.subplot_mosaic([['A', 'B']])

lib/matplotlib/pyplot.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,25 @@ def fignum_exists(num: int | str) -> bool:
11781178
)
11791179

11801180

1181+
def _raise_if_figure_exists(num, func_name, clear=False):
1182+
"""
1183+
Raise a ValueError if the figure *num* already exists.
1184+
"""
1185+
if num is not None and not clear:
1186+
if isinstance(num, FigureBase):
1187+
raise ValueError(
1188+
f"num {num!r} cannot be a FigureBase instance. "
1189+
f"plt.{func_name}() is for creating new figures. "
1190+
f"To add to an existing figure, use fig.{func_name}() "
1191+
"instead.")
1192+
1193+
if fignum_exists(num):
1194+
raise ValueError(
1195+
f"Figure {num!r} already exists. Use plt.figure({num!r}) "
1196+
f"to get it or plt.close({num!r}) to close it. "
1197+
f"Alternatively, pass 'clear=True' to {func_name}().")
1198+
1199+
11811200
def get_fignums() -> list[int]:
11821201
"""Return a list of existing figure numbers."""
11831202
return sorted(_pylab_helpers.Gcf.figs)
@@ -1856,6 +1875,9 @@ def subplots(
18561875
fig, ax = plt.subplots(num=10, clear=True)
18571876
18581877
"""
1878+
num = fig_kw.get('num')
1879+
_raise_if_figure_exists(fig_kw.get('num'), "subplots", fig_kw.get('clear'))
1880+
18591881
fig = figure(**fig_kw)
18601882
axs = fig.subplots(nrows=nrows, ncols=ncols, sharex=sharex, sharey=sharey,
18611883
squeeze=squeeze, subplot_kw=subplot_kw,
@@ -2029,6 +2051,9 @@ def subplot_mosaic(
20292051
total layout.
20302052
20312053
"""
2054+
num = fig_kw.get('num')
2055+
_raise_if_figure_exists(fig_kw.get('num'), "subplot_mosaic", fig_kw.get('clear'))
2056+
20322057
fig = figure(**fig_kw)
20332058
ax_dict = fig.subplot_mosaic( # type: ignore[misc]
20342059
mosaic, # type: ignore[arg-type]

lib/matplotlib/tests/test_pyplot.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,3 +532,42 @@ def assert_same_signature(func1, func2):
532532

533533
def test_setloglevel_signature():
534534
assert_same_signature(plt.set_loglevel, mpl.set_loglevel)
535+
536+
537+
def test_subplots_reuse_existing_figure_error():
538+
"""Test interaction of plt.subplots(num=...) with existing figures."""
539+
# Create a figure with a specific number first.
540+
fig = plt.figure(1)
541+
542+
# Case 1: Reusing without clear=True should raise ValueError
543+
with pytest.raises(ValueError, match="already exists"):
544+
plt.subplots(num=1)
545+
546+
# Case 2: Reusing WITH clear=True should work fine (no error)
547+
fig_new, axs = plt.subplots(num=1, clear=True)
548+
assert fig_new is fig
549+
550+
# Case 3: Test passing the actual Figure object (The "Narrow Check")
551+
with pytest.raises(ValueError, match="cannot be a FigureBase instance"):
552+
plt.subplots(num=fig)
553+
554+
plt.close(1)
555+
556+
557+
def test_subplot_mosaic_reuse_existing_figure_error():
558+
"""Test that plt.subplot_mosaic raises ValueError when reusing a figure."""
559+
fig = plt.figure(2)
560+
561+
# 1. Test passing the existing figure number
562+
with pytest.raises(ValueError, match="already exists"):
563+
plt.subplot_mosaic([['A']], num=2)
564+
565+
# 2. Test passing the actual Figure object
566+
with pytest.raises(ValueError, match="cannot be a FigureBase instance"):
567+
plt.subplot_mosaic([['A']], num=fig)
568+
569+
# 3. Test that clear=True allows reuse without error
570+
fig_new, axd = plt.subplot_mosaic([['A']], num=2, clear=True)
571+
assert fig_new is fig
572+
573+
plt.close(2)

0 commit comments

Comments
 (0)