Skip to content

Add include_obj_callback and include_obj_callback_strict #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ Authors in order of the timeline of their contributions:
- [Uwe Fladrich](https://github.com/uwefladrich) for fixing bug when diff'ing non-sequence iterables
- [Michal Ozery-Flato](https://github.com/michalozeryflato) for setting equal_nan=ignore_nan_inequality in the call for np.array_equal
- [martin-kokos](https://github.com/martin-kokos) for using Pytest's tmp_path fixture instead of /tmp/
- Håvard Thom [havardthom](https://github.com/havardthom) for adding include_obj_callback and include_obj_callback_strict.
14 changes: 14 additions & 0 deletions deepdiff/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ def __init__(self,
exclude_obj_callback=None,
exclude_obj_callback_strict=None,
exclude_paths=None,
include_obj_callback=None,
include_obj_callback_strict=None,
include_paths=None,
exclude_regex_paths=None,
exclude_types=None,
Expand Down Expand Up @@ -201,6 +203,8 @@ def __init__(self,
self.ignore_string_case = ignore_string_case
self.exclude_obj_callback = exclude_obj_callback
self.exclude_obj_callback_strict = exclude_obj_callback_strict
self.include_obj_callback = include_obj_callback
self.include_obj_callback_strict = include_obj_callback_strict
self.number_to_string = number_to_string_func or number_to_string
self.iterable_compare_func = iterable_compare_func
self.ignore_private_variables = ignore_private_variables
Expand Down Expand Up @@ -464,6 +468,16 @@ def _skip_this(self, level):
(self.exclude_obj_callback_strict(level.t1, level_path) and
self.exclude_obj_callback_strict(level.t2, level_path)):
skip = True
elif self.include_obj_callback and level_path != 'root':
skip = True
if (self.include_obj_callback(level.t1, level_path) or self.include_obj_callback(level.t2, level_path)):
skip = False
elif self.include_obj_callback_strict and level_path != 'root':
skip = True
if (self.include_obj_callback_strict(level.t1, level_path) and
self.include_obj_callback_strict(level.t2, level_path)):
skip = False


return skip

Expand Down
11 changes: 10 additions & 1 deletion docs/diff_doc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,16 @@ exclude_obj_callback: function, default = None

exclude_obj_callback_strict: function, default = None
:ref:`exclude_obj_callback_strict_label`
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
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.

include_obj_callback: function, default = None
:ref:`include_obj_callback_label`
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.
This is to give the user a higher level of control than one can achieve via include_paths.

include_obj_callback_strict: function, default = None
:ref:`include_obj_callback_strict_label`
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.

get_deep_distance: Boolean, default = False
: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.
Expand Down
38 changes: 38 additions & 0 deletions docs/ignore_types_or_values.rst
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,44 @@ exclude_obj_callback_strict: function, default = None
>>> DeepDiff(t1, t2, exclude_obj_callback_strict=exclude_obj_callback_strict)
{'values_changed': {"root['x']": {'new_value': 12, 'old_value': 10}}}


.. _include_obj_callback_label:

Include Obj Callback
--------------------

include_obj_callback: function, default = None
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.
This is to give the user a higher level of control than one can achieve via include_paths.

>>> def include_obj_callback(obj, path):
... return True if "include" in path or isinstance(obj, int) else False
...
>>> t1 = {"x": 10, "y": "b", "z": "c", "include_me": "a"}
>>> t2 = {"x": 10, "y": "b", "z": "c", "include_me": "b"}
>>> DeepDiff(t1, t2, include_obj_callback=include_obj_callback)
{'values_changed': {"root['include_me']": {'new_value': "b", 'old_value': "a"}}}


.. _include_obj_callback_strict_label:

Include Obj Callback Strict
---------------------------

include_obj_callback_strict: function, default = None
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.

>>> def include_obj_callback_strict(obj, path):
... return True if isinstance(obj, int) and obj > 10 else False
...
>>> t1 = {"x": 10, "y": "b", "z": "c"}
>>> t1 = {"x": 12, "y": "b", "z": "c"}
>>> DeepDiff(t1, t2, include_obj_callback=include_obj_callback_strict)
{'values_changed': {"root['x']": {'new_value': 12, 'old_value': 10}}}
>>> DeepDiff(t1, t2, include_obj_callback_strict=include_obj_callback_strict)
{}


.. _truncate_datetime_label:

Truncate Datetime
Expand Down
2 changes: 2 additions & 0 deletions tests/test_delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,8 @@ def test_delta_view_and_to_delta_dict_are_equal_when_parameteres_passed(self):
'exclude_types_tuple': None,
'ignore_type_subclasses': False,
'ignore_string_case': False,
'include_obj_callback': None,
'include_obj_callback_strict': None,
'exclude_obj_callback': None,
'exclude_obj_callback_strict': None,
'ignore_private_variables': True,
Expand Down
24 changes: 24 additions & 0 deletions tests/test_diff_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,30 @@ def test_skip_regexp(self):
result = {}
assert result == ddiff

def test_include_obj_callback(self):
def include_obj_callback(obj, path):
return True if "include" in path or isinstance(obj, int) else False

t1 = {"x": 10, "y": "b", "z": "c", "include_me": "a"}
t2 = {"x": 10, "y": "c", "z": "b", "include_me": "b"}
ddiff = DeepDiff(t1, t2, include_obj_callback=include_obj_callback)
result = {'values_changed': {"root['include_me']": {'new_value': "b", 'old_value': "a"}}}
assert result == ddiff
assert {"root['include_me']"} == ddiff.affected_paths
assert {"include_me"} == ddiff.affected_root_keys

def test_include_obj_callback_strict(self):
def include_obj_callback_strict(obj, path):
return True if isinstance(obj, int) and obj > 10 else False

t1 = {"x": 11, "y": 10, "z": "c"}
t2 = {"x": 12, "y": 12, "z": "c"}
ddiff = DeepDiff(t1, t2, include_obj_callback_strict=include_obj_callback_strict)
result = {'values_changed': {"root['x']": {'new_value': 12, 'old_value': 11}}}
assert result == ddiff
assert {"root['x']"} == ddiff.affected_paths
assert {"x"} == ddiff.affected_root_keys

def test_skip_exclude_obj_callback(self):
def exclude_obj_callback(obj, path):
return True if "skip" in path or isinstance(obj, int) else False
Expand Down