Skip to content

Commit 25b723a

Browse files
committed
Merge branch 'dev' of github.com:seperman/deepdiff into dev
2 parents 96847f2 + 5ee7d6c commit 25b723a

File tree

7 files changed

+101
-10
lines changed

7 files changed

+101
-10
lines changed

deepdiff/deephash.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ def __init__(self,
144144
parent="root",
145145
encodings=None,
146146
ignore_encoding_errors=False,
147+
ignore_iterable_order=True,
147148
**kwargs):
148149
if kwargs:
149150
raise ValueError(
@@ -190,6 +191,7 @@ def __init__(self,
190191
self.ignore_private_variables = ignore_private_variables
191192
self.encodings = encodings
192193
self.ignore_encoding_errors = ignore_encoding_errors
194+
self.ignore_iterable_order = ignore_iterable_order
193195

194196
self._hash(obj, parent=parent, parents_ids=frozenset({get_id(obj)}))
195197

@@ -424,7 +426,9 @@ def _prep_iterable(self, obj, parent, parents_ids=EMPTY_FROZENSET):
424426
'{}|{}'.format(i, v) for i, v in result.items()
425427
]
426428

427-
result = sorted(map(str, result)) # making sure the result items are string and sorted so join command works.
429+
result = map(str, result) # making sure the result items are string so join command works.
430+
if self.ignore_iterable_order:
431+
result = sorted(result)
428432
result = ','.join(result)
429433
result = KEY_TO_VAL_STR.format(type(obj).__name__, result)
430434

deepdiff/diff.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from math import isclose as is_close
1414
from collections.abc import Mapping, Iterable, Sequence
1515
from collections import defaultdict
16+
from inspect import getmembers
1617
from itertools import zip_longest
1718
from ordered_set import OrderedSet
1819
from deepdiff.helper import (strings, bytes_type, numbers, uuids, datetimes, ListItemRemovedOrAdded, notpresent,
@@ -417,20 +418,25 @@ def _diff_enum(self, level, parents_ids=frozenset(), local_tree=None):
417418

418419
def _diff_obj(self, level, parents_ids=frozenset(), is_namedtuple=False, local_tree=None):
419420
"""Difference of 2 objects"""
421+
processing_error = False
420422
try:
421423
if is_namedtuple:
422424
t1 = level.t1._asdict()
423425
t2 = level.t2._asdict()
424-
else:
426+
elif all('__dict__' in dir(t) for t in level):
425427
t1 = detailed__dict__(level.t1, ignore_private_variables=self.ignore_private_variables)
426428
t2 = detailed__dict__(level.t2, ignore_private_variables=self.ignore_private_variables)
427-
except AttributeError:
428-
try:
429+
elif all('__slots__' in dir(t) for t in level):
429430
t1 = self._dict_from_slots(level.t1)
430431
t2 = self._dict_from_slots(level.t2)
431-
except AttributeError:
432-
self._report_result('unprocessed', level, local_tree=local_tree)
433-
return
432+
else:
433+
t1 = {k: v for k, v in getmembers(level.t1) if not callable(v)}
434+
t2 = {k: v for k, v in getmembers(level.t2) if not callable(v)}
435+
except AttributeError:
436+
processing_error = True
437+
if processing_error is True:
438+
self._report_result('unprocessed', level, local_tree=local_tree)
439+
return
434440

435441
self._diff_dict(
436442
level,
@@ -876,7 +882,8 @@ def _diff_by_forming_pairs_and_comparing_one_by_one(
876882
x,
877883
y,
878884
child_relationship_class=child_relationship_class,
879-
child_relationship_param=j)
885+
child_relationship_param=j
886+
)
880887
self._diff(next_level, parents_ids_added, local_tree=local_tree)
881888

882889
def _diff_ordered_iterable_by_difflib(
@@ -1529,7 +1536,7 @@ def _diff(self, level, parents_ids=frozenset(), _original_type=None, local_tree=
15291536
if isinstance(level.t1, booleans):
15301537
self._diff_booleans(level, local_tree=local_tree)
15311538

1532-
if isinstance(level.t1, strings):
1539+
elif isinstance(level.t1, strings):
15331540
self._diff_str(level, local_tree=local_tree)
15341541

15351542
elif isinstance(level.t1, datetimes):

deepdiff/model.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,10 @@ def __setattr__(self, key, value):
577577
else:
578578
self.__dict__[key] = value
579579

580+
def __iter__(self):
581+
yield self.t1
582+
yield self.t2
583+
580584
@property
581585
def repetition(self):
582586
return self.additional['repetition']

docs/deephash_doc.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ ignore_private_variables: Boolean, default = True
123123
ignore_encoding_errors: Boolean, default = False
124124
If you want to get away with UnicodeDecodeError without passing explicit character encodings, set this option to True. If you want to make sure the encoding is done properly, keep this as False and instead pass an explicit list of character encodings to be considered via the encodings parameter.
125125

126+
ignore_iterable_order: Boolean, default = True
127+
If order of items in an iterable should not cause the hash of the iterable to be different.
126128

127129
number_format_notation : string, default="f"
128130
number_format_notation is what defines the meaning of significant digits. The default value of "f" means the digits AFTER the decimal point. "f" stands for fixed point. The other option is "e" which stands for exponent notation or scientific notation.

requirements-cli.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
click==8.1.3
2-
pyyaml==6.0
2+
pyyaml==6.0.1

tests/test_diff_text.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import datetime
33
import pytest
44
import logging
5+
import re
56
import uuid
67
from enum import Enum
78
from typing import List
@@ -605,6 +606,64 @@ class MyEnum(Enum):
605606
}
606607
assert ddiff == result
607608

609+
def test_precompiled_regex(self):
610+
611+
pattern_1 = re.compile('foo')
612+
pattern_2 = re.compile('foo')
613+
pattern_3 = re.compile('foo', flags=re.I)
614+
pattern_4 = re.compile('(foo)')
615+
pattern_5 = re.compile('bar')
616+
617+
# same object
618+
ddiff = DeepDiff(pattern_1, pattern_1)
619+
result = {}
620+
assert ddiff == result
621+
622+
# same pattern, different object
623+
ddiff = DeepDiff(pattern_1, pattern_2)
624+
result = {}
625+
assert ddiff == result
626+
627+
# same pattern, different flags
628+
ddiff = DeepDiff(pattern_1, pattern_3)
629+
result = {
630+
'values_changed': {
631+
'root.flags': {
632+
'new_value': 34,
633+
'old_value': 32,
634+
},
635+
}
636+
}
637+
assert ddiff == result
638+
639+
# same pattern, different groups
640+
ddiff = DeepDiff(pattern_1, pattern_4)
641+
result = {
642+
'values_changed': {
643+
'root.pattern': {
644+
'new_value': '(foo)',
645+
'old_value': 'foo',
646+
},
647+
'root.groups': {
648+
'new_value': 1,
649+
'old_value': 0,
650+
},
651+
}
652+
}
653+
assert ddiff == result
654+
655+
# different pattern
656+
ddiff = DeepDiff(pattern_1, pattern_5)
657+
result = {
658+
'values_changed': {
659+
'root.pattern': {
660+
'new_value': 'bar',
661+
'old_value': 'foo',
662+
},
663+
}
664+
}
665+
assert ddiff == result
666+
608667
def test_custom_objects_change(self):
609668
t1 = CustomClass(1)
610669
t2 = CustomClass(2)

tests/test_hash.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,21 @@ def test_same_sets_same_hash(self):
368368
t2_hash = DeepHashPrep(t2)
369369

370370
assert t1_hash[get_id(t1)] == t2_hash[get_id(t2)]
371+
372+
@pytest.mark.parametrize("list1, list2, ignore_iterable_order, is_equal", [
373+
([1, 2], [2, 1], False, False),
374+
([1, 2], [2, 1], True, True),
375+
([1, 2, 3], [1, 3, 2], False, False),
376+
([1, [1, 2, 3]], [1, [3, 2, 1]], False, False),
377+
([1, [1, 2, 3]], [1, [3, 2, 1]], True, True),
378+
((1, 2), (2, 1), False, False),
379+
((1, 2), (2, 1), True, True),
380+
])
381+
def test_ignore_iterable_order(self, list1, list2, ignore_iterable_order, is_equal):
382+
list1_hash = DeepHash(list1, ignore_iterable_order=ignore_iterable_order)
383+
list2_hash = DeepHash(list2, ignore_iterable_order=ignore_iterable_order)
384+
385+
assert is_equal == (list1_hash[list1] == list2_hash[list2])
371386

372387
@pytest.mark.parametrize("t1, t2, significant_digits, number_format_notation, result", [
373388
({0.012, 0.98}, {0.013, 0.99}, 1, "f", 'set:float:0.0,float:1.0'),

0 commit comments

Comments
 (0)