Diff does not work for attributes of a class that inherits from dict or collections.UserDict
ecourreges-orange opened this issue · comments
DeepDiff on a class that inherits from dict of UserDict produces no diff when dict is the same and attributes are different.
This is not a problem for the same class that doesn't inherit from dict, which doesn't even need to define any comparison functions to work
To Reproduce
from collections import UserDict
from deepdiff import DeepDiff
from functools import total_ordering
class Quota:
def __init__(self, cpu, mem) -> None:
self.cpu: int = cpu
self.mem: int = mem
@total_ordering
class QuotaTree(UserDict):
def __eq__(self, other):
if isinstance(other, QuotaTree):
print('Comparing QuotaTree eq')
return self.cpu == other.cpu and self.mem == other.mem and self.data == other.data
return False
def __lt__(self, other):
if isinstance(other, QuotaTree):
print('Comparing QuotaTree eq')
return self.cpu < other.cpu and self.mem < other.mem and self.data < other.data
return False
def __init__(self, cpu, mem) -> None:
UserDict.__init__(self)
self.cpu: int = cpu
self.mem: int = mem
def qt_compare_func(x: QuotaTree, y: QuotaTree, level=None):
try:
print('Comparing QuotaTree')
return False #self.cpu == other.cpu and self.mem == other.mem and self.data == other.data
except Exception:
raise CannotCompare() from None
qt1 = QuotaTree(123,666)
qt2 = QuotaTree(456,777)
print(qt1 == qt2)
print(DeepDiff(qt1, qt2, ignore_order=True, verbose_level=2, iterable_compare_func=qt_compare_func))
quota1 = Quota(123,666)
quota2 = Quota(456,777)
print(quota1 == quota2)
print(DeepDiff(quota1, quota2, ignore_order=True, verbose_level=2))
This shows that no comparison function is ever executed by DeepDiff on the QuotaTree, so what am I doing wrong?
python deepdiffbug.py
Comparing QuotaTree eq
False
{}
False
{'values_changed': {'root.cpu': {'new_value': 456, 'old_value': 123}, 'root.mem': {'new_value': 777, 'old_value': 666}}}
OS, DeepDiff version and Python version:
- OS: Ubuntu 22.04
- Version 22.04 LTS
- Python Version 3.10.6
- DeepDiff Version 6.6.0
Thanks.
Hi @ecourreges-orange
UserDict
is not officially supported. Is that not remnants of Python 2 that are not used in Py3 anymore? It seems like DeepDiff is doing what it is supposed to do though.
iterable_compare_func
is run when comparing iterables such as lists, tuples, etc. It is not used when comparing non-iterable objects. UserDict is not an iterable.
If you look at the examples, we use the iterable_compare_func for comparing lists. For example lists of dictionaries.
https://zepworks.com/deepdiff/current/custom.html#iterable-compare-func-label
Hey there, not so fast at closing, I said it also happens with inheriting the normal dict:
from deepdiff import DeepDiff
from functools import total_ordering
class Quota:
def __init__(self, cpu, mem) -> None:
self.cpu: int = cpu
self.mem: int = mem
@total_ordering
class QuotaTree(dict):
def __eq__(self, other):
if isinstance(other, QuotaTree):
print('Comparing QuotaTree eq')
return self.cpu == other.cpu and self.mem == other.mem and self.data == other.data
return False
def __lt__(self, other):
if isinstance(other, QuotaTree):
print('Comparing QuotaTree eq')
return self.cpu < other.cpu and self.mem < other.mem and self.data < other.data
return False
def __init__(self, cpu, mem) -> None:
self.cpu: int = cpu
self.mem: int = mem
print("Fails if inherits from dict")
qt1 = QuotaTree(123,666)
qt2 = QuotaTree(456,777)
qt1['quota2'] = qt2
print(qt1 == qt2)
print(DeepDiff(qt1, qt2, ignore_order=True, verbose_level=2))
print("Succeeds if basic class")
quota1 = Quota(123,666)
quota2 = Quota(456,777)
print(quota1 == quota2)
print(DeepDiff(quota1, quota2, ignore_order=True, verbose_level=2))
What am I doing wrong here, it only sees the diff in dict, but not in attributes of my class!
Fails if inherits from dict
Comparing QuotaTree eq
False
{'dictionary_item_removed': {"root['quota2']": {}}}
Succeeds if basic class
False
{'values_changed': {'root.cpu': {'new_value': 456, 'old_value': 123}, 'root.mem': {'new_value': 777, 'old_value': 666}}}
I would expect dictionary_item_removed AND values_changed in the first case
Did you have time to check this? Thanks.