Skip to content

Commit bd0df1f

Browse files
committed
Backport PR #23462: Fix AttributeError for pickle load of Figure class
There were a number of complications with the backport: - the new test generalized an existing test that had picked up additional changes on the main branch - the signature of the subproccess helper has changed on the main branch
1 parent 4ee298b commit bd0df1f

File tree

2 files changed

+103
-1
lines changed

2 files changed

+103
-1
lines changed

lib/matplotlib/figure.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2919,7 +2919,8 @@ def __setstate__(self, state):
29192919
import matplotlib._pylab_helpers as pylab_helpers
29202920
allnums = plt.get_fignums()
29212921
num = max(allnums) + 1 if allnums else 1
2922-
mgr = plt._backend_mod.new_figure_manager_given_figure(num, self)
2922+
backend = plt._get_backend_mod()
2923+
mgr = backend.new_figure_manager_given_figure(num, self)
29232924
pylab_helpers.Gcf._set_new_active_manager(mgr)
29242925
plt.draw_if_interactive()
29252926

lib/matplotlib/tests/test_pickle.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
from io import BytesIO
2+
import ast
23
import pickle
34

45
import numpy as np
56
import pytest
67

78
from matplotlib import cm
89
from matplotlib.testing.decorators import image_comparison
10+
import matplotlib as mpl
11+
from matplotlib.testing import subprocess_run_helper
12+
from matplotlib.testing.decorators import check_figures_equal
913
from matplotlib.dates import rrulewrapper
1014
import matplotlib.pyplot as plt
1115
import matplotlib.transforms as mtransforms
@@ -110,6 +114,103 @@ def test_complete():
110114
assert fig.get_label() == 'Figure with a label?'
111115

112116

117+
def _pickle_load_subprocess():
118+
import os
119+
import pickle
120+
121+
path = os.environ['PICKLE_FILE_PATH']
122+
123+
with open(path, 'rb') as blob:
124+
fig = pickle.load(blob)
125+
126+
print(str(pickle.dumps(fig)))
127+
128+
129+
def _generate_complete_test_figure(fig_ref):
130+
fig_ref.set_size_inches((10, 6))
131+
plt.figure(fig_ref)
132+
133+
plt.suptitle('Can you fit any more in a figure?')
134+
135+
# make some arbitrary data
136+
x, y = np.arange(8), np.arange(10)
137+
data = u = v = np.linspace(0, 10, 80).reshape(10, 8)
138+
v = np.sin(v * -0.6)
139+
140+
# Ensure lists also pickle correctly.
141+
plt.subplot(3, 3, 1)
142+
plt.plot(list(range(10)))
143+
144+
plt.subplot(3, 3, 2)
145+
plt.contourf(data, hatches=['//', 'ooo'])
146+
plt.colorbar()
147+
148+
plt.subplot(3, 3, 3)
149+
plt.pcolormesh(data)
150+
151+
plt.subplot(3, 3, 4)
152+
plt.imshow(data)
153+
154+
plt.subplot(3, 3, 5)
155+
plt.pcolor(data)
156+
157+
ax = plt.subplot(3, 3, 6)
158+
ax.set_xlim(0, 7)
159+
ax.set_ylim(0, 9)
160+
plt.streamplot(x, y, u, v)
161+
162+
ax = plt.subplot(3, 3, 7)
163+
ax.set_xlim(0, 7)
164+
ax.set_ylim(0, 9)
165+
plt.quiver(x, y, u, v)
166+
167+
plt.subplot(3, 3, 8)
168+
plt.scatter(x, x ** 2, label='$x^2$')
169+
plt.legend(loc='upper left')
170+
171+
plt.subplot(3, 3, 9)
172+
plt.errorbar(x, x * -0.5, xerr=0.2, yerr=0.4)
173+
174+
175+
@mpl.style.context("default")
176+
@check_figures_equal(extensions=['png'])
177+
def test_pickle_load_from_subprocess(fig_test, fig_ref, tmp_path):
178+
_generate_complete_test_figure(fig_ref)
179+
180+
fp = tmp_path / 'sinus.pickle'
181+
assert not fp.exists()
182+
183+
with fp.open('wb') as file:
184+
pickle.dump(fig_ref, file, pickle.HIGHEST_PROTOCOL)
185+
assert fp.exists()
186+
187+
proc = subprocess_run_helper(
188+
_pickle_load_subprocess,
189+
timeout=60,
190+
PICKLE_FILE_PATH=str(fp),
191+
)
192+
193+
loaded_fig = pickle.loads(ast.literal_eval(proc.stdout))
194+
195+
loaded_fig.canvas.draw()
196+
197+
fig_test.set_size_inches(loaded_fig.get_size_inches())
198+
fig_test.figimage(loaded_fig.canvas.renderer.buffer_rgba())
199+
200+
plt.close(loaded_fig)
201+
202+
203+
def test_gcf():
204+
fig = plt.figure("a label")
205+
buf = BytesIO()
206+
pickle.dump(fig, buf, pickle.HIGHEST_PROTOCOL)
207+
plt.close("all")
208+
assert plt._pylab_helpers.Gcf.figs == {} # No figures must be left.
209+
fig = pickle.loads(buf.getbuffer())
210+
assert plt._pylab_helpers.Gcf.figs != {} # A manager is there again.
211+
assert fig.get_label() == "a label"
212+
213+
113214
def test_no_pyplot():
114215
# tests pickle-ability of a figure not created with pyplot
115216
from matplotlib.backends.backend_pdf import FigureCanvasPdf

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy