Skip to content

Commit 0126007

Browse files
authored
Merge pull request #412 from seperman/dev
6.4.0
2 parents a95defa + cfa0fba commit 0126007

20 files changed

+324
-65
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# DeepDiff Change log
22

3+
- v6-4-0
4+
- [Add Ignore List Order Option to DeepHash](https://github.com/seperman/deepdiff/pull/403) by
5+
[Bobby Morck](https://github.com/bmorck)
6+
- [pyyaml to 6.0.1 to fix cython build problems](https://github.com/seperman/deepdiff/pull/406) by [Robert Bo Davis](https://github.com/robert-bo-davis)
7+
- [Precompiled regex simple diff](https://github.com/seperman/deepdiff/pull/413) by [cohml](https://github.com/cohml)
8+
- New flag: `zip_ordered_iterables` for forcing iterable items to be compared one by one.
39
- v6-3-1
410
- Bugfix deephash for paths by [maggelus](https://github.com/maggelus)
511
- Bugfix deephash compiled regex [maggelus](https://github.com/maggelus)

README.md

+10-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# DeepDiff v 6.3.1
1+
# DeepDiff v 6.4.0
22

33
![Downloads](https://img.shields.io/pypi/dm/deepdiff.svg?style=flat)
44
![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat)
@@ -17,31 +17,20 @@
1717

1818
Tested on Python 3.7+ and PyPy3.
1919

20-
- **[Documentation](https://zepworks.com/deepdiff/6.3.1/)**
20+
- **[Documentation](https://zepworks.com/deepdiff/6.4.0/)**
2121

2222
## What is new?
2323

2424
Please check the [ChangeLog](CHANGELOG.md) file for the detailed information.
2525

26-
DeepDiff 6-3-1
26+
DeepDiff 6-4-0
2727

28-
This release includes many bug fixes.
28+
- [Add Ignore List Order Option to DeepHash](https://github.com/seperman/deepdiff/pull/403) by
29+
[Bobby Morck](https://github.com/bmorck)
30+
- [pyyaml to 6.0.1 to fix cython build problems](https://github.com/seperman/deepdiff/pull/406) by [Robert Bo Davis](https://github.com/robert-bo-davis)
31+
- [Precompiled regex simple diff](https://github.com/seperman/deepdiff/pull/413) by [cohml](https://github.com/cohml)
32+
- New flag: `zip_ordered_iterables` for forcing iterable items to be compared one by one.
2933

30-
- Bugfix deephash for paths by [maggelus](https://github.com/maggelus)
31-
- Bugfix deephash compiled regex [maggelus](https://github.com/maggelus)
32-
- Fix tests dependent on toml by [martin-kokos](https://github.com/martin-kokos)
33-
- Bugfix for `include_paths` for nested dictionaries by [kor4ik](https://github.com/kor4ik)
34-
- Use tomli and tomli-w for dealing with tomli files by [martin-kokos](https://github.com/martin-kokos)
35-
- Bugfix for `datetime.date` by [Alex Sauer-Budge](https://github.com/amsb)
36-
37-
38-
DeepDiff 6-3-0
39-
40-
- [`PrefixOrSuffixOperator`](https://zepworks.com/deepdiff/current/custom.html#prefix-or-suffix-operator-label): This operator will skip strings that are suffix or prefix of each other.
41-
- [`include_obj_callback`](https://zepworks.com/deepdiff/current/ignore_types_or_values.html#include-obj-callback-label) and `include_obj_callback_strict` are added by [Håvard Thom](https://github.com/havardthom).
42-
- Fixed a corner case where numpy's `np.float32` nans are not ignored when using `ignore_nan_equality` by [Noam Gottlieb](https://github.com/noamgot)
43-
- `orjson` becomes optional again.
44-
- Fix for `ignore_type_in_groups` with numeric values so it does not report number changes when the number types are different.
4534

4635
## Installation
4736

@@ -93,11 +82,11 @@ Thank you!
9382

9483
How to cite this library (APA style):
9584

96-
Dehpour, S. (2023). DeepDiff (Version 6.3.1) [Software]. Available from https://github.com/seperman/deepdiff.
85+
Dehpour, S. (2023). DeepDiff (Version 6.4.0) [Software]. Available from https://github.com/seperman/deepdiff.
9786

9887
How to cite this library (Chicago style):
9988

100-
Dehpour, Sep. 2023. DeepDiff (version 6.3.1).
89+
Dehpour, Sep. 2023. DeepDiff (version 6.4.0).
10190

10291
# Authors
10392

deepdiff/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""This module offers the DeepDiff, DeepSearch, grep, Delta and DeepHash classes."""
22
# flake8: noqa
3-
__version__ = '6.3.1'
3+
__version__ = '6.4.0'
44
import logging
55

66
if __name__ == '__main__':

deepdiff/deephash.py

+5-1
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

+20-11
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,
@@ -142,6 +143,7 @@ def __init__(self,
142143
ignore_type_in_groups=None,
143144
ignore_type_subclasses=False,
144145
iterable_compare_func=None,
146+
zip_ordered_iterables=False,
145147
log_frequency_in_sec=0,
146148
math_epsilon=None,
147149
max_diffs=None,
@@ -166,7 +168,7 @@ def __init__(self,
166168
"number_format_notation, exclude_paths, include_paths, exclude_types, exclude_regex_paths, ignore_type_in_groups, "
167169
"ignore_string_type_changes, ignore_numeric_type_changes, ignore_type_subclasses, truncate_datetime, "
168170
"ignore_private_variables, ignore_nan_inequality, number_to_string_func, verbose_level, "
169-
"view, hasher, hashes, max_passes, max_diffs, "
171+
"view, hasher, hashes, max_passes, max_diffs, zip_ordered_iterables, "
170172
"cutoff_distance_for_pairs, cutoff_intersection_for_pairs, log_frequency_in_sec, cache_size, "
171173
"cache_tuning_sample_size, get_deep_distance, group_by, cache_purge_level, "
172174
"math_epsilon, iterable_compare_func, _original_type, "
@@ -208,6 +210,7 @@ def __init__(self,
208210
self.include_obj_callback_strict = include_obj_callback_strict
209211
self.number_to_string = number_to_string_func or number_to_string
210212
self.iterable_compare_func = iterable_compare_func
213+
self.zip_ordered_iterables = zip_ordered_iterables
211214
self.ignore_private_variables = ignore_private_variables
212215
self.ignore_nan_inequality = ignore_nan_inequality
213216
self.hasher = hasher
@@ -415,20 +418,25 @@ def _diff_enum(self, level, parents_ids=frozenset(), local_tree=None):
415418

416419
def _diff_obj(self, level, parents_ids=frozenset(), is_namedtuple=False, local_tree=None):
417420
"""Difference of 2 objects"""
421+
processing_error = False
418422
try:
419423
if is_namedtuple:
420424
t1 = level.t1._asdict()
421425
t2 = level.t2._asdict()
422-
else:
426+
elif all('__dict__' in dir(t) for t in level):
423427
t1 = detailed__dict__(level.t1, ignore_private_variables=self.ignore_private_variables)
424428
t2 = detailed__dict__(level.t2, ignore_private_variables=self.ignore_private_variables)
425-
except AttributeError:
426-
try:
429+
elif all('__slots__' in dir(t) for t in level):
427430
t1 = self._dict_from_slots(level.t1)
428431
t2 = self._dict_from_slots(level.t2)
429-
except AttributeError:
430-
self._report_result('unprocessed', level, local_tree=local_tree)
431-
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
432440

433441
self._diff_dict(
434442
level,
@@ -655,7 +663,6 @@ def _compare_in_order(
655663
Default compare if `iterable_compare_func` is not provided.
656664
This will compare in sequence order.
657665
"""
658-
659666
if t1_from_index is None:
660667
return [((i, i), (x, y)) for i, (x, y) in enumerate(
661668
zip_longest(
@@ -743,7 +750,8 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
743750
child_relationship_class = NonSubscriptableIterableRelationship
744751

745752
if (
746-
isinstance(level.t1, Sequence)
753+
not self.zip_ordered_iterables
754+
and isinstance(level.t1, Sequence)
747755
and isinstance(level.t2, Sequence)
748756
and self._all_values_basic_hashable(level.t1)
749757
and self._all_values_basic_hashable(level.t2)
@@ -874,7 +882,8 @@ def _diff_by_forming_pairs_and_comparing_one_by_one(
874882
x,
875883
y,
876884
child_relationship_class=child_relationship_class,
877-
child_relationship_param=j)
885+
child_relationship_param=j
886+
)
878887
self._diff(next_level, parents_ids_added, local_tree=local_tree)
879888

880889
def _diff_ordered_iterable_by_difflib(
@@ -1527,7 +1536,7 @@ def _diff(self, level, parents_ids=frozenset(), _original_type=None, local_tree=
15271536
if isinstance(level.t1, booleans):
15281537
self._diff_booleans(level, local_tree=local_tree)
15291538

1530-
if isinstance(level.t1, strings):
1539+
elif isinstance(level.t1, strings):
15311540
self._diff_str(level, local_tree=local_tree)
15321541

15331542
elif isinstance(level.t1, datetimes):

deepdiff/helper.py

+40-2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,40 @@ class pydantic_base_model_type:
108108

109109
NUMERICS = frozenset(string.digits)
110110

111+
112+
def _int_or_zero(value):
113+
"""
114+
Tries to extract some number from a string.
115+
116+
12c becomes 12
117+
"""
118+
try:
119+
return int(value)
120+
except Exception:
121+
result = []
122+
for char in value:
123+
if char in NUMERICS:
124+
result.append(char)
125+
if result:
126+
return int(''.join(result))
127+
return 0
128+
129+
130+
def get_semvar_as_integer(version):
131+
"""
132+
Converts:
133+
134+
'1.23.5' to 1023005
135+
"""
136+
version = version.split('.')
137+
if len(version) > 3:
138+
version = version[:3]
139+
elif len(version) < 3:
140+
version.extend(['0'] * (3 - len(version)))
141+
142+
return sum([10**(i * 3) * _int_or_zero(v) for i, v in enumerate(reversed(version))])
143+
144+
111145
# we used to use OrderedDictPlus when dictionaries in Python were not ordered.
112146
dict_ = dict
113147

@@ -120,6 +154,10 @@ class pydantic_base_model_type:
120154

121155
pypy3 = py3 and hasattr(sys, "pypy_translation_info")
122156

157+
158+
if get_semvar_as_integer(np.__version__) < 1019000:
159+
sys.exit('The minimum required Numpy version is 1.19.0. Please upgrade your Numpy package.')
160+
123161
strings = (str, bytes) # which are both basestring
124162
unicode_type = str
125163
bytes_type = bytes
@@ -321,8 +359,8 @@ def type_in_type_group(item, type_group):
321359

322360
def type_is_subclass_of_type_group(item, type_group):
323361
return isinstance(item, type_group) \
324-
or (isinstance(item, type) and issubclass(item, type_group)) \
325-
or type_in_type_group(item, type_group)
362+
or (isinstance(item, type) and issubclass(item, type_group)) \
363+
or type_in_type_group(item, type_group)
326364

327365

328366
def get_doc(doc_filename):

deepdiff/model.py

+4
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/changelog.rst

+14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ Changelog
55

66
DeepDiff Changelog
77

8+
- v6-4-0
9+
10+
- `Add Ignore List Order Option to
11+
DeepHash <https://github.com/seperman/deepdiff/pull/403>`__ by
12+
`Bobby Morck <https://github.com/bmorck>`__
13+
- `pyyaml to 6.0.1 to fix cython build
14+
problems <https://github.com/seperman/deepdiff/pull/406>`__ by
15+
`Robert Bo Davis <https://github.com/robert-bo-davis>`__
16+
- `Precompiled regex simple
17+
diff <https://github.com/seperman/deepdiff/pull/413>`__ by
18+
`cohml <https://github.com/cohml>`__
19+
- New flag: ``zip_ordered_iterables`` for forcing iterable items to
20+
be compared one by one.
21+
822
- v6-3-1
923

1024
- Bugfix deephash for paths by

docs/conf.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@
6161
# built documents.
6262
#
6363
# The short X.Y version.
64-
version = '6.3.1'
64+
version = '6.4.0'
6565
# The full version, including alpha/beta/rc tags.
66-
release = '6.3.1'
66+
release = '6.4.0'
6767

6868
load_dotenv(override=True)
6969
DOC_VERSION = os.environ.get('DOC_VERSION', version)

docs/deephash_doc.rst

+2
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.

docs/diff_doc.rst

+4
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ ignore_encoding_errors: Boolean, default = False
129129
:ref:`ignore_encoding_errors_label` 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 :ref:`encodings_label` parameter.
130130

131131

132+
zip_ordered_iterables: Boolean, default = False
133+
:ref:`zip_ordered_iterables_label`:
134+
When comparing ordered iterables such as lists, DeepDiff tries to find the smallest difference between the two iterables to report. That means that items in the two lists are not paired individually in the order of appearance in the iterables. Sometimes, that is not the desired behavior. Set this flag to True to make DeepDiff pair and compare the items in the iterables in the order they appear.
135+
132136
iterable_compare_func:
133137
:ref:`iterable_compare_func_label`:
134138
There are times that we want to guide DeepDiff as to what items to compare with other items. In such cases we can pass a iterable_compare_func that takes a function pointer to compare two items. The function takes three parameters (x, y, level) and should return True if it is a match, False if it is not a match or raise CannotCompare if it is unable to compare the two.

docs/index.rst

+13-23
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
contain the root `toctree` directive.
55
66
7-
DeepDiff 6.3.1 documentation!
7+
DeepDiff 6.4.0 documentation!
88
=============================
99

1010
*******
@@ -32,31 +32,21 @@ What is New
3232
***********
3333

3434

35-
DeepDiff 6-3-1
35+
DeepDiff 6-4-0
3636
--------------
3737

38-
This release includes many bug fixes.
38+
- `Add Ignore List Order Option to
39+
DeepHash <https://github.com/seperman/deepdiff/pull/403>`__ by
40+
`Bobby Morck <https://github.com/bmorck>`__
41+
- `pyyaml to 6.0.1 to fix cython build
42+
problems <https://github.com/seperman/deepdiff/pull/406>`__ by
43+
`Robert Bo Davis <https://github.com/robert-bo-davis>`__
44+
- `Precompiled regex simple
45+
diff <https://github.com/seperman/deepdiff/pull/413>`__ by
46+
`cohml <https://github.com/cohml>`__
47+
- New flag: ``zip_ordered_iterables`` for forcing iterable items to
48+
be compared one by one.
3949

40-
- Bugfix deephash for paths by `maggelus <https://github.com/maggelus>`__
41-
- Bugfix deephash compiled regex `maggelus <https://github.com/maggelus>`__
42-
- Fix tests dependent on toml by `martin-kokos <https://github.com/martin-kokos>`__
43-
- Bugfix for ``include_paths`` for nested dictionaries by `kor4ik <https://github.com/kor4ik>`__
44-
- Use tomli and tomli-w for dealing with tomli files by `martin-kokos <https://github.com/martin-kokos>`__
45-
- Bugfix for ``datetime.date`` by `Alex Sauer-Budge <https://github.com/amsb>`__
46-
47-
48-
DeepDiff 6-3-0
49-
--------------
50-
51-
- :ref:`prefix_or_suffix_operator_label`: This operator will skip strings that are
52-
suffix or prefix of each other.
53-
- :ref:`include_obj_callback_label` and :ref:`include_obj_callback_strict_label` are
54-
added by `Håvard Thom <https://github.com/havardthom>`__.
55-
- Fixed a corner case where numpy’s ``np.float32`` nans are not ignored
56-
when using ``ignore_nan_equality`` by `Noam
57-
Gottlieb <https://github.com/noamgot>`__
58-
- ``orjson`` becomes optional again.
59-
- Fix for ``ignore_type_in_groups`` with numeric values so it does not report number changes when the number types are different.
6050

6151
*********
6252
Tutorials

0 commit comments

Comments
 (0)