Skip to content

Commit 5151b1b

Browse files
authored
Merge pull request #370 from havardthom/dev
Add include_obj_callback and include_obj_callback_strict
2 parents 5e7dcdd + 1c4e274 commit 5151b1b

File tree

6 files changed

+89
-1
lines changed

6 files changed

+89
-1
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ Authors in order of the timeline of their contributions:
5050
- [Uwe Fladrich](https://github.com/uwefladrich) for fixing bug when diff'ing non-sequence iterables
5151
- [Michal Ozery-Flato](https://github.com/michalozeryflato) for setting equal_nan=ignore_nan_inequality in the call for np.array_equal
5252
- [martin-kokos](https://github.com/martin-kokos) for using Pytest's tmp_path fixture instead of /tmp/
53+
- Håvard Thom [havardthom](https://github.com/havardthom) for adding include_obj_callback and include_obj_callback_strict.

deepdiff/diff.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ def __init__(self,
121121
exclude_obj_callback=None,
122122
exclude_obj_callback_strict=None,
123123
exclude_paths=None,
124+
include_obj_callback=None,
125+
include_obj_callback_strict=None,
124126
include_paths=None,
125127
exclude_regex_paths=None,
126128
exclude_types=None,
@@ -201,6 +203,8 @@ def __init__(self,
201203
self.ignore_string_case = ignore_string_case
202204
self.exclude_obj_callback = exclude_obj_callback
203205
self.exclude_obj_callback_strict = exclude_obj_callback_strict
206+
self.include_obj_callback = include_obj_callback
207+
self.include_obj_callback_strict = include_obj_callback_strict
204208
self.number_to_string = number_to_string_func or number_to_string
205209
self.iterable_compare_func = iterable_compare_func
206210
self.ignore_private_variables = ignore_private_variables
@@ -464,6 +468,16 @@ def _skip_this(self, level):
464468
(self.exclude_obj_callback_strict(level.t1, level_path) and
465469
self.exclude_obj_callback_strict(level.t2, level_path)):
466470
skip = True
471+
elif self.include_obj_callback and level_path != 'root':
472+
skip = True
473+
if (self.include_obj_callback(level.t1, level_path) or self.include_obj_callback(level.t2, level_path)):
474+
skip = False
475+
elif self.include_obj_callback_strict and level_path != 'root':
476+
skip = True
477+
if (self.include_obj_callback_strict(level.t1, level_path) and
478+
self.include_obj_callback_strict(level.t2, level_path)):
479+
skip = False
480+
467481

468482
return skip
469483

docs/diff_doc.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,16 @@ exclude_obj_callback: function, default = None
6262

6363
exclude_obj_callback_strict: function, default = None
6464
:ref:`exclude_obj_callback_strict_label`
65-
A function that works the same way as exclude_obj_callback, but excludes elements from the result only if the function returns True for both elements
65+
A function that works the same way as exclude_obj_callback, but excludes elements from the result only if the function returns True for both elements.
66+
67+
include_obj_callback: function, default = None
68+
:ref:`include_obj_callback_label`
69+
A function that takes the object and its path and returns a Boolean. If True is returned, the object is included in the results, otherwise it is excluded.
70+
This is to give the user a higher level of control than one can achieve via include_paths.
71+
72+
include_obj_callback_strict: function, default = None
73+
:ref:`include_obj_callback_strict_label`
74+
A function that works the same way as include_obj_callback, but includes elements in the result only if the function returns True for both elements.
6675

6776
get_deep_distance: Boolean, default = False
6877
:ref:`get_deep_distance_label` will get you the deep distance between objects. The distance is a number between 0 and 1 where zero means there is no diff between the 2 objects and 1 means they are very different. Note that this number should only be used to compare the similarity of 2 objects and nothing more. The algorithm for calculating this number may or may not change in the future releases of DeepDiff.

docs/ignore_types_or_values.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,44 @@ exclude_obj_callback_strict: function, default = None
291291
>>> DeepDiff(t1, t2, exclude_obj_callback_strict=exclude_obj_callback_strict)
292292
{'values_changed': {"root['x']": {'new_value': 12, 'old_value': 10}}}
293293

294+
295+
.. _include_obj_callback_label:
296+
297+
Include Obj Callback
298+
--------------------
299+
300+
include_obj_callback: function, default = None
301+
A function that takes the object and its path and returns a Boolean. If True is returned, the object is included in the results, otherwise it is excluded.
302+
This is to give the user a higher level of control than one can achieve via include_paths.
303+
304+
>>> def include_obj_callback(obj, path):
305+
... return True if "include" in path or isinstance(obj, int) else False
306+
...
307+
>>> t1 = {"x": 10, "y": "b", "z": "c", "include_me": "a"}
308+
>>> t2 = {"x": 10, "y": "b", "z": "c", "include_me": "b"}
309+
>>> DeepDiff(t1, t2, include_obj_callback=include_obj_callback)
310+
{'values_changed': {"root['include_me']": {'new_value': "b", 'old_value': "a"}}}
311+
312+
313+
.. _include_obj_callback_strict_label:
314+
315+
Include Obj Callback Strict
316+
---------------------------
317+
318+
include_obj_callback_strict: function, default = None
319+
A function that works the same way as include_obj_callback, but includes elements in the result only if the function returns True for both elements.
320+
321+
>>> def include_obj_callback_strict(obj, path):
322+
... return True if isinstance(obj, int) and obj > 10 else False
323+
...
324+
>>> t1 = {"x": 10, "y": "b", "z": "c"}
325+
>>> t1 = {"x": 12, "y": "b", "z": "c"}
326+
>>> DeepDiff(t1, t2, include_obj_callback=include_obj_callback_strict)
327+
{'values_changed': {"root['x']": {'new_value': 12, 'old_value': 10}}}
328+
>>> DeepDiff(t1, t2, include_obj_callback_strict=include_obj_callback_strict)
329+
{}
330+
331+
294332
.. _truncate_datetime_label:
295333

296334
Truncate Datetime

tests/test_delta.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,8 @@ def test_delta_view_and_to_delta_dict_are_equal_when_parameteres_passed(self):
11881188
'exclude_types_tuple': None,
11891189
'ignore_type_subclasses': False,
11901190
'ignore_string_case': False,
1191+
'include_obj_callback': None,
1192+
'include_obj_callback_strict': None,
11911193
'exclude_obj_callback': None,
11921194
'exclude_obj_callback_strict': None,
11931195
'ignore_private_variables': True,

tests/test_diff_text.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,30 @@ def test_skip_regexp(self):
13961396
result = {}
13971397
assert result == ddiff
13981398

1399+
def test_include_obj_callback(self):
1400+
def include_obj_callback(obj, path):
1401+
return True if "include" in path or isinstance(obj, int) else False
1402+
1403+
t1 = {"x": 10, "y": "b", "z": "c", "include_me": "a"}
1404+
t2 = {"x": 10, "y": "c", "z": "b", "include_me": "b"}
1405+
ddiff = DeepDiff(t1, t2, include_obj_callback=include_obj_callback)
1406+
result = {'values_changed': {"root['include_me']": {'new_value': "b", 'old_value': "a"}}}
1407+
assert result == ddiff
1408+
assert {"root['include_me']"} == ddiff.affected_paths
1409+
assert {"include_me"} == ddiff.affected_root_keys
1410+
1411+
def test_include_obj_callback_strict(self):
1412+
def include_obj_callback_strict(obj, path):
1413+
return True if isinstance(obj, int) and obj > 10 else False
1414+
1415+
t1 = {"x": 11, "y": 10, "z": "c"}
1416+
t2 = {"x": 12, "y": 12, "z": "c"}
1417+
ddiff = DeepDiff(t1, t2, include_obj_callback_strict=include_obj_callback_strict)
1418+
result = {'values_changed': {"root['x']": {'new_value': 12, 'old_value': 11}}}
1419+
assert result == ddiff
1420+
assert {"root['x']"} == ddiff.affected_paths
1421+
assert {"x"} == ddiff.affected_root_keys
1422+
13991423
def test_skip_exclude_obj_callback(self):
14001424
def exclude_obj_callback(obj, path):
14011425
return True if "skip" in path or isinstance(obj, int) else False

0 commit comments

Comments
 (0)