Repository URL to install this package:
|
Version:
6.4.1 ▾
|
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
import unittest
from traits.api import HasTraits, Instance, Str, Any, Property
class Foo(HasTraits):
s = Str
class ClassWithAny(HasTraits):
x = Property
_x = Any
def _get_x(self):
return self._x
def _set_x(self, x):
self._x = x
class ClassWithInstance(HasTraits):
x = Property
_x = Instance(Foo)
def _get_x(self):
return self._x
def _set_x(self, x):
self._x = x
class ClassWithClassAttribute(HasTraits):
name = "class defined name"
foo = Str
class BazAny(HasTraits):
other = Any
class BarAny(HasTraits):
other = Any
class BazInstance(HasTraits):
# A BarInstance owned by this object.
other = Instance("BarInstance")
# A Foo owned by this object and not referenced by others.
unique = Instance(Foo)
# A Foo owned by this object and referenced by others.
shared = Instance(Foo)
# A Foo not owned by this object, may or may not be shared with other
# objects found via owned references (e.g. other.ref). For the tests,
# ref will always reference a Foo that is not owned by any of the objects
# reachable via owned references, and therefore, that Foo object should
# not be cloned.
ref = Instance(Foo, copy="ref")
class BarInstance(HasTraits):
# used as circular reference back to owning BazInstance
# NOTE: Setting copy to 'ref' will mean that when BarInstance is cloned,
# the 'other' trait will not be copied, and will still point to the
# 'other' attribute of the original BarInstance.
other = Instance("BazInstance", copy="ref")
# A Foo owned by this object and not referenced by others.
unique = Instance(Foo)
# A Foo owned by the 'other' object and referenced by this object.
shared = Instance(Foo)
# A Foo not owned by this object, may or may not be shared with other
# objects found via owned references (e.g. other.ref). For the tests,
# ref will always reference a Foo that is not owned by any of the objects
# reachable via owned references, and therefore, that Foo object should
# not be cloned.
ref = Instance(Foo, copy="ref")
class CloneTestCase(unittest.TestCase):
""" Test cases for traits clone """
def test_any(self):
b = ClassWithAny()
f = Foo()
f.s = "the f"
b.x = f
bc = b.clone_traits(traits="all", copy="deep")
self.assertNotEqual(id(bc.x), id(f), "Foo x not cloned")
def test_instance(self):
b = ClassWithInstance()
f = Foo()
f.s = "the f"
b.x = f
bc = b.clone_traits(traits="all", copy="deep")
self.assertNotEqual(id(bc.x), id(f), "Foo x not cloned")
def test_class_attribute_missing(self):
""" This test demonstrates a problem with Traits objects with class
attributes. A change to the value of a class attribute via one
instance causes the attribute to be removed from other instances.
AttributeError: 'ClassWithClassAttribute' object has no attribute
'name'
"""
s = "class defined name"
c = ClassWithClassAttribute()
self.assertEqual(s, c.name)
c2 = ClassWithClassAttribute()
self.assertEqual(s, c.name)
self.assertEqual(s, c2.name)
s2 = "name class attribute changed via clone"
c2.name = s2
self.assertEqual(s2, c2.name)
# this is failing with AttributeError: 'ClassWithClassAttribute'
# object has no attribute 'name'
self.assertEqual(s, c.name)
def test_Any_circular_references(self):
# Demonstrates that Any traits default to copy='ref'
bar = BarAny()
baz = BazAny()
bar.other = baz
baz.other = bar
bar_copy = bar.clone_traits()
self.assertIsNot(bar_copy, bar)
self.assertIs(bar_copy.other, baz)
self.assertIs(bar_copy.other.other, bar)
def test_Any_circular_references_deep(self):
# Demonstrates that Any traits can be forced to deep copy.
bar = BarAny()
baz = BazAny()
bar.other = baz
baz.other = bar
bar_copy = bar.clone_traits(copy="deep")
self.assertIsNot(bar_copy, bar)
self.assertIsNot(bar_copy.other, baz)
self.assertIsNot(bar_copy.other.other, bar)
self.assertIs(bar_copy.other.other, bar_copy)
def test_Instance_circular_references(self):
ref = Foo(s="ref")
bar_unique = Foo(s="bar.foo")
shared = Foo(s="shared")
baz_unique = Foo(s="baz.unique")
baz = BazInstance()
baz.unique = baz_unique
baz.shared = shared
baz.ref = ref
bar = BarInstance()
bar.unique = bar_unique
bar.shared = shared
bar.ref = ref
bar.other = baz
baz.other = bar
baz_copy = baz.clone_traits()
# Check Baz and Baz attributes....
self.assertIsNot(baz_copy, baz)
self.assertIsNot(baz_copy.other, bar)
self.assertIsNot(baz_copy.unique, baz.unique)
self.assertIsNot(baz_copy.shared, baz.shared)
self.assertIs(baz_copy.ref, ref)
# Check Bar and Bar attributes....
bar_copy = baz_copy.other
# Check the Bar owned object
self.assertIsNot(bar_copy.unique, bar.unique)
# Check the Bar reference to an object 'outside' the cloned graph.
self.assertIs(bar_copy.ref, ref)
# Check references to objects that where cloned, they should reference
# the new clones not the original objects, except when copy is set
# to 'ref' (as in the case of the 'other' trait).
# When copy is set to ref, the trait does not get cloned. Therefore,
# baz_copy.other.other is baz (and not baz_copy).
self.assertIsNot(bar_copy.other, baz_copy)
self.assertIs(bar_copy.other, baz)
# 'shared' does not have copy set to 'ref', and so bar_copy.shared
# should reference the new clone.
# should reference the new clones
self.assertIsNot(bar_copy.shared, baz.shared)
self.assertIs(bar_copy.shared, baz_copy.shared)
def test_Instance_circular_references_deep(self):
ref = Foo(s="ref")
bar_unique = Foo(s="bar.foo")
shared = Foo(s="shared")
baz_unique = Foo(s="baz.unique")
baz = BazInstance()
baz.unique = baz_unique
baz.shared = shared
baz.ref = ref
bar = BarInstance()
bar.unique = bar_unique
bar.shared = shared
bar.ref = ref
bar.other = baz
baz.other = bar
baz_copy = baz.clone_traits(copy="deep")
# Check Baz and Baz attributes....
self.assertIsNot(baz_copy, baz)
self.assertIsNot(baz_copy.other, bar)
self.assertIsNot(baz_copy.unique, baz.unique)
self.assertIsNot(baz_copy.shared, baz.shared)
# baz_copy.ref is checked below with bar_copy.ref.
# Check Bar and Bar attributes....
bar_copy = baz_copy.other
# Check the Bar owned object
self.assertIsNot(bar_copy.unique, bar.unique)
# Since the two original 'ref' links were to a shared object,
# the cloned links should be to a shared object. Also, the shared
# object should be the original 'ref' object, since copy was set to
# 'ref'.
self.assertIs(baz_copy.ref, bar_copy.ref)
self.assertIs(bar_copy.ref, ref)
# Check references to objects that where cloned, they should reference
# the new clones not the original objects, except when copy is set
# to 'ref' (as in the case of the 'other' trait). That is, the 'deep'
# flag on clone_traits should not override the 'copy' metadata on
# the trait.
self.assertIsNot(bar_copy.other, baz_copy)
self.assertIs(bar_copy.other, baz)
# 'shared' does not have copy set to 'ref', and so bar_copy.shared
# should reference the new clone.
self.assertIsNot(bar_copy.shared, baz.shared)
self.assertIs(bar_copy.shared, baz_copy.shared)