-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Make all transforms copiable (and thus scales, too). #19281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
import copy | ||
|
||
import matplotlib.pyplot as plt | ||
from matplotlib.scale import ( | ||
LogTransform, InvertedLogTransform, | ||
|
@@ -210,3 +212,10 @@ def test_pass_scale(): | |
ax.set_yscale(scale) | ||
assert ax.xaxis.get_scale() == 'log' | ||
assert ax.yaxis.get_scale() == 'log' | ||
|
||
|
||
def test_scale_deepcopy(): | ||
sc = mscale.LogScale(axis='x', base=10) | ||
sc2 = copy.deepcopy(sc) | ||
assert str(sc.get_transform()) == str(sc2.get_transform()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a rough check that there is no shared state, I'd add
or, if you're uncomfortable with testing private attributes:
But personally, I'd test the internal state here, because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes (although this revealed the need to separately implement There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 The test found an implementation flaw. It's working 😄 |
||
assert sc._transform is not sc2._transform |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ | |
# `np.minimum` instead of the builtin `min`, and likewise for `max`. This is | ||
# done so that `nan`s are propagated, instead of being silently dropped. | ||
|
||
import copy | ||
import functools | ||
import textwrap | ||
import weakref | ||
|
@@ -139,11 +140,33 @@ def __setstate__(self, data_dict): | |
k: weakref.ref(v, lambda _, pop=self._parents.pop, k=k: pop(k)) | ||
for k, v in self._parents.items() if v is not None} | ||
|
||
def __copy__(self, *args): | ||
raise NotImplementedError( | ||
"TransformNode instances can not be copied. " | ||
"Consider using frozen() instead.") | ||
__deepcopy__ = __copy__ | ||
def __copy__(self): | ||
other = copy.copy(super()) | ||
# If `c = a + b; a1 = copy(a)`, then modifications to `a1` do not | ||
# propagate back to `c`, i.e. we need to clear the parents of `a1`. | ||
other._parents = {} | ||
# If `c = a + b; c1 = copy(c)`, then modifications to `a` also need to | ||
# be propagated to `c1`. | ||
for key, val in vars(self).items(): | ||
if isinstance(val, TransformNode) and id(self) in val._parents: | ||
other.set_children(val) # val == getattr(other, key) | ||
return other | ||
|
||
def __deepcopy__(self, memo): | ||
# We could deepcopy the entire transform tree, but nothing except | ||
# `self` is accessible publicly, so we may as well just freeze `self`. | ||
other = self.frozen() | ||
if other is not self: | ||
return other | ||
# Some classes implement frozen() as returning self, which is not | ||
# acceptable for deepcopying, so we need to handle them separately. | ||
other = copy.deepcopy(super(), memo) | ||
# If `c = a + b; a1 = copy(a)`, then modifications to `a1` do not | ||
timhoffm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# propagate back to `c`, i.e. we need to clear the parents of `a1`. | ||
other._parents = {} | ||
# If `c = a + b; c1 = copy(c)`, this creates a separate tree | ||
timhoffm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# (`c1 = a1 + b1`) so nothing needs to be done. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, so with a deepcopy the tranform is frozen, or at least to the point where someone would need to dig out the children? Does the |
||
return other | ||
|
||
def invalidate(self): | ||
""" | ||
|
Uh oh!
There was an error while loading. Please reload this page.