Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-testfixtures for 
openSUSE:Factory checked in at 2022-11-15 13:18:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-testfixtures (Old)
 and      /work/SRC/openSUSE:Factory/.python-testfixtures.new.1597 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-testfixtures"

Tue Nov 15 13:18:12 2022 rev:23 rq:1035575 version:7.0.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-testfixtures/python-testfixtures.changes  
2022-10-10 18:44:23.970847600 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-testfixtures.new.1597/python-testfixtures.changes
        2022-11-15 13:20:54.740641820 +0100
@@ -1,0 +2,14 @@
+Wed Nov  9 19:29:23 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com>
+
+- Update to 7.0.3 (3 Nov 2022)
+  * Further bugfixes around self-referential datastructures and 
:func:`compare`.
+
+- Update to 7.0.2 (1 Nov 2022)
+  * Reinstate support for self-referential data structures in :func:`compare`. 
The new implementation provides more clarity about
+    what's going on and also ignores more immutable data types.
+
+- Update to 7.0.1 (1 Nov 2022)
+  * Remove non-functional support for self-referential data structures in 
:func:`compare`. The functionality didn't work but did cause
+    erroneous reported equality of values in dictionaries that were actually 
not equal. 
+
+-------------------------------------------------------------------

Old:
----
  testfixtures-7.0.0.tar.gz

New:
----
  testfixtures-7.0.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-testfixtures.spec ++++++
--- /var/tmp/diff_new_pack.V6R3YQ/_old  2022-11-15 13:20:55.144643906 +0100
+++ /var/tmp/diff_new_pack.V6R3YQ/_new  2022-11-15 13:20:55.152643948 +0100
@@ -16,10 +16,9 @@
 #
 
 
-%{?!python_module:%define python_module() python3-%{**}}
 %define skip_python2 1
 Name:           python-testfixtures
-Version:        7.0.0
+Version:        7.0.3
 Release:        0
 Summary:        A collection of helpers and mock objects for unit tests and 
doc tests
 License:        MIT

++++++ testfixtures-7.0.0.tar.gz -> testfixtures-7.0.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testfixtures-7.0.0/CHANGELOG.rst 
new/testfixtures-7.0.3/CHANGELOG.rst
--- old/testfixtures-7.0.0/CHANGELOG.rst        2022-07-06 09:40:25.000000000 
+0200
+++ new/testfixtures-7.0.3/CHANGELOG.rst        2022-11-03 11:37:48.000000000 
+0100
@@ -1,6 +1,25 @@
 Changes
 =======
 
+7.0.3 (3 Nov 2022)
+------------------
+
+- Further bugfixes around self-referential datastructures and :func:`compare`.
+
+7.0.2 (1 Nov 2022)
+------------------
+
+- Reinstate support for self-referential data structures in :func:`compare`.
+  The new implementation provides more clarity about what's going on and also 
ignores more
+  immutable data types.
+
+7.0.1 (1 Nov 2022)
+------------------
+
+- Remove non-functional support for self-referential data structures in 
:func:`compare`.
+  The functionality didn't work but did cause erroneous reported equality of 
values in dictionaries
+  that were actually not equal.
+
 7.0.0 (6 Jul 2022)
 ------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testfixtures-7.0.0/PKG-INFO 
new/testfixtures-7.0.3/PKG-INFO
--- old/testfixtures-7.0.0/PKG-INFO     2022-07-06 09:40:31.347435000 +0200
+++ new/testfixtures-7.0.3/PKG-INFO     2022-11-03 11:37:55.922026200 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: testfixtures
-Version: 7.0.0
+Version: 7.0.3
 Summary: A collection of helpers and mock objects for unit tests and doc tests.
 Home-page: https://github.com/Simplistix/testfixtures
 Author: Chris Withers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testfixtures-7.0.0/testfixtures/comparison.py 
new/testfixtures-7.0.3/testfixtures/comparison.py
--- old/testfixtures-7.0.0/testfixtures/comparison.py   2022-07-06 
09:40:25.000000000 +0200
+++ new/testfixtures-7.0.3/testfixtures/comparison.py   2022-11-03 
11:37:48.000000000 +0100
@@ -19,6 +19,10 @@
 from unittest.mock import call as unittest_mock_call
 
 
+# Some common types that are immutable, for optimisation purposes within 
CompareContext
+IMMUTABLE_TYPEs = str, bytes, int, float, tuple, type(None)
+
+
 def diff(x: str, y: str, x_label: str = '', y_label: str = ''):
     """
     A shorthand function that uses :mod:`difflib` to return a
@@ -148,6 +152,8 @@
     Return a textual description of the difference between two objects
     including information about their types.
     """
+    if type(x) is AlreadySeen and type(x.obj) is type(y) and x.obj == y:
+        return ''
     source = locals()
     to_render = {}
     for name in 'x', 'y':
@@ -486,6 +492,23 @@
 _unsafe_iterables = str, bytes, dict
 
 
+class AlreadySeen:
+
+    def __init__(self, id_, obj, breadcrumb):
+        self.id = id_
+        self.obj = obj
+        self.breadcrumb = breadcrumb
+
+    def __repr__(self):
+        return f'<AlreadySeen for {self.obj!r} at {self.breadcrumb} with id 
{self.id}>'
+
+    def __eq__(self, other):
+        if isinstance(other, AlreadySeen):
+            return self.breadcrumb == other.breadcrumb
+        else:
+            return self.obj == other
+
+
 class CompareContext(object):
 
     def __init__(
@@ -511,7 +534,7 @@
         self.options: Dict[str, Any] = options or {}
         self.message: str = ''
         self.breadcrumbs: List[str] = []
-        self._seen = set()
+        self._seen = {}
 
     def extract_args(self, args: tuple, x: Any, y: Any, expected: Any, actual: 
Any) -> List:
 
@@ -572,22 +595,23 @@
     def _separator(self) -> str:
         return '\n\nWhile comparing %s: ' % ''.join(self.breadcrumbs[1:])
 
-    def seen(self, x: Any, y: Any) -> bool:
-        # don't get confused by interning:
-        singleton_types = str, bytes, int, float
-        if isinstance(x, singleton_types) and isinstance(y, singleton_types):
-            return False
-        key = id(x), id(y)
-        if key in self._seen:
-            return True
-        self._seen.add(key)
+    def _break_loops(self, obj, breadcrumb):
+        # Don't bother with this process for simple, immutable types:
+        if isinstance(obj, IMMUTABLE_TYPEs):
+            return obj
+
+        id_ = id(obj)
+        breadcrumb_ = self._seen.get(id_)
+        if breadcrumb_ is not None:
+            return AlreadySeen(id_, obj, breadcrumb_)
+        else:
+            self._seen[id_] = breadcrumb
+            return obj
 
     def different(self, x: Any, y: Any, breadcrumb: str) -> Union[bool, 
Optional[str]]:
 
-        if self.seen(x, y):
-            # a self-referential hierarchy; so lets say this one is
-            # equal and hope the first time we saw it covers things...
-            return False
+        x = self._break_loops(x, breadcrumb)
+        y = self._break_loops(y, breadcrumb)
 
         recursed = bool(self.breadcrumbs)
         self.breadcrumbs.append(breadcrumb)
@@ -596,18 +620,18 @@
         current_message = ''
         try:
 
-            if not (self.strict or self.ignore_eq) and x == y:
-                return False
+            if type(y) is AlreadySeen or not (self.strict or self.ignore_eq):
+                try:
+                    if x == y:
+                        return False
+                except RecursionError:
+                    pass
 
             comparer: Comparer = self._lookup(x, y)
 
             result = comparer(x, y, self)
             specific_comparer = comparer is not compare_simple
 
-            if self.strict:
-                if x == y and not specific_comparer:
-                    return False
-
             if result:
 
                 if specific_comparer and recursed:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/testfixtures-7.0.0/testfixtures/tests/test_compare.py 
new/testfixtures-7.0.3/testfixtures/tests/test_compare.py
--- old/testfixtures-7.0.0/testfixtures/tests/test_compare.py   2022-07-06 
09:40:25.000000000 +0200
+++ new/testfixtures-7.0.3/testfixtures/tests/test_compare.py   2022-11-03 
11:37:48.000000000 +0100
@@ -395,6 +395,27 @@
             "'y': 1.0 != 2.0"
             )
 
+    def test_dict_identical_none_matching_nones_and_ones(self):
+        self.check_raises(
+            {
+                'foo': None,
+                'baz': None,
+            },
+            {
+                'foo': 1,
+                'baz': 1,
+            },
+            "dict not as expected:\n"
+            "\n"
+            'values differ:\n'
+            "'baz': None != 1\n"
+            "'foo': None != 1\n"
+            '\n'
+            "While comparing ['baz']: None != 1\n"
+            "\n"
+            "While comparing ['foo']: None != 1"
+            )
+
     def test_dict_labels_specified(self):
         self.check_raises(
             dict(x=1, y=2), dict(x=2, z=3),
@@ -949,7 +970,7 @@
             "second:\n[2]"
             )
 
-    def test_strict_okay(self):
+    def test_same_object_strict_okay(self):
         m = object()
         compare(m, m, strict=True)
 
@@ -1538,6 +1559,20 @@
         m.foo(1, 2, x=3)
         compare(m.mock_calls, m.mock_calls, strict=True)
 
+    def test_mock_call_equal(self):
+        m1 = Mock()
+        m1.foo(1, 2, x=3)
+        m2 = Mock()
+        m2.foo(1, 2, x=3)
+        compare(m1.mock_calls, m2.mock_calls)
+
+    def test_mock_call_equal_strict(self):
+        m1 = Mock()
+        m1.foo(1, 2, x=3)
+        m2 = Mock()
+        m2.foo(1, 2, x=3)
+        compare(m1.mock_calls, m2.mock_calls, strict=True)
+
     def test_calls_different(self):
         m1 = Mock()
         m2 = Mock()
@@ -1897,6 +1932,146 @@
             'Both x and y appear as "re.compile(\''+'a'*199+')", but are not 
equal!'
             )
 
+    def test_self_referential_same(self):
+        expected = {1: 'foo'}
+        expected[2] = expected
+        actual = {1: 'foo'}
+        actual[2] = actual
+        compare(expected, actual)
+
+    def test_self_referential_different(self):
+        expected = {1: 'foo'}
+        expected[2] = expected
+        actual = {1: 'bar'}
+        actual[2] = actual
+        self.check_raises(
+            expected,
+            actual,
+            'dict not as expected:\n'
+            '\n'
+            'same:\n'
+            '[2]\n'
+            '\n'
+            'values differ:\n'
+            "1: 'foo' != 'bar'\n"
+            '\n'
+            "While comparing [1]: 'foo' != 'bar'"
+        )
+
+    def test_self_referential_different_but_shows_already_seen(self):
+        ouroboros1 = {}
+        ouroboros1['ouroboros'] = ouroboros1
+        ouroboros2 = {}
+        ouroboros2['ouroboros'] = ouroboros2
+        id2 = str(id(ouroboros2))
+        self.check_raises(
+            {1: ouroboros1, 2: 'foo'},
+            {1: ouroboros2, 2: ouroboros2},
+            'dict not as expected:\n'
+            '\n'
+            'same:\n'
+            '[1]\n'
+            '\n'
+            'values differ:\n'
+            "2: 'foo' != {'ouroboros': <Recursion on dict with id="+id2+">}\n"
+            '\n'
+            "While comparing [2]: 'foo' != "
+            "<AlreadySeen for {'ouroboros': {...}} at [1] with id "+id2+">"
+        )
+
+    def test_self_referential_object_tree(self):
+
+        class Node:
+
+            def __init__(self):
+                self.parent = None
+                self.children = []
+
+            def add(self, child: 'Node'):
+                self.children.append(child)
+                child.parent = self
+
+            def __repr__(self):
+                return f'<Node: {self.children}>'
+
+        expected = Node()
+        expected.add(Node())
+        expected.add(Node())
+
+        actual = Node()
+        actual.add(Node())
+
+        self.check_raises(
+            expected,
+            actual,
+            'Node not as expected:\n'
+            '\n'
+            'attributes same:\n'
+            "['parent']\n"
+            '\n'
+            'attributes differ:\n'
+            "'children': [<Node: []>, <Node: []>] != [<Node: []>]\n"
+            '\n'
+            'While comparing .children: sequence not as expected:\n'
+            '\n'
+            'same:\n'
+            '[<Node: []>]\n'
+            '\n'
+            'first:\n'
+            '[<Node: []>]\n'
+            '\n'
+            'second:\n'
+            '[]'
+        )
+
+    def test_repeated_object_on_the_left_side_ignore_eq(self):
+        item = [1, 2, 3]
+        compare(expected=[item, item], actual=[[1, 2, 3], [1, 2, 3]], 
ignore_eq=True)
+
+    def test_repeated_object_on_both_sides_ignore_eq(self):
+        item = [1, 2, 3]
+        compare(expected=[item, item], actual=[item, [1, 2, 3]], 
ignore_eq=True)
+
+    def 
test_repeated_object_on_both_sides_left_at_compare_strict_type_same(self):
+        item = [1, 2, 3]
+        compare(expected=[item, item], actual=[item, [1, 2, 3]], strict=True)
+
+    def 
test_repeated_object_on_both_sides_right_at_compare_strict_type_same(self):
+        item = [1, 2, 3]
+        compare(expected=[item, [1, 2, 3]], actual=[item, item], strict=True)
+
+    def test_repeated_object_on_both_sides_strict_type_different(self):
+        item = [1, 2, 3]
+
+        class MyList(list):
+
+            def __repr__(self):
+                return f'<{type(self).__name__}:{super().__repr__()}>'
+
+        type_repr = repr(MyList)
+
+        self.check_raises(
+            [item, item],
+            [item, MyList((1, 2, 3))],
+            strict=True,
+            message = (
+                'sequence not as expected:\n'
+                '\n'
+                'same:\n'
+                '[[1, 2, 3]]\n'
+                '\n'
+                'first:\n'
+                '[[1, 2, 3]]\n'
+                '\n'
+                'second:\n'
+                '[<MyList:[1, 2, 3]>]\n'
+                '\n'
+                f"While comparing [1]: <AlreadySeen for [1, 2, 3] at ... "
+                f"(<class 'testfixtures.comparison.AlreadySeen'>) != "
+                f"<MyList:[1, 2, 3]> ({type_repr})"
+            )
+        )
+
 
 class TestIgnore(CompareHelper):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testfixtures-7.0.0/testfixtures/version.txt 
new/testfixtures-7.0.3/testfixtures/version.txt
--- old/testfixtures-7.0.0/testfixtures/version.txt     2022-07-06 
09:40:25.000000000 +0200
+++ new/testfixtures-7.0.3/testfixtures/version.txt     2022-11-03 
11:37:48.000000000 +0100
@@ -1 +1 @@
-7.0.0
+7.0.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testfixtures-7.0.0/testfixtures.egg-info/PKG-INFO 
new/testfixtures-7.0.3/testfixtures.egg-info/PKG-INFO
--- old/testfixtures-7.0.0/testfixtures.egg-info/PKG-INFO       2022-07-06 
09:40:31.000000000 +0200
+++ new/testfixtures-7.0.3/testfixtures.egg-info/PKG-INFO       2022-11-03 
11:37:55.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: testfixtures
-Version: 7.0.0
+Version: 7.0.3
 Summary: A collection of helpers and mock objects for unit tests and doc tests.
 Home-page: https://github.com/Simplistix/testfixtures
 Author: Chris Withers

Reply via email to