8000 MNT: Deprecate changing Figure.number · matplotlib/matplotlib@b81c2c9 · GitHub
[go: up one dir, main page]

Skip to content

Commit b81c2c9

Browse files
committed
MNT: Deprecate changing Figure.number
Historically, pyplot dynamically added a number attribute to figure. However, this number must stay in sync with the figure manger. AFAICS overwriting the number attribute does not have the desired effect for pyplot. But there are some repos in GitHub that do change number. So let's take it slow and properly migrate away from writing. Making the dynamic attribute private and wrapping it in a property allows to maintain current behavior and deprecate write-access. When the deprecation expires, there's no need for duplicate state anymore and the private _number attribute can be replaced by `self.canvas.manager.num` if that exists and None otherwise. Also closes #28994.
1 parent ac1d009 commit b81c2c9

File tree

5 files changed

+64
-1
lines changed

5 files changed

+64
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Changing ``Figure.number``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Changing ``Figure.number`` is deprecated. This value is used by `.pyplot`
5+
to identify figures. It must stay in sync with the pyplot internal state
6+
and is not intended to be modified by the user.

lib/matplotlib/_pylab_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def _set_new_active_manager(cls, manager):
108108
manager._cidgcf = manager.canvas.mpl_connect(
109109
"button_press_event", lambda event: cls.set_active(manager))
110110
fig = manager.canvas.figure
111-
fig.number = manager.num
111+
fig._number = manager.num
112112
label = fig.get_label()
113113
if label:
114114
manager.set_window_title(label)

lib/matplotlib/figure.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2808,6 +2808,36 @@ def axes(self):
28082808

28092809
get_axes = axes.fget
28102810

2811+
@property
2812+
def number(self):
2813+
"""The figure id, used to identify figures in `.pyplot`."""
2814+
# Historically, pyplot dynamically added a number attribute to figure.
2815+
# However, this number must stay in sync with the figure manager.
2816+
# AFAICS overwriting the number attribute does not have the desired
2817+
# effect for pyplot. But there are some repos in GitHub that do change
2818+
# number. So let's take it slow and properly migrate away from writing.
2819+
#
2820+
# Making the dynamic attribute private and wrapping it in a property
2821+
# allows to maintain current behavior and deprecate write-access.
2822+
#
2823+
# When the deprecation expires, there's no need for duplicate state
2824+
# anymore and the private _number attribute can be replaced by
2825+
# `self.canvas.manager.num` if that exists and None otherwise.
2826+
if hasattr(self, '_number'):
2827+
return self._number
2828+
else:
2829+
raise AttributeError(
2830+
"'Figure' object has no attribute 'number'. In the future this"
2831+
"will change to returning 'None' instead.")
2832+
2833+
@number.setter
2834+
def number(self, num):
2835+
_api.warn_deprecated(
2836+
"3.10",
2837+
message="Changing 'Figure.number' is deprecated since %(since)s and "
2838+
"will raise an error starting %(removal)s")
2839+
self._number = num
2840+
28112841
def _get_renderer(self):
28122842
if hasattr(self.canvas, 'get_renderer'):
28132843
return self.canvas.get_renderer()

lib/matplotlib/figure.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ class Figure(FigureBase):
343343
def get_layout_engine(self) -> LayoutEngine | None: ...
344344
def _repr_html_(self) -> str | None: ...
345345
def show(self, warn: bool = ...) -> None: ...
346+
@property
347+
def number(self) -> int | str: ...
348+
@number.setter
349+
def number(self, num: int | str) -> None: ...
346350
@property # type: ignore[misc]
347351
def axes(self) -> list[Axes]: ... # type: ignore[override]
348352
def get_axes(self) -> list[Axes]: ...

lib/matplotlib/tests/test_figure.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,29 @@ def test_figure_label():
151151
plt.figure(Figure())
152152

153153

154+
def test_figure_label_replaced():
155+
plt.close('all')
156+
fig = plt.figure(1)
157+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
158+
match="Changing 'Figure.number' is deprecated"):
159+
fig.number = 2
160+
assert fig.number == 2
161+
162+
163+
def test_figure_no_label():
164+
# standalone figures do not have a figure attribute
165+
fig = Figure()
166+
with pytest.raises(AttributeError):
167+
fig.number
168+
# but one can set one
169+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
170+
match="Changing 'Figure.number' is deprecated"):
171+
fig.number = 5
172+
assert fig.number == 5
173+
# even though it's not known by pyplot
174+
assert not plt.fignum_exists(fig.number)
175+
176+
154177
def test_fignum_exists():
155178
# pyplot figure creation, selection and closing with fignum_exists
156179
plt.figure('one')

0 commit comments

Comments
 (0)