From 5478183d18d640d2db59b4cb4f508bf88c445fc6 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 13 Aug 2015 23:41:47 -0400 Subject: [PATCH 1/3] BUG: Avoid set_children when unpickling TransformWrapper. Calling set_children may or may not work if the child has been unpickled yet, and it actually causes duplicate parents to be set in the times when it does appear to work. --- lib/matplotlib/transforms.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 0df6ea4c4b12..1bf67e26b392 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1533,6 +1533,10 @@ def __init__(self, child): msg = ("'child' must be an instance of" " 'matplotlib.transform.Transform'") raise ValueError(msg) + self._init(child) + self.set_children(child) + + def _init(self, child): Transform.__init__(self) self.input_dims = child.input_dims self.output_dims = child.output_dims @@ -1553,7 +1557,7 @@ def __getstate__(self): def __setstate__(self, state): # re-initialise the TransformWrapper with the state's child - self.__init__(state['child']) + self._init(state['child']) def __repr__(self): return "TransformWrapper(%r)" % self._child @@ -1564,7 +1568,6 @@ def frozen(self): def _set(self, child): self._child = child - self.set_children(child) self.transform = child.transform self.transform_affine = child.transform_affine @@ -1593,6 +1596,7 @@ def set(self, child): " output dimensions as the current child.") raise ValueError(msg) + self.set_children(child) self._set(child) self._invalid = 0 From 180afeb1b612d8f4621fc9943b1a0cd979e15b42 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 13 Aug 2015 23:53:26 -0400 Subject: [PATCH 2/3] BUG: Also pickle TransformWrapper's parents. Pickling would lose track of the parents for a TransformWrapper because of the override of grandparent's pickling methods. --- lib/matplotlib/transforms.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 1bf67e26b392..e1408fd7a19d 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1552,12 +1552,18 @@ def __str__(self): return str(self._child) def __getstate__(self): - # only store the child - return {'child': self._child} + # only store the child and parents + return { + 'child': self._child, + # turn the weakkey dictionary into a normal dictionary + 'parents': dict(six.iteritems(self._parents)) + } def __setstate__(self, state): # re-initialise the TransformWrapper with the state's child self._init(state['child']) + # turn the normal dictionary back into a WeakValueDictionary + self._parents = WeakValueDictionary(state['parents']) def __repr__(self): return "TransformWrapper(%r)" % self._child From 2047c3a21aeca3ddc9d38f278780c3bd338dbb72 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 12 Sep 2015 21:35:06 -0400 Subject: [PATCH 3/3] TST: Check unpickling of pickled TransformWrappers. --- lib/matplotlib/tests/test_pickle.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 4854edac5429..a5272db57135 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -12,6 +12,7 @@ from matplotlib.testing.decorators import cleanup, image_comparison import matplotlib.pyplot as plt +import matplotlib.transforms as mtransforms def depth_getter(obj, @@ -252,6 +253,34 @@ def test_polar(): plt.draw() +class TransformBlob(object): + def __init__(self): + self.identity = mtransforms.IdentityTransform() + self.identity2 = mtransforms.IdentityTransform() + # Force use of the more complex composition. + self.composite = mtransforms.CompositeGenericTransform( + self.identity, + self.identity2) + # Check parent -> child links of TransformWrapper. + self.wrapper = mtransforms.TransformWrapper(self.composite) + # Check child -> parent links of TransformWrapper. + self.composite2 = mtransforms.CompositeGenericTransform( + self.wrapper, + self.identity) + + +def test_transform(): + obj = TransformBlob() + pf = pickle.dumps(obj) + del obj + + obj = pickle.loads(pf) + # Check parent -> child links of TransformWrapper. + assert_equal(obj.wrapper._child, obj.composite) + # Check child -> parent links of TransformWrapper. + assert_equal(list(obj.wrapper._parents.values()), [obj.composite2]) + + if __name__ == '__main__': import nose nose.runmodule(argv=['-s'])