diff --git a/traits/tests/test_comparison_mode.py b/traits/tests/test_comparison_mode.py new file mode 100644 index 000000000..82419a1bb --- /dev/null +++ b/traits/tests/test_comparison_mode.py @@ -0,0 +1,246 @@ +# (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 ( + Any, + cached_property, + ComparisonMode, + HasTraits, + Property, + Str, +) + + +class NoneCompare(HasTraits): + bar = Any(comparison_mode=ComparisonMode.none) + + +class IdentityCompare(HasTraits): + bar = Any(comparison_mode=ComparisonMode.identity) + + +class EqualityCompare(HasTraits): + bar = Any(comparison_mode=ComparisonMode.equality) + + +class Foo(HasTraits): + """ + Class implementing custom equality. + """ + + name = Str + + def __eq__(self, other): + return self.name == other.name + + +class TestComparisonMode(unittest.TestCase): + def setUp(self): + self.a = Foo(name="a") + self.same_as_a = Foo(name="a") + self.different_from_a = Foo(name="not a") + + def bar_changed(self, object, trait, old, new): + self.changed_object = object + self.changed_trait = trait + self.changed_old = old + self.changed_new = new + self.changed_count += 1 + + def reset_change_tracker(self): + self.changed_object = None + self.changed_trait = None + self.changed_old = None + self.changed_new = None + self.changed_count = 0 + + def check_tracker(self, object, trait, old, new, count): + self.assertEqual(count, self.changed_count) + self.assertIs(object, self.changed_object) + self.assertEqual(trait, self.changed_trait) + self.assertIs(old, self.changed_old) + self.assertIs(new, self.changed_new) + + def test_none_first_assignment(self): + nc = NoneCompare() + nc.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = nc.bar + nc.bar = self.a + self.check_tracker(nc, "bar", default_value, self.a, 1) + + def test_identity_first_assignment(self): + ic = IdentityCompare() + ic.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ic.bar + ic.bar = self.a + self.check_tracker(ic, "bar", default_value, self.a, 1) + + def test_equality_first_assignment(self): + ec = EqualityCompare() + ec.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ec.bar + ec.bar = self.a + self.check_tracker(ec, "bar", default_value, self.a, 1) + + def test_none_same_object(self): + nc = NoneCompare() + nc.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = nc.bar + nc.bar = self.a + self.check_tracker(nc, "bar", default_value, self.a, 1) + + nc.bar = self.a + self.check_tracker(nc, "bar", self.a, self.a, 2) + + def test_identity_same_object(self): + ic = IdentityCompare() + ic.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ic.bar + ic.bar = self.a + self.check_tracker(ic, "bar", default_value, self.a, 1) + + ic.bar = self.a + self.check_tracker(ic, "bar", default_value, self.a, 1) + + def test_equality_same_object(self): + ec = EqualityCompare() + ec.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ec.bar + ec.bar = self.a + self.check_tracker(ec, "bar", default_value, self.a, 1) + + ec.bar = self.a + self.check_tracker(ec, "bar", default_value, self.a, 1) + + def test_none_different_object(self): + nc = NoneCompare() + nc.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = nc.bar + nc.bar = self.a + self.check_tracker(nc, "bar", default_value, self.a, 1) + + nc.bar = self.different_from_a + self.check_tracker(nc, "bar", self.a, self.different_from_a, 2) + + def test_identity_different_object(self): + ic = IdentityCompare() + ic.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ic.bar + ic.bar = self.a + self.check_tracker(ic, "bar", default_value, self.a, 1) + + ic.bar = self.different_from_a + self.check_tracker(ic, "bar", self.a, self.different_from_a, 2) + + def test_equality_different_object(self): + ec = EqualityCompare() + ec.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ec.bar + ec.bar = self.a + self.check_tracker(ec, "bar", default_value, self.a, 1) + + ec.bar = self.different_from_a + self.check_tracker(ec, "bar", self.a, self.different_from_a, 2) + + def test_none_different_object_same_as(self): + nc = NoneCompare() + nc.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = nc.bar + nc.bar = self.a + self.check_tracker(nc, "bar", default_value, self.a, 1) + + nc.bar = self.same_as_a + self.check_tracker(nc, "bar", self.a, self.same_as_a, 2) + + def test_identity_different_object_same_as(self): + ic = IdentityCompare() + ic.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ic.bar + ic.bar = self.a + self.check_tracker(ic, "bar", default_value, self.a, 1) + + ic.bar = self.same_as_a + self.check_tracker(ic, "bar", self.a, self.same_as_a, 2) + + def test_equality_different_object_same_as(self): + ec = EqualityCompare() + ec.on_trait_change(self.bar_changed, "bar") + + self.reset_change_tracker() + + default_value = ec.bar + ec.bar = self.a + self.check_tracker(ec, "bar", default_value, self.a, 1) + + # Values of a and same_as_a are the same and should therefore not + # be considered a change. + ec.bar = self.same_as_a + self.check_tracker(ec, "bar", default_value, self.a, 1) + + def test_comparison_mode_none_with_cached_property(self): + # Even though the property is cached such that old value equals new + # value, its change event is tied to the dependent. + + class Model(HasTraits): + value = Property(depends_on="name") + name = Str(comparison_mode=ComparisonMode.none) + + @cached_property + def _get_value(self): + return self.trait_names + + instance = Model() + events = [] + instance.on_trait_change(lambda: events.append(None), "value") + + instance.name = "A" + + events.clear() + + # when + instance.name = "A" + + # then + self.assertEqual(len(events), 1) diff --git a/traits/tests/test_rich_compare.py b/traits/tests/test_rich_compare.py deleted file mode 100644 index 96007750f..000000000 --- a/traits/tests/test_rich_compare.py +++ /dev/null @@ -1,272 +0,0 @@ -# (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 -import warnings - -from traits.api import ( - Any, cached_property, ComparisonMode, HasTraits, Property, Str, -) - - -class IdentityCompare(HasTraits): - bar = Any(comparison_mode=ComparisonMode.identity) - - -class RichCompare(HasTraits): - bar = Any(comparison_mode=ComparisonMode.equality) - - -class RichCompareTests: - def bar_changed(self, object, trait, old, new): - self.changed_object = object - self.changed_trait = trait - self.changed_old = old - self.changed_new = new - self.changed_count += 1 - - def reset_change_tracker(self): - self.changed_object = None - self.changed_trait = None - self.changed_old = None - self.changed_new = None - self.changed_count = 0 - - def check_tracker(self, object, trait, old, new, count): - self.assertEqual(count, self.changed_count) - self.assertIs(object, self.changed_object) - self.assertEqual(trait, self.changed_trait) - self.assertIs(old, self.changed_old) - self.assertIs(new, self.changed_new) - - def test_id_first_assignment(self): - ic = IdentityCompare() - ic.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = ic.bar - ic.bar = self.a - self.check_tracker(ic, "bar", default_value, self.a, 1) - - def test_rich_first_assignment(self): - rich = RichCompare() - rich.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = rich.bar - rich.bar = self.a - self.check_tracker(rich, "bar", default_value, self.a, 1) - - def test_id_same_object(self): - ic = IdentityCompare() - ic.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = ic.bar - ic.bar = self.a - self.check_tracker(ic, "bar", default_value, self.a, 1) - - ic.bar = self.a - self.check_tracker(ic, "bar", default_value, self.a, 1) - - def test_rich_same_object(self): - rich = RichCompare() - rich.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = rich.bar - rich.bar = self.a - self.check_tracker(rich, "bar", default_value, self.a, 1) - - rich.bar = self.a - self.check_tracker(rich, "bar", default_value, self.a, 1) - - def test_id_different_object(self): - ic = IdentityCompare() - ic.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = ic.bar - ic.bar = self.a - self.check_tracker(ic, "bar", default_value, self.a, 1) - - ic.bar = self.different_from_a - self.check_tracker(ic, "bar", self.a, self.different_from_a, 2) - - def test_rich_different_object(self): - rich = RichCompare() - rich.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = rich.bar - rich.bar = self.a - self.check_tracker(rich, "bar", default_value, self.a, 1) - - rich.bar = self.different_from_a - self.check_tracker(rich, "bar", self.a, self.different_from_a, 2) - - def test_id_different_object_same_as(self): - ic = IdentityCompare() - ic.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = ic.bar - ic.bar = self.a - self.check_tracker(ic, "bar", default_value, self.a, 1) - - ic.bar = self.same_as_a - self.check_tracker(ic, "bar", self.a, self.same_as_a, 2) - - def test_rich_different_object_same_as(self): - rich = RichCompare() - rich.on_trait_change(self.bar_changed, "bar") - - self.reset_change_tracker() - - default_value = rich.bar - rich.bar = self.a - self.check_tracker(rich, "bar", default_value, self.a, 1) - - # Values of a and same_as_a are the same and should therefore not - # be considered a change. - rich.bar = self.same_as_a - self.check_tracker(rich, "bar", default_value, self.a, 1) - - -class Foo(HasTraits): - name = Str - - def __ne__(self, other): - # Traits uses != to do the rich compare. The default implementation - # of __ne__ is to compare the object identities. - return self.name != other.name - - def __eq__(self, other): - # Not required, but a good idea to make __eq__ and __ne__ compatible - return self.name == other.name - - -class RichCompareHasTraitsTestCase(unittest.TestCase, RichCompareTests): - def setUp(self): - self.a = Foo(name="a") - self.same_as_a = Foo(name="a") - self.different_from_a = Foo(name="not a") - - def test_assumptions(self): - self.assertIsNot(self.a, self.same_as_a) - self.assertIsNot(self.a, self.different_from_a) - - self.assertEqual(self.a.name, self.same_as_a.name) - self.assertNotEqual(self.a.name, self.different_from_a.name) - - -class OldRichCompareTestCase(unittest.TestCase): - def test_rich_compare_false(self): - with warnings.catch_warnings(record=True) as warn_msgs: - warnings.simplefilter("always", DeprecationWarning) - - class OldRichCompare(HasTraits): - bar = Any(rich_compare=False) - - # Check for a DeprecationWarning. - self.assertEqual(len(warn_msgs), 1) - warn_msg = warn_msgs[0] - self.assertIs(warn_msg.category, DeprecationWarning) - self.assertIn( - "'rich_compare' metadata has been deprecated", - str(warn_msg.message) - ) - _, _, this_module = __name__.rpartition(".") - self.assertIn(this_module, warn_msg.filename) - - # Behaviour matches comparison_mode=ComparisonMode.identity. - old_compare = OldRichCompare() - events = [] - old_compare.on_trait_change(lambda: events.append(None), "bar") - - some_list = [1, 2, 3] - - self.assertEqual(len(events), 0) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = [1, 2, 3] - self.assertEqual(len(events), 2) - old_compare.bar = [4, 5, 6] - self.assertEqual(len(events), 3) - - def test_rich_compare_true(self): - with warnings.catch_warnings(record=True) as warn_msgs: - warnings.simplefilter("always", DeprecationWarning) - - class OldRichCompare(HasTraits): - bar = Any(rich_compare=True) - - # Check for a DeprecationWarning. - self.assertEqual(len(warn_msgs), 1) - warn_msg = warn_msgs[0] - self.assertIs(warn_msg.category, DeprecationWarning) - self.assertIn( - "'rich_compare' metadata has been deprecated", - str(warn_msg.message) - ) - _, _, this_module = __name__.rpartition(".") - self.assertIn(this_module, warn_msg.filename) - - # Behaviour matches comparison_mode=ComparisonMode.identity. - old_compare = OldRichCompare() - events = [] - old_compare.on_trait_change(lambda: events.append(None), "bar") - - some_list = [1, 2, 3] - - self.assertEqual(len(events), 0) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = [1, 2, 3] - self.assertEqual(len(events), 1) - old_compare.bar = [4, 5, 6] - self.assertEqual(len(events), 2) - - def test_rich_compare_with_cached_property(self): - # Even though the property is cached such that old value equals new - # value, its change event is tied to the dependent. - - class Model(HasTraits): - value = Property(depends_on="name") - name = Str(comparison_mode=ComparisonMode.none) - - @cached_property - def _get_value(self): - return self.trait_names - - instance = Model() - events = [] - instance.on_trait_change(lambda: events.append(None), "value") - - instance.name = "A" - - events.clear() - - # when - instance.name = "A" - - # then - self.assertEqual(len(events), 1) diff --git a/traits/tests/test_traits.py b/traits/tests/test_traits.py index 38182280f..7ded0bdb3 100644 --- a/traits/tests/test_traits.py +++ b/traits/tests/test_traits.py @@ -11,7 +11,6 @@ # Imports import unittest -import warnings from traits.api import ( Any, @@ -1077,76 +1076,6 @@ class HasComparisonMode(HasTraits): old_compare.bar = [4, 5, 6] self.assertEqual(len(events), 2) - def test_rich_compare_false(self): - with warnings.catch_warnings(record=True) as warn_msgs: - warnings.simplefilter("always", DeprecationWarning) - - class OldRichCompare(HasTraits): - bar = Trait(rich_compare=False) - - # Check for a DeprecationWarning. - self.assertEqual(len(warn_msgs), 1) - warn_msg = warn_msgs[0] - self.assertIs(warn_msg.category, DeprecationWarning) - self.assertIn( - "'rich_compare' metadata has been deprecated", - str(warn_msg.message) - ) - _, _, this_module = __name__.rpartition(".") - self.assertIn(this_module, warn_msg.filename) - - # Behaviour matches comparison_mode=ComparisonMode.identity. - old_compare = OldRichCompare() - events = [] - old_compare.on_trait_change(lambda: events.append(None), "bar") - - some_list = [1, 2, 3] - - self.assertEqual(len(events), 0) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = [1, 2, 3] - self.assertEqual(len(events), 2) - old_compare.bar = [4, 5, 6] - self.assertEqual(len(events), 3) - - def test_rich_compare_true(self): - with warnings.catch_warnings(record=True) as warn_msgs: - warnings.simplefilter("always", DeprecationWarning) - - class OldRichCompare(HasTraits): - bar = Trait(rich_compare=True) - - # Check for a DeprecationWarning. - self.assertEqual(len(warn_msgs), 1) - warn_msg = warn_msgs[0] - self.assertIs(warn_msg.category, DeprecationWarning) - self.assertIn( - "'rich_compare' metadata has been deprecated", - str(warn_msg.message) - ) - _, _, this_module = __name__.rpartition(".") - self.assertIn(this_module, warn_msg.filename) - - # Behaviour matches comparison_mode=ComparisonMode.identity. - old_compare = OldRichCompare() - events = [] - old_compare.on_trait_change(lambda: events.append(None), "bar") - - some_list = [1, 2, 3] - - self.assertEqual(len(events), 0) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = some_list - self.assertEqual(len(events), 1) - old_compare.bar = [1, 2, 3] - self.assertEqual(len(events), 1) - old_compare.bar = [4, 5, 6] - self.assertEqual(len(events), 2) - @requires_traitsui class TestDeprecatedTraits(unittest.TestCase): diff --git a/traits/trait_type.py b/traits/trait_type.py index ea7280832..7252f4635 100644 --- a/traits/trait_type.py +++ b/traits/trait_type.py @@ -17,10 +17,9 @@ """ import logging -import warnings from .base_trait_handler import BaseTraitHandler -from .constants import ComparisonMode, DefaultValue, TraitKind +from .constants import DefaultValue, TraitKind from .trait_base import Missing, Self, TraitsCache, Undefined, class_of from .trait_dict_object import TraitDictObject from .trait_errors import TraitError @@ -468,23 +467,6 @@ def as_ctrait(self): trait.post_setattr = post_setattr trait.is_mapped = self.is_mapped - # Note: The use of 'rich_compare' metadata is deprecated; use - # 'comparison_mode' metadata instead. Ref: enthought/traits#602. - rich_compare = metadata.get("rich_compare") - if rich_compare is not None: - warnings.warn( - "The 'rich_compare' metadata has been deprecated. Please " - "use the 'comparison_mode' metadata instead. In a future " - "release, rich_compare will have no effect.", - DeprecationWarning, - stacklevel=6, - ) - - if rich_compare: - trait.comparison_mode = ComparisonMode.equality - else: - trait.comparison_mode = ComparisonMode.identity - comparison_mode = metadata.pop("comparison_mode", None) if comparison_mode is not None: trait.comparison_mode = comparison_mode diff --git a/traits/traits.py b/traits/traits.py index cd0692f0a..49c8b89b5 100644 --- a/traits/traits.py +++ b/traits/traits.py @@ -34,10 +34,8 @@ """ from types import FunctionType, MethodType -import warnings from .constants import ( - ComparisonMode, DefaultValue, TraitKind, ) @@ -478,21 +476,6 @@ def as_ctrait(self): trait.post_setattr = post_setattr trait.is_mapped = handler.is_mapped - rich_compare = metadata.get("rich_compare") - if rich_compare is not None: - # Ref: enthought/traits#602 - warnings.warn( - "The 'rich_compare' metadata has been deprecated. Please " - "use the 'comparison_mode' metadata instead. In a future " - "release, rich_compare will have no effect.", - DeprecationWarning, - stacklevel=4, - ) - if rich_compare: - trait.comparison_mode = ComparisonMode.equality - else: - trait.comparison_mode = ComparisonMode.identity - comparison_mode = metadata.pop("comparison_mode", None) if comparison_mode is not None: trait.comparison_mode = comparison_mode