Skip to content

Commit bd262e9

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 e22a16a commit bd262e9

File tree

3 files changed

+38
-4
lines changed

3 files changed

+38
-4
lines changed

lib/matplotlib/axes/_base.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,15 +395,16 @@ def _grab_next_args(self, *args, **kwargs):
395395
yield from self._plot_args(this, kwargs)
396396

397397

398+
_shared_x_axes = cbook.Grouper()
399+
_shared_y_axes = cbook.Grouper()
400+
_twinned_axes = cbook.Grouper()
401+
402+
398403
class _AxesBase(martist.Artist):
399404
"""
400405
"""
401406
name = "rectilinear"
402407

403-
_shared_x_axes = cbook.Grouper()
404-
_shared_y_axes = cbook.Grouper()
405-
_twinned_axes = cbook.Grouper()
406-
407408
def __str__(self):
408409
return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})".format(
409410
type(self).__name__, self._position.bounds)
@@ -468,6 +469,13 @@ def __init__(self, fig, rect,
468469
""" % {'scale': ' | '.join(
469470
[repr(x) for x in mscale.get_scale_names()])}
470471
martist.Artist.__init__(self)
472+
473+
# Reference the global instances in the instance dict to support
474+
# pickling.
475+
self._shared_x_axes = _shared_x_axes
476+
self._shared_y_axes = _shared_y_axes
477+
self._twinned_axes = _twinned_axes
478+
471479
if isinstance(rect, mtransforms.Bbox):
472480
self._position = rect
473481
else:

lib/matplotlib/cbook/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,25 @@ class Grouper(object):
981981
def __init__(self, init=()):
982982
self._mapping = {ref(x): [ref(x)] for x in init}
983983

984+
def __getstate__(self):
985+
mapping = {}
986+
for k, vs in self._mapping.items():
987+
k = k()
988+
if k is None:
989+
continue
990+
mapping[k] = l = []
991+
for v in vs:
992+
v = v()
993+
if v is None:
994+
continue
995+
l.append(v)
996+
return {"_mapping": mapping}
997+
998+
def __setstate__(self, state):
999+
self.__dict__ = state
1000+
self._mapping = {ref(k): [ref(v) for v in vs]
1001+
for k, vs in self._mapping.items()}
1002+
9841003
def __contains__(self, item):
9851004
return ref(item) in self._mapping
9861005

lib/matplotlib/tests/test_pickle.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,10 @@ def test_rrulewrapper():
187187
except RecursionError:
188188
print('rrulewrapper pickling test failed')
189189
raise
190+
191+
192+
def test_shared():
193+
fig, axs = plt.subplots(2, sharex=True)
194+
fig = pickle.loads(pickle.dumps(fig))
195+
fig.axes[0].set_xlim(10, 20)
196+
assert fig.axes[1].get_xlim() == (10, 20)

0 commit comments

Comments
 (0)