Skip to content

Commit

Permalink
Deepdiff removed (#491)
Browse files Browse the repository at this point in the history
* Find diffs between objects

* dev

* style

* req

* docs

* copy

* -print

* -print

* CHANGELOG

* After review
  • Loading branch information
marcin-usielski authored Dec 4, 2023
1 parent 2ed5f77 commit 6124dda
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 23 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## moler 2.17.0
* Implementation of internal Quectel command for setting network preferences
* Implementation of internal Quectel AT command for setting network preferences
* Removed Deepdiff

## moler 2.16.0
* Tests on Python 3.12
Expand Down
175 changes: 157 additions & 18 deletions moler/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,17 @@
"""

__author__ = 'Grzegorz Latuszek, Michal Ernst, Marcin Usielski'
__copyright__ = 'Copyright (C) 2018-2022, Nokia'
__copyright__ = 'Copyright (C) 2018-2023, Nokia'
__email__ = '[email protected], [email protected], [email protected]'

import copy
import datetime
import importlib
import logging
import re
from functools import wraps
from types import FunctionType, MethodType
from six import string_types

import deepdiff

if datetime.time not in deepdiff.diff.numbers:
deepdiff.diff.numbers = deepdiff.diff.numbers + (datetime.time,)
from math import isclose

try:
import collections.abc as collections
Expand Down Expand Up @@ -217,31 +212,175 @@ def update_dict(target_dict, expand_dict):
target_dict[key] = expand_dict[key]


def compare_objects(first_object, second_object, ignore_order=False, report_repetition=False, significant_digits=None,
exclude_paths=None, exclude_types=None, verbose_level=2):
def compare_objects(first_object, second_object, significant_digits=None,
exclude_types=None):
"""
Return difference between two objects.
:param first_object: first object to compare
:param second_object: second object to compare
:param ignore_order: ignore difference in order
:param report_repetition: report when is repetition
:param significant_digits: use to properly compare numbers(float arithmetic error)
:param exclude_paths: path which be excluded from comparison
:param exclude_types: types which be excluded from comparison
:param verbose_level: higher verbose level shows you more details - default 0.
:return: difference between two objects
"""
if exclude_paths is None:
exclude_paths = set()
if exclude_types is None:
exclude_types = set()

diff = deepdiff.DeepDiff(first_object, second_object, ignore_order=ignore_order,
report_repetition=report_repetition, significant_digits=significant_digits,
exclude_paths=exclude_paths, exclude_types=exclude_types, verbose_level=verbose_level)
diff = diff_data(first_object=first_object, second_object=second_object,
significant_digits=significant_digits, exclude_types=exclude_types)

return diff


def diff_data(first_object, second_object, significant_digits=None,
exclude_types=None, msg=None):
"""
Compare two objects recursively and return a message indicating any differences.
:param first_object: The first object for comparison.
:param second_object: The second object for comparison.
:param significant_digits: The number of significant digits to consider for float
comparison.
:param exclude_types: A list of types to exclude from comparison.
:param msg: A message to prepend to any difference messages.
Defaults to 'root' if not provided.
:return: A message indicating the differences, or an empty string if objects are
equal.
"""
if msg is None:
msg = 'root'
type_first = type(first_object)
type_second = type(second_object)
if type_first != type_second:
return "{} {} is type of {} but {} is type of {}".format(msg, first_object,
type_first,
second_object,
type_second)
elif exclude_types is not None and type_first in exclude_types:
return ""
elif isinstance(first_object, (list, tuple)):
return _compare_lists(first_object=first_object, second_object=second_object,
significant_digits=significant_digits,
exclude_types=exclude_types, msg=msg)
elif isinstance(first_object, dict):
return _compare_dicts(first_object=first_object, second_object=second_object,
significant_digits=significant_digits,
exclude_types=exclude_types, msg=msg)
elif isinstance(first_object, set):
return _compare_sets(first_object=first_object, second_object=second_object,
msg=msg)
elif isinstance(first_object, float):
abs_tol = 0.0001
if significant_digits:
abs_tol = 1.0 / 10 ** significant_digits
if not isclose(first_object, second_object, abs_tol=abs_tol):
return "{} the first value {} is different from the second value" \
" {}.".format(msg, first_object, second_object)
else:
if first_object != second_object:
return "{} First value {} is different from the second {}.".format(
msg, first_object, second_object)

return ""


def _compare_dicts(first_object, second_object, msg, significant_digits=None,
exclude_types=None):
"""
Compare two dictionaries recursively and return a message indicating any
differences.
:param first_object: The first dictionary for comparison.
:param second_object: The second dictionary for comparison.
:param significant_digits: The number of significant digits to consider for float
comparison.
:param exclude_types: A list of types to exclude from comparison.
:param msg: A message to prepend to any difference messages.
:return: A message indicating the differences, or an empty string if objects are
equal.
"""
keys_first = set(first_object.keys())
keys_second = set(second_object.keys())
diff = keys_first ^ keys_second
if diff:
for key in keys_first:
if key not in keys_second:
return "{} key {} is in the first {} but not in the second dict {}.".format(
msg, key, first_object, second_object)
for key in keys_second:
if key not in keys_first:
return "{} key {} is in the second {} but not in the first dict {}.".format(
msg, key, first_object, second_object)
else:
for key in keys_first:
res = diff_data(first_object=first_object[key],
second_object=second_object[key],
significant_digits=significant_digits,
exclude_types=exclude_types,
msg="{} -> [{}]".format(msg, key))
if res:
return res
return ""


def _compare_sets(first_object, second_object, msg):
"""
Compare two sets.
:param first_object: The first object for comparison.
:param second_object: The second object for comparison.
:param msg: A message to prepend to any difference messages.
:return: A message indicating the differences, or an empty string if objects are
equal.
"""
diff = first_object.symmetric_difference(second_object)
if diff:
for item in first_object:
if item not in second_object:
return "{} item {} is in the first set {} but not in the second set {}.".format(
msg, item, first_object, second_object)
for item in second_object:
if item not in first_object:
return "{} item {} is in the second set {} but not in the first set {}.".format(
msg, item, first_object, second_object)
return ""


def _compare_lists(first_object, second_object, msg, significant_digits=None,
exclude_types=None):
"""
Compare two lists or tuples recursively and return a message indicating any
differences.
:param first_object: The first list or tuple for comparison.
:param second_object: The second list or tuple for comparison.
:param significant_digits: The number of significant digits to consider for float
comparison.
:param exclude_types: A list of types to exclude from comparison.
:param msg: A message to prepend to any difference messages.
:return: A message indicating the differences, or an empty string if objects are
equal.
"""
len_first = len(first_object)
len_second = len(second_object)
if len_first != len_second:
return "{} List {} has {} item(s) but {} has {} item(s)".format(
msg, first_object, len_first, second_object, len_second)
max_element = len(first_object)
for i in range(0, max_element):
res = diff_data(first_object=first_object[i], second_object=second_object[i],
msg="{} -> [{}]".format(msg, i),
significant_digits=significant_digits,
exclude_types=exclude_types,
)
if res:
return res
return ""


def convert_to_number(value):
"""
Convert value to Python number type.
Expand Down
4 changes: 0 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ transitions
apscheduler == 3.8.1; python_version < '3'
apscheduler; python_version > '3'
trollius; python_version < '3'
deepdiff == 3.3.0; python_version < '3.5'
deepdiff == 5.0.2; python_version >= '3.5' and python_version < '3.6'
deepdiff == 5.7.0; python_version >= '3.6' and python_version < '3.7'
deepdiff <= 6.3.1; python_version >= '3.7'
psutil
python-dateutil
pyserial
Expand Down
33 changes: 33 additions & 0 deletions test/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
__copyright__ = 'Copyright (C) 2018-2023, Nokia'
__email__ = '[email protected], [email protected]'

import copy

import mock
import pytest
import re
Expand Down Expand Up @@ -345,3 +347,34 @@ def test_regexp_with_right_anchor():
expected = "abc"
regex = re.compile(r"abc$")
assert expected == regexp_without_anchors(regex).pattern


def test_diff_the_same_structure():
from moler.helpers import diff_data
a = ['a', 3, 4.0, True, False, ['abc', 'def'], (2.5, 3.6, 4.2), {1, 2, 3},
{'a': 3, 'b': {'c': 5, 'd': 6.3}}]
b = copy.deepcopy(a)
msg = diff_data(first_object=a, second_object=b)
assert "" == msg


def test_diff_different_types():
from moler.helpers import diff_data
a = ['a', 3, 4.0, True, False, ['abc', 'def'], (2.5, 3.6, 4.2), {1, 2, 3},
{'a': 3, 'b': {'c': 5, 'd': 6.3}}]
b = copy.deepcopy(a)
b[3] = 4
msg = diff_data(first_object=a, second_object=b)
assert "root -> [3] True is type of <class 'bool'> but 4 is type of <class 'int'>"\
== msg


def test_diff_different_values():
from moler.helpers import diff_data
a = ['a', 3, 4.0, True, False, ['abc', 'def'], (2.5, 3.6, 4.2), {1, 2, 3},
{'a': 3, 'b': {'c': 5, 'd': 6.3}}]
b = copy.deepcopy(a)
b[-1] = {'a': 3, 'b': {'c': 5, 'd': 6.2}}
msg = diff_data(first_object=a, second_object=b)
assert "root -> [8] -> [b] -> [d] the first value 6.3 is different from the" \
" second value 6.2." == msg

0 comments on commit 6124dda

Please sign in to comment.