Skip to content

Commit 970cb57

Browse files
committed
Restore axes sharedness when unpickling.
Previously, pickling and unpickling shared axes would result in axes sharing a ticker instance (because that's how shared axes are set up), but without changes of one's xlims propagated to the other. The reason is that that sharedness information is stored in AxesBase._shared_x_axes, which does *not* get pickled together with the Axes instance: the latter only has a textual reference "I am an instance of AxesBase", so the Grouper information is lost. To keep the Grouper information valid, instead move the Groupers to the instance dictionaries (as references to global groupers). Also make Groupers picklable following a similar strategy as Transforms, i.e. by transforming weakrefs into real refs when pickling and transforming them back into weakref when unpickling.
1 parent e54939a commit 970cb57

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

lib/matplotlib/axes/_base.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -406,15 +406,16 @@ def _grab_next_args(self, *args, **kwargs):
406406
yield from self._plot_args(this, kwargs)
407407

408408

409+
_shared_x_axes = cbook.Grouper()
410+
_shared_y_axes = cbook.Grouper()
411+
_twinned_axes = cbook.Grouper()
412+
413+
409414
class _AxesBase(martist.Artist):
410415
"""
411416
"""
412417
name = "rectilinear"
413418

414-
_shared_x_axes = cbook.Grouper()
415-
_shared_y_axes = cbook.Grouper()
416-
_twinned_axes = cbook.Grouper()
417-
418419
def __str__(self):
419420
return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})".format(
420421
type(self).__name__, self._position.bounds)
@@ -479,6 +480,13 @@ def __init__(self, fig, rect,
479480
""" % {'scale': ' | '.join(
480481
[repr(x) for x in mscale.get_scale_names()])}
481482
martist.Artist.__init__(self)
483+
484+
# Reference the global instances in the instance dict to support
485+
# pickling.
486+
self._shared_x_axes = _shared_x_axes
487+
self._shared_y_axes = _shared_y_axes
488+
self._twinned_axes = _twinned_axes
489+
482490
if isinstance(rect, mtransforms.Bbox):
483491
self._position = rect
484492
else:

lib/matplotlib/cbook/__init__.py

+20-3
Original file line numberDiff line numberDiff line change
@@ -1479,9 +1479,26 @@ class Grouper(object):
14791479
14801480
"""
14811481
def __init__(self, init=()):
1482-
mapping = self._mapping = {}
1483-
for x in init:
1484-
mapping[ref(x)] = [ref(x)]
1482+
self._mapping = {ref(x): [ref(x)] for x in init}
1483+
1484+
def __getstate__(self):
1485+
mapping = {}
1486+
for k, vs in self._mapping.items():
1487+
k = k()
1488+
if k is None:
1489+
continue
1490+
mapping[k] = l = []
1491+
for v in vs:
1492+
v = v()
1493+
if v is None:
1494+
continue
1495+
l.append(v)
1496+
return {"_mapping": mapping}
1497+
1498+
def __setstate__(self, state):
1499+
self.__dict__ = state
1500+
self._mapping = {ref(k): [ref(v) for v in vs]
1501+
for k, vs in self._mapping.items()}
14851502

14861503
def __contains__(self, item):
14871504
return ref(item) in self._mapping

lib/matplotlib/tests/test_pickle.py

+7
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,10 @@ def test_rrulewrapper():
189189
except RecursionError:
190190
print('rrulewrapper pickling test failed')
191191
raise
192+
193+
194+
def test_shared():
195+
fig, axs = plt.subplots(2, sharex=True)
196+
fig = pickle.loads(pickle.dumps(fig))
197+
fig.axes[0].set_xlim(10, 20)
198+
assert fig.axes[1].get_xlim() == (10, 20)

0 commit comments

Comments
 (0)