seperman / deepdiff

DeepDiff: Deep Difference and search of any Python object/data. DeepHash: Hash of any object based on its contents. Delta: Use deltas to reconstruct objects by adding deltas together.

Home Page:http://zepworks.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.