From 2df31ea5f54a27ab3ef1b021cc24a1ce5f729b04 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Wed, 21 Mar 2018 18:19:53 +0100 Subject: [PATCH 1/4] fix(trait): new TC failing on copy(HasTrait) for #481 clones have shared `_trait_values` with original. --- traitlets/tests/test_traitlets.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/traitlets/tests/test_traitlets.py b/traitlets/tests/test_traitlets.py index 1e2029ae..acb8a634 100644 --- a/traitlets/tests/test_traitlets.py +++ b/traitlets/tests/test_traitlets.py @@ -2598,7 +2598,7 @@ class C(HasTraits): a = Unicode('hard default') def _a_default(self): return 'default method' - + C._a_default = lambda self: 'overridden' c = C() assert c.a == 'overridden' @@ -2609,7 +2609,7 @@ class C(HasTraits): @default('a') def _a_default(self): return 'default method' - + C._a_default = lambda self: 'overridden' c = C() assert c.a == 'overridden' @@ -2620,8 +2620,21 @@ class C(HasTraits): @default('a') def _a_default(self): return 'default method' - + c = C() c._a_default = lambda self: 'overridden' assert c.a == 'overridden' +def test_copy_HasTraits(): + from copy import copy + + class C(HasTraits): + a = Int() + + c = C(a=1) + assert c.a == 1 + + cc = copy(c) + cc.a = 2 + assert cc.a == 2 + assert c.a == 1 From bdd8d5d0d956bce64cad5928f5d58c6ce460654e Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Wed, 21 Mar 2018 18:22:43 +0100 Subject: [PATCH 2/4] fix(trait): #481 clone `_trait-values` dict in __getstate__ --- traitlets/traitlets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/traitlets/traitlets.py b/traitlets/traitlets.py index 4ad3e952..b67ec9d3 100644 --- a/traitlets/traitlets.py +++ b/traitlets/traitlets.py @@ -1088,6 +1088,7 @@ def __getstate__(self): # recall of instance_init during __setstate__ d['_trait_notifiers'] = {} d['_trait_validators'] = {} + d['_trait_values'] = self._trait_values.copy() return d def __setstate__(self, state): From 1d53a217637061face4b60638e163db4ba8f92d5 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Thu, 22 Mar 2018 17:45:24 +0100 Subject: [PATCH 3/4] fix(trait): pin `_cross_validation_lock=False` when cloning - As agreed by rmorshea(https://github.com/ipython/traitlets/issues/481#issuecomment-375124641) & minrk(https://github.com/ipython/traitlets/pull/482#issuecomment-375228575). - Added FIXME remark about pending resolution of what to do in case the lock is held. --- traitlets/traitlets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/traitlets/traitlets.py b/traitlets/traitlets.py index b67ec9d3..be2a8f95 100644 --- a/traitlets/traitlets.py +++ b/traitlets/traitlets.py @@ -1089,6 +1089,8 @@ def __getstate__(self): d['_trait_notifiers'] = {} d['_trait_validators'] = {} d['_trait_values'] = self._trait_values.copy() + d['_cross_validation_lock'] = False # FIXME: raise if cloning locked! + return d def __setstate__(self, state): From d454aa998759f2ea590154363799aa899e355bae Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Thu, 22 Mar 2018 17:50:08 +0100 Subject: [PATCH 4/4] fix(utils): Sentinel must ignore copy/deepcopy + pep8 --- traitlets/utils/sentinel.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/traitlets/utils/sentinel.py b/traitlets/utils/sentinel.py index dc57a259..0760bec8 100644 --- a/traitlets/utils/sentinel.py +++ b/traitlets/utils/sentinel.py @@ -3,6 +3,7 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. + class Sentinel(object): def __init__(self, name, module, docstring=None): @@ -11,7 +12,11 @@ def __init__(self, name, module, docstring=None): if docstring: self.__doc__ = docstring - def __repr__(self): - return str(self.module)+'.'+self.name + return str(self.module) + '.' + self.name + + def __copy__(self): + return self + def __deepcopy__(self, memo): + return self