diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b0f7856f..8cdc0e21 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,8 @@ assignees: '' --- +Please checkout the [F.A.Q](https://zepworks.com/deepdiff/current/faq.html) page before creating a bug ticket to make sure it is not already addressed. + **Describe the bug** A clear and concise description of what the bug is. diff --git a/AUTHORS.md b/AUTHORS.md index 83f35113..458b1530 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -41,3 +41,5 @@ Authors in order of the timeline of their contributions: - Sun Ao [eggachecat](https://github.com/eggachecat) for adding custom operators. - Sun Ao [eggachecat](https://github.com/eggachecat) for adding ignore_order_func. - [SlavaSkvortsov](https://github.com/SlavaSkvortsov) for fixing unprocessed key error. +- Håvard Thom [havardthom](https://github.com/havardthom) for adding UUID support. +- Dhanvantari Tilak [Dhanvantari](https://github.com/Dhanvantari) for Bug-Fix: `TypeError in _get_numbers_distance() when ignore_order = True`. diff --git a/CHANGELOG.md b/CHANGELOG.md index e75ddb9e..3c67bbc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # DeepDiff Change log +- v5-7-0: + - https://github.com/seperman/deepdiff/pull/284 Bug-Fix: TypeError in _get_numbers_distance() when ignore_order = True by @Dhanvantari + - https://github.com/seperman/deepdiff/pull/280 Add support for UUIDs by @havardthom + - Major bug in delta when it comes to iterable items added or removed is investigated by @uwefladrich and resolved by @seperman - v5-6-0: Adding custom operators, and ignore_order_func. Bugfix: verbose_level==0 should disable values_changes. Bugfix: unprocessed key error. - v5-5-0: adding iterable_compare_func for DeepDiff, adding output_format of list for path() in tree view. - v5-4-0: adding strict_checking for numbers in DeepSearch. diff --git a/README.md b/README.md index bc101db7..7ac71e77 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# DeepDiff v 5.6.0 +# DeepDiff v 5.7.0 ![Downloads](https://img.shields.io/pypi/dm/deepdiff.svg?style=flat) ![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat) @@ -14,13 +14,16 @@ Tested on Python 3.6+ and PyPy3. -**NOTE: Python 2 is not supported any more. DeepDiff v3.3.0 was the last version to support Python 2** +- **[Documentation](https://zepworks.com/deepdiff/5.7.0/)** -**NOTE: The last version of DeepDiff to work on Python 3.5 was DeepDiff 5-0-2** +## What is new? -- [Documentation](https://zepworks.com/deepdiff/5.6.0/) +DeepDiff 5-7-0 includes bug fixes and improvements: + +- https://github.com/seperman/deepdiff/pull/284 Bug-Fix: TypeError in _get_numbers_distance() when ignore_order = True by @Dhanvantari +- https://github.com/seperman/deepdiff/pull/280 Add support for UUIDs by @havardthom +- Major bug in delta when it comes to iterable items added or removed is investigated by @uwefladrich and resolved by @seperman -## What is new? DeepDiff 5-6-0 allows you to pass custom operators. @@ -110,13 +113,13 @@ Note: if you want to use DeepDiff via commandline, make sure to run `pip install DeepDiff gets the difference of 2 objects. -> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.6.0/diff.html) -> - The full documentation of all modules can be found on +> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.7.0/diff.html) +> - The full documentation of all modules can be found on > - Tutorials and posts about DeepDiff can be found on ## A few Examples -> Note: This is just a brief overview of what DeepDiff can do. Please visit for full documentation. +> Note: This is just a brief overview of what DeepDiff can do. Please visit for full documentation. ### List difference ignoring order or duplicates @@ -320,8 +323,8 @@ Example: ``` -> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.6.0/diff.html) -> - The full documentation can be found on +> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.7.0/diff.html) +> - The full documentation can be found on # Deep Search @@ -353,8 +356,8 @@ And you can pass all the same kwargs as DeepSearch to grep too: {'matched_paths': {"root['somewhere']": 'around'}, 'matched_values': {"root['long']": 'somewhere'}} ``` -> - Please take a look at the [DeepSearch docs](https://zepworks.com/deepdiff/5.6.0/dsearch.html) -> - The full documentation can be found on +> - Please take a look at the [DeepSearch docs](https://zepworks.com/deepdiff/5.7.0/dsearch.html) +> - The full documentation can be found on # Deep Hash (New in v4-0-0) @@ -362,8 +365,8 @@ And you can pass all the same kwargs as DeepSearch to grep too: DeepHash is designed to give you hash of ANY python object based on its contents even if the object is not considered hashable! DeepHash is supposed to be deterministic in order to make sure 2 objects that contain the same data, produce the same hash. -> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.6.0/deephash.html) -> - The full documentation can be found on +> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.7.0/deephash.html) +> - The full documentation can be found on Let's say you have a dictionary object. @@ -411,8 +414,8 @@ Which you can write as: At first it might seem weird why DeepHash(obj)[obj] but remember that DeepHash(obj) is a dictionary of hashes of all other objects that obj contains too. -> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.6.0/deephash.html) -> - The full documentation can be found on +> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.7.0/deephash.html) +> - The full documentation can be found on # Using DeepDiff in unit tests diff --git a/deepdiff/__init__.py b/deepdiff/__init__.py index 79a37575..55539373 100644 --- a/deepdiff/__init__.py +++ b/deepdiff/__init__.py @@ -1,6 +1,6 @@ """This module offers the DeepDiff, DeepSearch, grep, Delta and DeepHash classes.""" # flake8: noqa -__version__ = '5.6.0' +__version__ = '5.7.0' import logging if __name__ == '__main__': diff --git a/deepdiff/delta.py b/deepdiff/delta.py index 146eae82..cae3541a 100644 --- a/deepdiff/delta.py +++ b/deepdiff/delta.py @@ -279,12 +279,19 @@ def _do_attribute_added(self): if attribute_added: self._do_item_added(attribute_added) + @staticmethod + def _sort_key_for_item_added(path_and_value): + elements = _path_to_elements(path_and_value[0]) + # Example elements: [(4.3, 'GET'), ('b', 'GETATTR'), ('a3', 'GET')] + # We only care about the values in the elements not how to get the values. + return [i[0] for i in elements] + def _do_item_added(self, items, sort=True, insert=False): if sort: # sorting items by their path so that the items with smaller index # are applied first (unless `sort` is `False` so that order of # added items is retained, e.g. for dicts). - items = sorted(items.items(), key=lambda x: x[0]) + items = sorted(items.items(), key=self._sort_key_for_item_added) else: items = items.items() @@ -392,7 +399,7 @@ def _do_item_removed(self, items): """ # Sorting the iterable_item_removed in reverse order based on the paths. # So that we delete a bigger index before a smaller index - for path, expected_old_value in sorted(items.items(), key=lambda x: x[0], reverse=True): + for path, expected_old_value in sorted(items.items(), key=self._sort_key_for_item_added, reverse=True): elem_and_details = self._get_elements_and_details(path) if elem_and_details: elements, parent, parent_to_obj_elem, parent_to_obj_action, obj, elem, action = elem_and_details diff --git a/deepdiff/diff.py b/deepdiff/diff.py index 4480295a..f9eb54c0 100755 --- a/deepdiff/diff.py +++ b/deepdiff/diff.py @@ -13,7 +13,7 @@ from collections import defaultdict from itertools import zip_longest from ordered_set import OrderedSet -from deepdiff.helper import (strings, bytes_type, numbers, times, ListItemRemovedOrAdded, notpresent, +from deepdiff.helper import (strings, bytes_type, numbers, uuids, times, ListItemRemovedOrAdded, notpresent, IndexedHash, unprocessed, add_to_frozen_set, convert_item_or_items_into_set_else_none, get_type, convert_item_or_items_into_compiled_regexes_else_none, @@ -1148,6 +1148,11 @@ def _diff_datetimes(self, level): if level.t1 != level.t2: self._report_result('values_changed', level) + def _diff_uuids(self, level): + """Diff UUIDs""" + if level.t1.int != level.t2.int: + self._report_result('values_changed', level) + def _diff_numpy_array(self, level, parents_ids=frozenset()): """Diff numpy arrays""" if level.path() not in self._numpy_paths: @@ -1309,6 +1314,9 @@ def _diff(self, level, parents_ids=frozenset(), _original_type=None): elif isinstance(level.t1, times): self._diff_datetimes(level) + elif isinstance(level.t1, uuids): + self._diff_uuids(level) + elif isinstance(level.t1, numbers): self._diff_numbers(level) diff --git a/deepdiff/distance.py b/deepdiff/distance.py index 321ff8cf..fb572d6b 100644 --- a/deepdiff/distance.py +++ b/deepdiff/distance.py @@ -194,9 +194,9 @@ def _get_numbers_distance(num1, num2, max_=1): """ if num1 == num2: return 0 - if isinstance(num1, float): + if not isinstance(num1, float): num1 = float(num1) - if isinstance(num2, float): + if not isinstance(num2, float): num2 = float(num2) # Since we have a default cutoff of 0.3 distance when # getting the pairs of items during the ingore_order=True diff --git a/deepdiff/helper.py b/deepdiff/helper.py index 1be4b0be..14a417dd 100644 --- a/deepdiff/helper.py +++ b/deepdiff/helper.py @@ -2,6 +2,7 @@ import re import os import datetime +import uuid import logging import warnings import time @@ -126,6 +127,7 @@ def copy(self): # pragma: no cover. Only used in pypy3 and py3.5 bytes_type = bytes only_numbers = (int, float, complex, Decimal) + numpy_numbers datetimes = (datetime.datetime, datetime.date, datetime.timedelta, datetime.time) +uuids = (uuid.UUID) times = (datetime.datetime, datetime.time) numbers = only_numbers + datetimes booleans = (bool, np_bool_) diff --git a/deepdiff/path.py b/deepdiff/path.py index cbea27fc..46028451 100644 --- a/deepdiff/path.py +++ b/deepdiff/path.py @@ -1,7 +1,6 @@ import logging from ast import literal_eval - -# TODO: it needs python3.6+ since dictionaries are ordered. +from functools import lru_cache logger = logging.getLogger(__name__) @@ -33,6 +32,7 @@ def _add_to_elements(elements, elem, inside): DEFAULT_FIRST_ELEMENT = ('root', GETATTR) +@lru_cache(maxsize=1024 * 128) def _path_to_elements(path, root_element=DEFAULT_FIRST_ELEMENT): """ Given a path, it extracts the elements that form the path and their relevant most likely retrieval action. diff --git a/docs/authors.rst b/docs/authors.rst index 85ffe926..04918ad3 100644 --- a/docs/authors.rst +++ b/docs/authors.rst @@ -52,6 +52,9 @@ Authors in order of the timeline of their contributions: - Sun Ao `eggachecat`_ for adding custom operators. - Sun Ao `eggachecat`_ for adding ignore_order_func. - `SlavaSkvortsov`_ for fixing unprocessed key error. +- Håvard Thom `havardthom`_ for adding UUID support. +- Dhanvantari Tilak `Dhanvantari`_ for Bug-Fix: + ``TypeError in _get_numbers_distance() when ignore_order = True``. .. _Sep Dehpour (Seperman): http://www.zepworks.com .. _Victor Hahn Castell: http://hahncastell.de @@ -89,6 +92,8 @@ Authors in order of the timeline of their contributions: .. _Tony-Wang: https://github.com/Tony-Wang .. _eggachecat: https://github.com/eggachecat .. _SlavaSkvortsov: https://github.com/SlavaSkvortsov +.. _havardthom: https://github.com/havardthom +.. _Dhanvantari: https://github.com/Dhanvantari Thank you for contributing to DeepDiff! diff --git a/docs/changelog.rst b/docs/changelog.rst index 0d7c1e22..07bc70c7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,15 @@ Changelog DeepDiff Changelog +- v5-7-0: + + - https://github.com/seperman/deepdiff/pull/284 Bug-Fix: TypeError + in \_get_numbers_distance() when ignore_order = True by + @Dhanvantari + - https://github.com/seperman/deepdiff/pull/280 Add support for + UUIDs by @havardthom + - Major bug in delta when it comes to iterable items added or + removed is investigated by @uwefladrich and resolved by @seperman - v5-6-0: Adding custom operators, and ignore_order_func. Bugfix: verbose_level==0 should disable values_changes. Bugfix: unprocessed key error. - v5-5-0: adding iterable_compare_func for DeepDiff, adding output_format of list for path() in tree view. - v5-4-0: adding strict_checking for numbers in DeepSearch. diff --git a/docs/conf.py b/docs/conf.py index 8bd3b42e..1e711272 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -60,9 +60,9 @@ # built documents. # # The short X.Y version. -version = '5.6.0' +version = '5.7.0' # The full version, including alpha/beta/rc tags. -release = '5.6.0' +release = '5.7.0' load_dotenv(override=True) DOC_VERSION = os.environ.get('DOC_VERSION', version) diff --git a/docs/faq.rst b/docs/faq.rst index b4a6ed05..f94ac8d6 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -5,7 +5,7 @@ F.A.Q Q: DeepDiff report is not precise when ignore_order=True ------------------------------------------------------ +-------------------------------------------------------- >>> from deepdiff import DeepDiff >>> from pprint import pprint @@ -58,7 +58,7 @@ Bump up these 2 parameters to 1 and you get what you want: Q: TypeError: Object of type type is not JSON serializable -------------------------------------------------------- +---------------------------------------------------------- I'm trying to serialize the DeepDiff results into json and I'm getting the TypeError. diff --git a/docs/index.rst b/docs/index.rst index a3db8c5a..7ccbcf25 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. -DeepDiff 5.6.0 documentation! +DeepDiff 5.7.0 documentation! ============================= ***************** @@ -27,18 +27,17 @@ The DeepDiff library includes the following modules: - **Commandline** Most of the above functionality is also available via the commandline module :doc:`/commandline` -************************* -Supported Python Versions -************************* - -DeepDiff is rigorously tested against Python 3.6 up to 3.10 and Pypy3 - -NOTE: Python 2 is not supported any more. DeepDiff v3.3.0 was the last version to supprt Python 2. - *********** What is New *********** +New In DeepDiff 5-7-0 +--------------------- + +- https://github.com/seperman/deepdiff/pull/284 Bug-Fix: TypeError in _get_numbers_distance() when ignore_order = True by @Dhanvantari +- https://github.com/seperman/deepdiff/pull/280 Add support for UUIDs by @havardthom +- Major bug in delta when it comes to iterable items added or removed is investigated by @uwefladrich and resolved by @seperman + New In DeepDiff 5-6-0 --------------------- diff --git a/setup.cfg b/setup.cfg index 51f9bf75..f47dccdf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 5.6.0 +current_version = 5.7.0 commit = True tag = True tag_name = {new_version} diff --git a/setup.py b/setup.py index e12e53c7..1dbace39 100755 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ if os.environ.get('USER', '') == 'vagrant': del os.link -version = '5.6.0' +version = '5.7.0' def get_reqs(filename): diff --git a/tests/test_delta.py b/tests/test_delta.py index 411cd91a..5e624069 100644 --- a/tests/test_delta.py +++ b/tests/test_delta.py @@ -197,7 +197,8 @@ def test_list_difference_add_delta_when_index_not_valid(self, mock_logger): delta = Delta(diff, log_errors=False) assert delta + t1 == t1 - expected_msg = ELEM_NOT_FOUND_TO_ADD_MSG.format(20, 'root[20]') + # since we sort the keys by the path elements, root[3] is gonna be processed before root[20] + expected_msg = ELEM_NOT_FOUND_TO_ADD_MSG.format(3, 'root[3]') delta2 = Delta(diff, verify_symmetry=True, raise_errors=True, log_errors=False) with pytest.raises(ValueError) as excinfo: @@ -588,6 +589,20 @@ def test_delta_dict_items_added_retain_order(self): 'to_delta_kwargs': {}, 'expected_delta_dict': {'values_changed': {"root[4]['b']": {'new_value': 'world!\nGoodbye!\n1\n2\nEnd'}}} }, + 'delta_case17_numbers_and_letters': { + 't1': [0, 1, 2, 3, 4, 5, 6, 7, 8], + 't2': [0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c'], + 'deepdiff_kwargs': {}, + 'to_delta_kwargs': {}, + 'expected_delta_dict': {'iterable_item_added': {'root[9]': 'a', 'root[10]': 'b', 'root[11]': 'c'}} + }, + 'delta_case18_numbers_and_letters': { + 't1': [0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c'], + 't2': [0, 1, 2, 3, 4, 5, 6, 7, 8], + 'deepdiff_kwargs': {}, + 'to_delta_kwargs': {}, + 'expected_delta_dict': {'iterable_item_removed': {'root[9]': 'a', 'root[10]': 'b', 'root[11]': 'c'}} + }, } diff --git a/tests/test_diff_text.py b/tests/test_diff_text.py index 5b3f9e43..fb8c8e0f 100755 --- a/tests/test_diff_text.py +++ b/tests/test_diff_text.py @@ -2,6 +2,7 @@ import datetime import pytest import logging +import uuid from decimal import Decimal from deepdiff import DeepDiff from deepdiff.helper import pypy3 @@ -157,6 +158,74 @@ def test_diffs_times(self): assert DeepDiff(t1, t2) == expected_result assert DeepDiff(t1, t3) == {} + def test_diffs_uuid1(self): + t1 = uuid.uuid1() + t2 = uuid.uuid1() + ddiff = DeepDiff(t1, t2) + result = { + 'values_changed': { + 'root': { + 'new_value': t2, + 'old_value': t1 + } + } + } + assert result == ddiff + ddiff = DeepDiff(t1, t1) + result = {} + assert result == ddiff + + def test_diffs_uuid3(self): + t1 = uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') + t2 = uuid.uuid3(uuid.NAMESPACE_DNS, 'stackoverflow.com') + ddiff = DeepDiff(t1, t2) + result = { + 'values_changed': { + 'root': { + 'new_value': t2, + 'old_value': t1 + } + } + } + assert result == ddiff + ddiff = DeepDiff(t1, t1) + result = {} + assert result == ddiff + + def test_diffs_uuid4(self): + t1 = uuid.uuid4() + t2 = uuid.uuid4() + ddiff = DeepDiff(t1, t2) + result = { + 'values_changed': { + 'root': { + 'new_value': t2, + 'old_value': t1 + } + } + } + assert result == ddiff + ddiff = DeepDiff(t1, t1) + result = {} + assert result == ddiff + + def test_diffs_uuid5(self): + t1 = uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') + t2 = uuid.uuid5(uuid.NAMESPACE_DNS, 'stackoverflow.com') + ddiff = DeepDiff(t1, t2) + result = { + 'values_changed': { + 'root': { + 'new_value': t2, + 'old_value': t1 + } + } + } + assert result == ddiff + ddiff = DeepDiff(t1, t1) + result = {} + assert result == ddiff + def test_string_difference(self): t1 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": "world"}} t2 = {1: 1, 2: 4, 3: 3, 4: {"a": "hello", "b": "world!"}} diff --git a/tests/test_distance.py b/tests/test_distance.py index 62d7dc5b..d074484b 100644 --- a/tests/test_distance.py +++ b/tests/test_distance.py @@ -187,7 +187,7 @@ def test_get_item_length_custom_class2_loop(self): @pytest.mark.parametrize('num1, num2, max_, expected', [ (10.0, 10, 1, 0), - (Decimal('10.1'), Decimal('10.2'), 1, Decimal('0.004926108374384236453201970443')), + (Decimal('10.1'), Decimal('10.2'), 1, 0.004926108374384236453201970443), (Decimal(10), Decimal(-10), 1, 1), (2, 3, 1, 0.2), (10, -10, .1, .1),