Skip to content
Snippets Groups Projects
Commit d190ed34 authored by Piotr Maślanka's avatar Piotr Maślanka
Browse files

added deep compare

parent 6b933997
No related branches found
No related tags found
No related merge requests found
# v2.17.12
* added deep comparison
......@@ -110,3 +110,16 @@ Without running into `TypeError: metaclass conflict: the metaclass of a derived
Following function will help with that:
.. autofunction:: satella.coding.metaclass_maker
Deep comparison
---------------
To analyze why two objects don't compare the same, you can use the following functions:
.. autofunction:: satella.coding.assert_equal
.. autoclass:: satella.coding.Inequal
:members:
.. autoclass:: satella.coding.InequalityReason
:members:
__version__ = '2.17.12a1'
__version__ = '2.17.12a2'
......@@ -20,8 +20,10 @@ from .overloading import overload, class_or_instancemethod
from .recast_exceptions import rethrow_as, silence_excs, catch_exception, log_exceptions, \
raises_exception, reraise_as
from .expect_exception import expect_exception
from .deep_compare import assert_equal, InequalityReason, Inequal
__all__ = [
'assert_equal', 'InequalityReason', 'Inequal',
'Closeable', 'contains', 'enum_value', 'reraise_as',
'expect_exception',
'overload', 'class_or_instancemethod',
......
import enum
class InequalityReason(enum.IntEnum):
NOT_EQUAL = 0 #: direct eq yielded not equal
LENGTH_MISMATCH = 1 #: length didn't match
KEY_NOT_FOUND = 2 #: key given as obj1 was not found
class Inequal(Exception):
"""
An exception raised by :meth:`~satella.coding.deep_compare` if two objects don't match
:ivar obj1: first object that was not equal, or key name
:ivar obj2: second object that was not equal, or None
:ivar reason: (:class:`~satella.coding.InequalityReason`) reason for inequality
"""
def __init__(self, obj1, obj2, reason: InequalityReason):
self.obj1 = obj1
self.obj2 = obj2
self.reason = reason
def assert_equal(a, b):
"""
Assert that two values are equal. If not, an :class:`satella.coding.Inequality` exception
will be thrown.
Objects are tried to compare using it's :code:`__eq__`.
:param a: first value to compare
:param b: second value to compare
:raises Inequal: objects were not equal
"""
if isinstance(a, (int, float, str, bytes, bytearray, type(None))):
if a != b:
raise Inequal(a, b, InequalityReason.NOT_EQUAL)
return
elif isinstance(a, (set, frozenset)):
if len(a) != len(b):
raise Inequal(a, b, InequalityReason.LENGTH_MISMATCH)
if a != b:
raise Inequal(a, b, InequalityReason.LENGTH_MISMATCH)
elif isinstance(a, (list, tuple)):
if len(a) != len(b):
raise Inequal(a, b, InequalityReason.LENGTH_MISMATCH)
for c, d in zip(a, b):
assert_equal(c, d)
elif isinstance(a, dict):
if len(a) != len(b):
raise Inequal(a, b, InequalityReason.LENGTH_MISMATCH)
for key in a.keys():
if key not in b:
raise Inequal(key, None, InequalityReason.KEY_NOT_FOUND)
assert_equal(a[key], b[key])
else:
try:
if a == b:
return
raise Inequal(a, b, InequalityReason.NOT_EQUAL)
except TypeError:
pass
import unittest
from satella.coding import assert_equal, Inequal
class TestDeepCompare(unittest.TestCase):
def assertInequal(self, a, b):
self.assertRaises(Inequal, lambda: assert_equal(a, b))
def test_compare(self):
assert_equal(2, 2)
self.assertInequal(3, 2)
self.assertInequal([], [6])
self.assertInequal({1: 2}, {3: 4})
self.assertInequal({1: 2}, {1: 4})
self.assertInequal({1: 2}, {3: 4, 5: 6})
assert_equal([1], [1])
assert_equal(set([1, 2]), set([2, 1]))
class Object:
def __init__(self, a):
self.a = a
def __eq__(self, other):
return self.a == other.a
assert_equal(Object(3), Object(3))
self.assertInequal(Object(3), Object(4))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment