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