Skip to content

Commit 8d2c0ac

Browse files
authored
Merge pull request #337 from seperman/dev
DeepDiff 6
2 parents 81341e2 + a55ed44 commit 8d2c0ac

19 files changed

+225
-54
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ A clear and concise description of what you expected to happen.
2121
**OS, DeepDiff version and Python version (please complete the following information):**
2222
- OS: [e.g. Ubuntu]
2323
- Version [e.g. 20LTS]
24+
- Python Version [e.g. 3.9.12]
25+
- DeepDiff Version [e.g. 5.8.0]
2426

2527
**Additional context**
2628
Add any other context about the problem here.

.github/workflows/main.yaml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ jobs:
1414
matrix:
1515
python-version: [3.7, 3.8, 3.9, "3.10"]
1616
architecture: ["x64"]
17-
1817
steps:
1918
- uses: actions/checkout@v2
2019
- name: Setup Python ${{ matrix.python-version }} on ${{ matrix.architecture }}
@@ -32,20 +31,30 @@ jobs:
3231
restore-keys: |
3332
${{ runner.os }}-pip-
3433
${{ runner.os }}-
34+
- name: Install dependencies py3.7
35+
if: matrix.python-version == 3.7
36+
run: pip install -r requirements-dev-3.7.txt
3537
- name: Install dependencies
38+
if: matrix.python-version != 3.7
3639
run: pip install -r requirements-dev.txt
3740
- name: Lint with flake8
41+
if: matrix.python-version == 3.10
3842
run: |
3943
# stop the build if there are Python syntax errors or undefined names
4044
flake8 deepdiff --count --select=E9,F63,F7,F82 --show-source --statistics
4145
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
4246
flake8 deepdiff --count --exit-zero --max-complexity=26 --max-line-lengt=250 --statistics
43-
- name: Test with pytest
47+
- name: Test with pytest and get the coverage
48+
if: matrix.python-version == 3.10
49+
run: |
50+
pytest --cov-report=xml --cov=deepdiff tests/ --runslow
51+
- name: Test with pytest and no coverage report
52+
if: matrix.python-version != 3.10
4453
run: |
45-
pytest --cov-report=xml --cov=deepdiff tests/ --runslow
54+
pytest
4655
- name: Upload coverage to Codecov
4756
uses: codecov/codecov-action@v1
48-
if: matrix.python-version == 3.8
57+
if: matrix.python-version == 3.10
4958
with:
5059
file: ./coverage.xml
5160
env_vars: OS,PYTHON

AUTHORS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,5 @@ Authors in order of the timeline of their contributions:
4444
- Håvard Thom [havardthom](https://github.com/havardthom) for adding UUID support.
4545
- Dhanvantari Tilak [Dhanvantari](https://github.com/Dhanvantari) for Bug-Fix: `TypeError in _get_numbers_distance() when ignore_order = True`.
4646
- Yael Mintz [yaelmi3](https://github.com/yaelmi3) for detailed pretty print when verbose_level=2.
47+
- Mikhail Khviyuzov [mskhviyu](https://github.com/mskhviyu) for Exclude obj callback strict.
48+
- [dtorres-sf](https://github.com/dtorres-sf) for the fix for diffing using iterable_compare_func with nested objects.

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ DeepDiff 5-8-0 includes bug fixes and improvements:
4141

4242
### Install from PyPi:
4343

44-
`pip install deepdiff`
44+
`pip install deepdiff6`
4545

4646
If you want to use DeepDiff from commandline:
4747

48-
`pip install "deepdiff[cli]"`
48+
`pip install "deepdiff6[cli]"`
49+
50+
> Note: prior to DeepDiff 6, we used `pip install deepdiff` to install DeepDiff. DeepDiff 6 is being published with a different package name on Pypi temporarily until further notice.
4951
5052
### Importing
5153

@@ -55,7 +57,7 @@ If you want to use DeepDiff from commandline:
5557
>>> from deepdiff import DeepHash # For hashing objects based on their contents
5658
```
5759

58-
Note: if you want to use DeepDiff via commandline, make sure to run `pip install "deepdiff[cli]"`. Then you can access the commands via:
60+
Note: if you want to use DeepDiff via commandline, make sure to run `pip install "deepdiff6[cli]"`. Then you can access the commands via:
5961

6062
- DeepDiff
6163
- `$ deep diff --help`

deepdiff/delta.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,21 @@ def _del_elem(self, parent, parent_to_obj_elem, parent_to_obj_action,
263263
def _do_iterable_item_added(self):
264264
iterable_item_added = self.diff.get('iterable_item_added', {})
265265
iterable_item_moved = self.diff.get('iterable_item_moved')
266+
267+
# First we need to create a placeholder for moved items.
268+
# This will then get replaced below after we go through added items.
269+
# Without this items can get double added because moved store the new_value and does not need item_added replayed
266270
if iterable_item_moved:
267-
added_dict = {v["new_path"]: v["value"] for k, v in iterable_item_moved.items()}
271+
added_dict = {v["new_path"]: None for k, v in iterable_item_moved.items()}
268272
iterable_item_added.update(added_dict)
269273

270274
if iterable_item_added:
271275
self._do_item_added(iterable_item_added, insert=True)
272276

277+
if iterable_item_moved:
278+
added_dict = {v["new_path"]: v["value"] for k, v in iterable_item_moved.items()}
279+
self._do_item_added(added_dict, insert=False)
280+
273281
def _do_dictionary_item_added(self):
274282
dictionary_item_added = self.diff.get('dictionary_item_added')
275283
if dictionary_item_added:

deepdiff/diff.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# However the docstring expects it in a specific order in order to pass!
88
import difflib
99
import logging
10+
from enum import Enum
1011
from copy import deepcopy
1112
from math import isclose as is_close
1213
from collections.abc import Mapping, Iterable
@@ -21,7 +22,7 @@
2122
number_to_string, datetime_normalize, KEY_TO_VAL_STR, booleans,
2223
np_ndarray, get_numpy_ndarray_rows, OrderedSetPlus, RepeatedTimer,
2324
TEXT_VIEW, TREE_VIEW, DELTA_VIEW, detailed__dict__,
24-
np, get_truncate_datetime, dict_, CannotCompare)
25+
np, get_truncate_datetime, dict_, CannotCompare, ENUM_IGNORE_KEYS)
2526
from deepdiff.serialization import SerializationMixin
2627
from deepdiff.distance import DistanceMixin
2728
from deepdiff.model import (
@@ -116,6 +117,7 @@ def __init__(self,
116117
cutoff_intersection_for_pairs=CUTOFF_INTERSECTION_FOR_PAIRS_DEFAULT,
117118
encodings=None,
118119
exclude_obj_callback=None,
120+
exclude_obj_callback_strict=None,
119121
exclude_paths=None,
120122
exclude_regex_paths=None,
121123
exclude_types=None,
@@ -194,6 +196,7 @@ def __init__(self,
194196
self.type_check_func = type_is_subclass_of_type_group if ignore_type_subclasses else type_in_type_group
195197
self.ignore_string_case = ignore_string_case
196198
self.exclude_obj_callback = exclude_obj_callback
199+
self.exclude_obj_callback_strict = exclude_obj_callback_strict
197200
self.number_to_string = number_to_string_func or number_to_string
198201
self.iterable_compare_func = iterable_compare_func
199202
self.ignore_private_variables = ignore_private_variables
@@ -386,8 +389,19 @@ def unmangle(attribute):
386389

387390
return {i: getattr(object, unmangle(i)) for i in all_slots}
388391

389-
def _diff_obj(self, level, parents_ids=frozenset(),
390-
is_namedtuple=False):
392+
def _diff_enum(self, level, parents_ids=frozenset()):
393+
t1 = detailed__dict__(level.t1, ignore_private_variables=self.ignore_private_variables, ignore_keys=ENUM_IGNORE_KEYS)
394+
t2 = detailed__dict__(level.t2, ignore_private_variables=self.ignore_private_variables, ignore_keys=ENUM_IGNORE_KEYS)
395+
396+
self._diff_dict(
397+
level,
398+
parents_ids,
399+
print_as_attribute=True,
400+
override=True,
401+
override_t1=t1,
402+
override_t2=t2)
403+
404+
def _diff_obj(self, level, parents_ids=frozenset(), is_namedtuple=False):
391405
"""Difference of 2 objects"""
392406
try:
393407
if is_namedtuple:
@@ -429,6 +443,10 @@ def _skip_this(self, level):
429443
elif self.exclude_obj_callback and \
430444
(self.exclude_obj_callback(level.t1, level.path()) or self.exclude_obj_callback(level.t2, level.path())):
431445
skip = True
446+
elif self.exclude_obj_callback_strict and \
447+
(self.exclude_obj_callback_strict(level.t1, level.path()) and
448+
self.exclude_obj_callback_strict(level.t2, level.path())):
449+
skip = True
432450

433451
return skip
434452

@@ -709,7 +727,7 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
709727
x,
710728
y,
711729
child_relationship_class=child_relationship_class,
712-
child_relationship_param=i)
730+
child_relationship_param=j)
713731
self._diff(next_level, parents_ids_added)
714732

715733
def _diff_str(self, level):
@@ -1350,6 +1368,9 @@ def _diff(self, level, parents_ids=frozenset(), _original_type=None):
13501368
elif isinstance(level.t1, Iterable):
13511369
self._diff_iterable(level, parents_ids, _original_type=_original_type)
13521370

1371+
elif isinstance(level.t1, Enum):
1372+
self._diff_enum(level, parents_ids)
1373+
13531374
else:
13541375
self._diff_obj(level, parents_ids)
13551376

deepdiff/helper.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ class np_type:
123123
TEXT_VIEW = 'text'
124124
DELTA_VIEW = '_delta'
125125

126+
ENUM_IGNORE_KEYS = frozenset(['_name_', '_value_', '_sort_order_'])
127+
126128

127129
def short_repr(item, max_length=15):
128130
"""Short representation of item if it is too long"""
@@ -495,7 +497,7 @@ def _eval_date(params):
495497

496498
def literal_eval_extended(item):
497499
"""
498-
An extend version of literal_eval
500+
An extended version of literal_eval
499501
"""
500502
try:
501503
return literal_eval(item)
@@ -584,21 +586,27 @@ def get_homogeneous_numpy_compatible_type_of_seq(seq):
584586
return False
585587

586588

587-
def detailed__dict__(obj, ignore_private_variables=True):
589+
def detailed__dict__(obj, ignore_private_variables=True, ignore_keys=frozenset()):
588590
"""
589591
Get the detailed dictionary of an object.
590592
591593
This is used so we retrieve object properties too.
592594
"""
593595
result = obj.__dict__.copy() # A shallow copy
594596
private_var_prefix = f"_{obj.__class__.__name__}__" # The semi private variables in Python get this prefix
597+
for key in ignore_keys:
598+
if key in result or (
599+
ignore_private_variables and key.startswith('__') and not key.startswith(private_var_prefix)
600+
):
601+
del result[key]
595602
for key in dir(obj):
596-
if key not in result and (
603+
if key not in result and key not in ignore_keys and (
597604
not ignore_private_variables or (
598605
ignore_private_variables and not key.startswith('__') and not key.startswith(private_var_prefix)
599606
)
600607
):
601608
value = getattr(obj, key)
602609
if not callable(value):
610+
print(f"{key}: {value}")
603611
result[key] = value
604612
return result

docs/authors.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Authors in order of the timeline of their contributions:
5656
- Dhanvantari Tilak `Dhanvantari`_ for Bug-Fix:
5757
``TypeError in _get_numbers_distance() when ignore_order = True``.
5858
- Yael Mintz `yaelmi3`_ for detailed pretty print when verbose_level=2.
59+
- Mikhail Khviyuzov `mskhviyu`_ for Exclude obj callback strict.
60+
- `dtorres-sf`_ for the fix for diffing using iterable_compare_func with nested objects.
5961

6062
.. _Sep Dehpour (Seperman): http://www.zepworks.com
6163
.. _Victor Hahn Castell: http://hahncastell.de
@@ -96,7 +98,7 @@ Authors in order of the timeline of their contributions:
9698
.. _havardthom: https://github.com/havardthom
9799
.. _Dhanvantari: https://github.com/Dhanvantari
98100
.. _yaelmi3: https://github.com/yaelmi3
99-
101+
.. _mskhviyu: https://github.com/mskhviyu
100102

101103
Thank you for contributing to DeepDiff!
102104

docs/ignore_types_or_values.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,18 @@ exclude_obj_callback: function, default = None
272272
>>> DeepDiff(t1, t2, exclude_obj_callback=exclude_obj_callback)
273273
{}
274274

275+
exclude_obj_callback_strict: function, default = None
276+
A function works the same way as exclude_obj_callback, but excludes elements from the result only if the function returns True for both elements
277+
278+
>>> def exclude_obj_callback_strict(obj, path):
279+
... return True if isinstance(obj, int) and obj > 10 else False
280+
...
281+
>>> t1 = {"x": 10, "y": "b", "z": "c"}
282+
>>> t2 = {"x": 12, "y": "b", "z": "c"}
283+
>>> DeepDiff(t1, t2, exclude_obj_callback=exclude_obj_callback_strict)
284+
{}
285+
>>> DeepDiff(t1, t2, exclude_obj_callback_strict=exclude_obj_callback_strict)
286+
{'values_changed': {"root['x']": {'new_value': 12, 'old_value': 10}}}
275287

276288
.. _truncate_datetime_label:
277289

docs/index.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,17 @@ Installation
7575

7676
Install from PyPi::
7777

78-
pip install deepdiff
78+
pip install deepdiff6
7979

8080
If you want to use DeepDiff from commandline::
8181

82-
pip install "deepdiff[cli]"
82+
pip install "deepdiff6[cli]"
8383

8484
Read about DeepDiff optimizations at :ref:`optimizations_label`
8585

86+
Note: prior to DeepDiff 6, we used pip install deepdiff to install DeepDiff::
87+
DeepDiff 6 is being published with a different package name on Pypi temporarily until further notice.
88+
8689

8790
Importing
8891
~~~~~~~~~
@@ -97,7 +100,7 @@ Importing
97100
98101
99102
Note: if you want to use DeepDiff via commandline, make sure to run::
100-
pip install "deepdiff[cli]"
103+
pip install "deepdiff6[cli]"
101104

102105
Then you can access the commands via:
103106

requirements-cli.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
click==8.0.3
2-
pyyaml==5.4.1
1+
click==8.1.3
2+
pyyaml==6.0
33
toml==0.10.2
4-
clevercsv==0.7.1
4+
clevercsv==0.7.4

requirements-dev-3.7.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
wheel==0.37.0
2+
-r requirements.txt
3+
-r requirements-cli.txt
4+
bump2version==1.0.1
5+
jsonpickle==2.2.0
6+
ipdb==0.13.9
7+
numpy==1.21.6
8+
pytest==7.1.2
9+
python-dotenv==0.20.0

requirements-dev.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ wheel==0.37.0
22
-r requirements.txt
33
-r requirements-cli.txt
44
bump2version==1.0.1
5-
jsonpickle==2.0.0
6-
coverage==6.0.2
5+
jsonpickle==2.2.0
6+
coverage==6.4.3
77
ipdb==0.13.9
8-
numpy==1.21.2
9-
pytest==6.2.5
8+
numpy==1.23.1
9+
pytest==7.1.2
1010
pytest-cov==3.0.0
11-
python-dotenv==0.19.1
12-
watchdog==2.1.6
13-
Sphinx==4.2.0
11+
python-dotenv==0.20.0
12+
watchdog==2.1.9
13+
Sphinx==5.1.1
1414
sphinx-sitemap==2.2.0
15-
flake8==4.0.1
15+
flake8==5.0.4

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ def get_reqs(filename):
3434
author='Seperman',
3535
author_email='[email protected]',
3636
license='MIT',
37-
packages=['deepdiff'],
37+
packages=['deepdiff6'],
3838
zip_safe=True,
3939
test_suite="tests",
4040
include_package_data=True,
41-
tests_require=['mock'], # 'numpy==1.11.2' numpy is needed but comes already installed with travis
41+
tests_require=['mock'],
4242
long_description=long_description,
4343
long_description_content_type='text/markdown',
4444
install_requires=reqs,

tests/fixtures/compare_func_result1.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
"root['Cars'][3]['production']"
77
],
88
"values_changed": {
9-
"root['Cars'][0]['dealers'][1]['quantity']": {
9+
"root['Cars'][2]['dealers'][0]['quantity']": {
1010
"new_value": 50,
1111
"old_value": 20
1212
},
13-
"root['Cars'][2]['model_numbers'][2]": {
13+
"root['Cars'][1]['model_numbers'][2]": {
1414
"new_value": 3,
1515
"old_value": 4
1616
},
@@ -20,20 +20,20 @@
2020
}
2121
},
2222
"iterable_item_added": {
23-
"root['Cars'][0]['dealers'][1]": {
23+
"root['Cars'][2]['dealers'][1]": {
2424
"id": 200,
2525
"address": "200 Fake St",
2626
"quantity": 10
2727
},
28-
"root['Cars'][2]['model_numbers'][3]": 4,
28+
"root['Cars'][1]['model_numbers'][3]": 4,
2929
"root['Cars'][0]": {
3030
"id": "7",
3131
"make": "Toyota",
3232
"model": "8Runner"
3333
}
3434
},
3535
"iterable_item_removed": {
36-
"root['Cars'][0]['dealers'][0]": {
36+
"root['Cars'][2]['dealers'][0]": {
3737
"id": 103,
3838
"address": "103 Fake St",
3939
"quantity": 50

0 commit comments

Comments
 (0)