From ce89789573f40f1d21949c1d82d84e745bb7d5a3 Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Wed, 15 Jan 2020 12:46:55 -0800 Subject: [PATCH 01/15] removing .json property. Json pickle library is now optional. --- README.md | 1 + deepdiff/diff.py | 44 +++++++++++++------------------------ deepdiff/diff_doc.rst | 2 +- requirements-dev.txt | 11 +++++----- requirements.txt | 1 - setup.py | 1 + tests/test_diff_text.py | 8 +++++++ tests/test_serialization.py | 14 ++---------- 8 files changed, 34 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index cef8b4f1..ed6b2267 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,7 @@ And then running # ChangeLog +- v4-1-0: .json property is finally removed. - v4-0-9: Fixing the bug for hashing custom unhashable objects - v4-0-8: Adding ignore_nan_inequality for float('nan') - v4-0-7: Hashing of the number 1 vs. True diff --git a/deepdiff/diff.py b/deepdiff/diff.py index f4973722..55b2cd92 100755 --- a/deepdiff/diff.py +++ b/deepdiff/diff.py @@ -10,7 +10,6 @@ import difflib import logging import json -import jsonpickle import warnings from itertools import zip_longest @@ -33,6 +32,12 @@ logger = logging.getLogger(__name__) warnings.simplefilter('once', DeprecationWarning) +try: + import jsonpickle +except ImportError: + jsonpickle = None + logger.info('jsonpickle is not installed. ') + TREE_VIEW = 'tree' TEXT_VIEW = 'text' @@ -642,37 +647,15 @@ def __diff(self, level, parents_ids=frozenset({})): else: self.__diff_obj(level, parents_ids) - @property - def json(self): - warnings.warn( - "json property will be deprecated. Instead use: to_json_pickle() to get the json pickle or to_json() for bare-bone json.", - DeprecationWarning - ) - if not hasattr(self, '_json'): - # copy of self removes all the extra attributes since it assumes - # we have only a simple dictionary. - copied = self.copy() - self._json = jsonpickle.encode(copied) - return self._json - def to_json_pickle(self): """ Get the json pickle of the diff object. Unless you need all the attributes and functionality of DeepDiff, running to_json() is the safer option that json pickle. """ - copied = self.copy() - return jsonpickle.encode(copied) - - @json.deleter - def json(self): - del self._json - - @classmethod - def from_json(cls, value): - warnings.warn( - "from_json is renamed to from_json_pickle", - DeprecationWarning - ) - return cls.from_json_pickle(value) + if jsonpickle: + copied = self.copy() + return jsonpickle.encode(copied) + else: + logger.error('jsonpickle library needs to be installed in order to run to_json_pickle') @classmethod def from_json_pickle(cls, value): @@ -680,7 +663,10 @@ def from_json_pickle(cls, value): Load DeepDiff object with all the bells and whistles from the json pickle dump. Note that json pickle dump comes from to_json_pickle """ - return jsonpickle.decode(value) + if jsonpickle: + return jsonpickle.decode(value) + else: + logger.error('jsonpickle library needs to be installed in order to run from_json_pickle') def to_json(self, default_mapping=None): """ diff --git a/deepdiff/diff_doc.rst b/deepdiff/diff_doc.rst index 4f093099..e1715714 100644 --- a/deepdiff/diff_doc.rst +++ b/deepdiff/diff_doc.rst @@ -375,7 +375,7 @@ ignore_type_in_groups 1. Set ignore_string_type_changes=True. 2. Or set ignore_type_in_groups=[(str, bytes)]. Here you are saying if we detect one type to be str and the other one bytes, do not report them as type change. It is exactly as passing ignore_type_in_groups=[DeepDiff.strings] or ignore_type_in_groups=DeepDiff.strings . - Now what if you want also typeA and typeB to be ignored when comparing agains each other? + Now what if you want also typeA and typeB to be ignored when comparing against each other? 1. ignore_type_in_groups=[DeepDiff.strings, (typeA, typeB)] 2. or ignore_type_in_groups=[(str, bytes), (typeA, typeB)] diff --git a/requirements-dev.txt b/requirements-dev.txt index d7a60688..55db4c96 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,8 @@ -r requirements.txt -pytest==4.3.1 -pytest-cov==2.6.1 -numpy==1.16.2 +bump2version==0.5.11 +jsonpickle==1.2 +ipdb==0.12.3 mmh3==2.5.1 -ipdb==0.11 -bump2version==0.5.10 +numpy==1.18.1 +pytest==5.3.2 +pytest-cov==2.8.1 diff --git a/requirements.txt b/requirements.txt index b075ccd0..38032517 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ -jsonpickle==1.0 ordered-set==3.1.1 diff --git a/setup.py b/setup.py index ca7dea52..9481c9aa 100755 --- a/setup.py +++ b/setup.py @@ -54,6 +54,7 @@ def get_reqs(filename): "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: PyPy", "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License" diff --git a/tests/test_diff_text.py b/tests/test_diff_text.py index 5b62eb8b..57063628 100755 --- a/tests/test_diff_text.py +++ b/tests/test_diff_text.py @@ -1483,6 +1483,14 @@ def test_ignore_type_in_groups_none_and_objects(self): result = {'values_changed': {'root[2]': {'new_value': 3.3, 'old_value': 3}}} assert result == ddiff + def test_ignore_type_in_groups_str_and_datetime(self): + now = datetime.datetime.utcnow() + t1 = [1, 2, 3, 'a', now] + t2 = [1, 2, 3, 'a', 'now'] + ddiff = DeepDiff(t1, t2, ignore_type_in_groups=[(str, bytes, datetime.datetime)]) + result = {'values_changed': {'root[4]': {'new_value': 'now', 'old_value': now}}} + assert result == ddiff + def test_ignore_string_type_changes_when_dict_keys_merge_is_not_deterministic(self): t1 = {'a': 10, b'a': 20} t2 = {'a': 11, b'a': 22} diff --git a/tests/test_serialization.py b/tests/test_serialization.py index d8236ebb..65a54cd5 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -23,7 +23,7 @@ def test_serialization_text(self): def test_deserialization(self): ddiff = DeepDiff(t1, t2) jsoned = ddiff.to_json_pickle() - ddiff2 = DeepDiff.from_json(jsoned) + ddiff2 = DeepDiff.from_json_pickle(jsoned) assert ddiff == ddiff2 def test_serialization_tree(self): @@ -36,19 +36,9 @@ def test_serialization_tree(self): def test_deserialization_tree(self): ddiff = DeepDiff(t1, t2, view='tree') jsoned = ddiff.to_json_pickle() - ddiff2 = DeepDiff.from_json(jsoned) + ddiff2 = DeepDiff.from_json_pickle(jsoned) assert 'type_changes' in ddiff2 - def test_deleting_serialization_cache_when_using_the_property(self): - t1 = {1: 1} - t2 = {1: 2} - ddiff = DeepDiff(t1, t2) - assert hasattr(ddiff, '_json') is False - ddiff.json - assert hasattr(ddiff, '_json') - del ddiff.json - assert hasattr(ddiff, '_json') is False - def test_serialize_custom_objects_throws_error(self): class A: pass From 696e7506e5baadb122a39afc6b15589108673cb3 Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Wed, 15 Jan 2020 12:49:57 -0800 Subject: [PATCH 02/15] Making the error message more explicit. --- deepdiff/diff.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deepdiff/diff.py b/deepdiff/diff.py index 55b2cd92..c0d0dee6 100755 --- a/deepdiff/diff.py +++ b/deepdiff/diff.py @@ -36,7 +36,8 @@ import jsonpickle except ImportError: jsonpickle = None - logger.info('jsonpickle is not installed. ') + logger.info('jsonpickle is not installed. The to_json_pickle and from_json_pickle functions will not work.' + 'If you dont need those functions, there is nothing to do.') TREE_VIEW = 'tree' TEXT_VIEW = 'text' From 68177e896299403642de1d47295efb2a0494a69a Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 17 Jan 2020 12:15:35 +0200 Subject: [PATCH 03/15] Fix for Python 3.10 and 10: don't compare sys.version string --- deepdiff/helper.py | 9 ++++----- setup.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/deepdiff/helper.py b/deepdiff/helper.py index 26e64cde..e3669bba 100644 --- a/deepdiff/helper.py +++ b/deepdiff/helper.py @@ -10,12 +10,11 @@ logger = logging.getLogger(__name__) -py_major_version = sys.version[0] -py_minor_version = sys.version[2] +py_major_version = sys.version_info.major -py2 = py_major_version == '2' -py3 = py_major_version == '3' -py4 = py_major_version == '4' +py2 = py_major_version == 2 +py3 = py_major_version == 3 +py4 = py_major_version == 4 if py4: logger.warning('Python 4 is not supported yet. Switching logic to Python 3.') # pragma: no cover diff --git a/setup.py b/setup.py index ca7dea52..ed57a897 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ import sys from setuptools import setup -if sys.version[0] == '2': # pragma: no cover +if sys.version_info.major[0] == 2: # pragma: no cover sys.exit('Python 2 is not supported anymore. The last version of DeepDiff that supported Py2 was 3.3.0') # if you are not using vagrant, just delete os.link directly, From 280af77d7dd4483ec3cb863c7d259f3af7cd66b8 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 17 Jan 2020 12:25:03 +0200 Subject: [PATCH 04/15] Drop support for EOL Python 3.4 --- .travis.yml | 3 --- README.md | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c8cae48..767e787f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,11 @@ language: python matrix: include: - - python: 3.4 - python: 3.5 - python: 3.6 - python: pypy3 - python: 3.7 - python: 3.8 - dist: xenial - sudo: true install: - pip install -r requirements-dev.txt diff --git a/README.md b/README.md index cef8b4f1..0efcee58 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - DeepSearch: Search for objects within other objects. - DeepHash: Hash any object based on their content. -Tested on Python 3.4, 3.5, 3.6, 3.7, Pypy3 +Tested on Python 3.5+ and PyPy3. **NOTE: Python 2 is not supported any more. DeepDiff v3.3.0 was the last version to support Python 2** From 360115d68f3823f3ec2ab105d4ac140e0f912400 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 17 Jan 2020 12:27:31 +0200 Subject: [PATCH 05/15] Upgrade Python syntax with pyupgrade --py3-plus --- deepdiff/deephash.py | 3 +-- deepdiff/diff.py | 1 - deepdiff/helper.py | 3 +-- deepdiff/model.py | 12 +++++------- deepdiff/search.py | 3 +-- docs/conf.py | 1 - tests/__init__.py | 5 +---- tests/test_diff_text.py | 39 ++++++++++++++++++------------------- tests/test_diff_tree.py | 1 - tests/test_hash.py | 3 +-- tests/test_helper.py | 1 - tests/test_model.py | 1 - tests/test_search.py | 7 +++---- tests/test_serialization.py | 1 - 14 files changed, 32 insertions(+), 49 deletions(-) diff --git a/deepdiff/deephash.py b/deepdiff/deephash.py index cd4c51d2..3cf12b53 100644 --- a/deepdiff/deephash.py +++ b/deepdiff/deephash.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import logging from collections.abc import Iterable, MutableMapping from collections import defaultdict @@ -232,7 +231,7 @@ def _prep_dict(self, obj, parent, parents_ids=EMPTY_FROZENSET, print_as_attribut break else: type_str = 'dict' - return "%s:{%s}" % (type_str, result) + return "{}:{{{}}}".format(type_str, result) def _prep_iterable(self, obj, parent, parents_ids=EMPTY_FROZENSET): diff --git a/deepdiff/diff.py b/deepdiff/diff.py index f4973722..ef86a919 100755 --- a/deepdiff/diff.py +++ b/deepdiff/diff.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # In order to run the docstrings: # python3 -m deepdiff.diff diff --git a/deepdiff/helper.py b/deepdiff/helper.py index 26e64cde..9ac975e3 100644 --- a/deepdiff/helper.py +++ b/deepdiff/helper.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys import datetime import re @@ -166,7 +165,7 @@ def add_to_frozen_set(parents_ids, item_id): def convert_item_or_items_into_set_else_none(items): if items: if isinstance(items, strings): - items = set([items]) + items = {items} else: items = set(items) else: diff --git a/deepdiff/model.py b/deepdiff/model.py index 7ad06dec..cc375ed0 100644 --- a/deepdiff/model.py +++ b/deepdiff/model.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from deepdiff.helper import RemapDict, strings, short_repr, Verbose, notpresent from ast import literal_eval from copy import copy @@ -156,7 +154,7 @@ def _from_tree_value_changed(self, tree): def _from_tree_unprocessed(self, tree): if 'unprocessed' in tree: for change in tree['unprocessed']: - self['unprocessed'].append("%s: %s and %s" % (change.path( + self['unprocessed'].append("{}: {} and {}".format(change.path( force=FORCE_DEFAULT), change.t1, change.t2)) def _from_tree_set_item_removed(self, tree): @@ -167,7 +165,7 @@ def _from_tree_set_item_removed(self, tree): item = change.t1 if isinstance(item, strings): item = "'%s'" % item - self['set_item_removed'].add("%s[%s]" % (path, str(item))) + self['set_item_removed'].add("{}[{}]".format(path, str(item))) # this syntax is rather peculiar, but it's DeepDiff 2.x compatible def _from_tree_set_item_added(self, tree): @@ -178,7 +176,7 @@ def _from_tree_set_item_added(self, tree): item = change.t2 if isinstance(item, strings): item = "'%s'" % item - self['set_item_added'].add("%s[%s]" % (path, str(item))) + self['set_item_added'].add("{}[{}]".format(path, str(item))) # this syntax is rather peculiar, but it's DeepDiff 2.x compatible) def _from_tree_repetition_change(self, tree): @@ -190,7 +188,7 @@ def _from_tree_repetition_change(self, tree): self['repetition_change'][path]['value'] = change.t1 -class DiffLevel(object): +class DiffLevel: """ An object of this class represents a single object-tree-level in a reported change. A double-linked list of these object describes a single change on all of its levels. @@ -523,7 +521,7 @@ def copy(self): return result -class ChildRelationship(object): +class ChildRelationship: """ Describes the relationship between a container object (the "parent") and the contained "child" object. diff --git a/deepdiff/search.py b/deepdiff/search.py index e6d01917..bb0a7003 100644 --- a/deepdiff/search.py +++ b/deepdiff/search.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import re from collections.abc import MutableMapping, Iterable import logging @@ -225,7 +224,7 @@ def __search_iterable(self, """Search iterables except dictionaries, sets and strings.""" for i, thing in enumerate(obj): - new_parent = "%s[%s]" % (parent, i) + new_parent = "{}[{}]".format(parent, i) if self.__skip_this(thing, parent=new_parent): continue diff --git a/docs/conf.py b/docs/conf.py index 55c69fb1..ec55bde6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # DeepDiff documentation build configuration file, created by # sphinx-quickstart on Mon Jul 20 06:06:44 2015. diff --git a/tests/__init__.py b/tests/__init__.py index de57a9f7..0f5a7e77 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- - - -class CustomClass(object): +class CustomClass: def __init__(self, a, b=None): self.a = a self.b = b diff --git a/tests/test_diff_text.py b/tests/test_diff_text.py index 5b62eb8b..b2358819 100755 --- a/tests/test_diff_text.py +++ b/tests/test_diff_text.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import datetime import pytest import logging @@ -271,19 +270,19 @@ def test_unicode(self): 2: 2, 3: 3, 4: { - "a": u"hello", - "b": u"world!\nGoodbye!\n1\n2\nEnd" + "a": "hello", + "b": "world!\nGoodbye!\n1\n2\nEnd" } } - t2 = {1: 1, 2: 2, 3: 3, 4: {"a": u"hello", "b": u"world\n1\n2\nEnd"}} + t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": "world\n1\n2\nEnd"}} ddiff = DeepDiff(t1, t2) result = { 'values_changed': { "root[4]['b']": { 'diff': '--- \n+++ \n@@ -1,5 +1,4 @@\n-world!\n-Goodbye!\n+world\n 1\n 2\n End', - 'new_value': u'world\n1\n2\nEnd', - 'old_value': u'world!\nGoodbye!\n1\n2\nEnd' + 'new_value': 'world\n1\n2\nEnd', + 'old_value': 'world!\nGoodbye!\n1\n2\nEnd' } } } @@ -597,7 +596,7 @@ def test_set_of_none(self): """ https://github.com/seperman/deepdiff/issues/64 """ - ddiff = DeepDiff(set(), set([None])) + ddiff = DeepDiff(set(), {None}) assert {'set_item_added': {'root[None]'}} == ddiff def test_list_that_contains_dictionary(self): @@ -971,7 +970,7 @@ def __init__(self, x, y): assert result == ddiff def test_custom_objects_with_single_protected_slot(self): - class ClassA(object): + class ClassA: __slots__ = '__a' def __init__(self): @@ -986,7 +985,7 @@ def __str__(self): assert {} == ddiff def test_custom_objects_with_weakref_in_slots(self): - class ClassA(object): + class ClassA: __slots__ = ['a', '__weakref__'] def __init__(self, a): @@ -1006,7 +1005,7 @@ def __init__(self, a): assert result == diff def get_custom_objects_add_and_remove(self): - class ClassA(object): + class ClassA: a = 1 def __init__(self, b): @@ -1054,7 +1053,7 @@ def test_custom_objects_add_and_remove_verbose(self): assert result == ddiff def get_custom_object_with_added_removed_methods(self): - class ClassA(object): + class ClassA: def method_a(self): pass @@ -1128,7 +1127,7 @@ def __init__(self): assert not DeepDiff(burritos, tacos, ignore_type_in_groups=[(Taco, Burrito)], ignore_order=True) def test_loop(self): - class LoopTest(object): + class LoopTest: def __init__(self, a): self.loop = self self.a = a @@ -1148,12 +1147,12 @@ def __init__(self, a): assert result == ddiff def test_loop2(self): - class LoopTestA(object): + class LoopTestA: def __init__(self, a): self.loop = LoopTestB self.a = a - class LoopTestB(object): + class LoopTestB: def __init__(self, a): self.loop = LoopTestA self.a = a @@ -1173,7 +1172,7 @@ def __init__(self, a): assert result == ddiff def test_loop3(self): - class LoopTest(object): + class LoopTest: def __init__(self, a): self.loop = LoopTest self.a = a @@ -1254,15 +1253,15 @@ def test_unicode_string_type_changes(self): Writing b"你好" throws an exception in Python 3 so can't be used for testing. These tests are currently useless till they are rewritten properly. """ - unicode_string = {"hello": u"你好"} + unicode_string = {"hello": "你好"} ascii_string = {"hello": "你好"} ddiff = DeepDiff(unicode_string, ascii_string) result = {} assert result == ddiff def test_unicode_string_value_changes(self): - unicode_string = {"hello": u"你好"} - ascii_string = {"hello": u"你好hello"} + unicode_string = {"hello": "你好"} + ascii_string = {"hello": "你好hello"} ddiff = DeepDiff(unicode_string, ascii_string) result = { 'values_changed': { @@ -1275,7 +1274,7 @@ def test_unicode_string_value_changes(self): assert result == ddiff def test_unicode_string_value_and_type_changes(self): - unicode_string = {"hello": u"你好"} + unicode_string = {"hello": "你好"} ascii_string = {"hello": "你好hello"} ddiff = DeepDiff(unicode_string, ascii_string) # In python3, all string is unicode, so these 2 strings only diff @@ -1310,7 +1309,7 @@ def test_int_to_unicode_string(self): def test_int_to_unicode(self): t1 = 1 - unicode_string = u"你好" + unicode_string = "你好" ddiff = DeepDiff(t1, unicode_string) # In python3, all string is unicode, so these 2 strings only diff # in values diff --git a/tests/test_diff_tree.py b/tests/test_diff_tree.py index fa9f299a..44abd648 100644 --- a/tests/test_diff_tree.py +++ b/tests/test_diff_tree.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import pytest from deepdiff import DeepDiff from deepdiff.helper import pypy3, notpresent diff --git a/tests/test_hash.py b/tests/test_hash.py index 7c3a3c6d..93601f54 100755 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import re import pytest import logging @@ -235,7 +234,7 @@ def test_dict_hash(self): 20: 'int:20', key1: key1_prepped, string1: string1_prepped, - get_id(obj): 'dict:{int:1:int:10;int:2:int:20;%s:%s}' % (key1, string1) + get_id(obj): 'dict:{{int:1:int:10;int:2:int:20;{}:{}}}'.format(key1, string1) } result = DeepHashPrep(obj, ignore_string_type_changes=True) assert expected_result == result diff --git a/tests/test_helper.py b/tests/test_helper.py index 78b0fb85..9f9b1386 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import pytest from decimal import Decimal from deepdiff.helper import short_repr, number_to_string diff --git a/tests/test_model.py b/tests/test_model.py index 59b7c6a6..9d079cda 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import logging import pytest from tests import CustomClass, CustomClassMisleadingRepr diff --git a/tests/test_search.py b/tests/test_search.py index 5e6f237b..d209bbd2 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import pytest from deepdiff import DeepSearch, grep from datetime import datetime @@ -177,7 +176,7 @@ def test_string_in_set_verbose(self): assert list(ds["matched_values"].values())[0] == item def test_loop(self): - class LoopTest(object): + class LoopTest: def __init__(self, a): self.loop = self self.a = a @@ -245,7 +244,7 @@ def test_unknown_parameters(self): DeepSearch(1, 1, wrong_param=2) def test_bad_attribute(self): - class Bad(object): + class Bad: __slots__ = ['x', 'y'] def __getattr__(self, key): @@ -314,7 +313,7 @@ def __eq__(self, other): assert DeepSearch(obj, item, verbose_level=1) == result def test_search_inherited_attributes(self): - class Parent(object): + class Parent: a = 1 class Child(Parent): diff --git a/tests/test_serialization.py b/tests/test_serialization.py index d8236ebb..3840a29b 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import json import pytest from deepdiff import DeepDiff From d9664beb1fd57fceefd14cf157f8b65edd26497b Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 17 Jan 2020 12:31:09 +0200 Subject: [PATCH 06/15] Cache pip --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 767e787f..adaa8265 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +cache: pip matrix: include: From bd1f770a77538dfde0ce836f6dc8af01ee4db325 Mon Sep 17 00:00:00 2001 From: Andrey Gavrilin Date: Mon, 27 Jan 2020 14:09:05 +0300 Subject: [PATCH 07/15] hashing classes --- deepdiff/deephash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepdiff/deephash.py b/deepdiff/deephash.py index cd4c51d2..09ca17c9 100644 --- a/deepdiff/deephash.py +++ b/deepdiff/deephash.py @@ -172,7 +172,7 @@ def __contains__(self, obj): def _prep_obj(self, obj, parent, parents_ids=EMPTY_FROZENSET, is_namedtuple=False): """Difference of 2 objects""" - original_type = type(obj) + original_type = type(obj) if not isinstance(obj, type) else obj try: if is_namedtuple: obj = obj._asdict() From 13903eeb69e199ff944b00ee11acd07eeb1b3b8a Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Wed, 29 Jan 2020 13:11:52 -0800 Subject: [PATCH 08/15] fixing hashing boolean in list --- deepdiff/diff.py | 10 ++++++++-- tests/test_diff_text.py | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/deepdiff/diff.py b/deepdiff/diff.py index c0d0dee6..23eb6b60 100755 --- a/deepdiff/diff.py +++ b/deepdiff/diff.py @@ -26,7 +26,7 @@ from deepdiff.model import RemapDict, ResultDict, TextResult, TreeResult, DiffLevel from deepdiff.model import DictRelationship, AttributeRelationship from deepdiff.model import SubscriptableIterableRelationship, NonSubscriptableIterableRelationship, SetRelationship -from deepdiff.deephash import DeepHash +from deepdiff.deephash import DeepHash, BoolObj from deepdiff.base import Base logger = logging.getLogger(__name__) @@ -482,7 +482,13 @@ def __create_hashtable(self, t, level): ignore_string_case=self.ignore_string_case, number_to_string_func=self.number_to_string, ) - item_hash = hashes_all[item] + # import pytest; pytest.set_trace() + key = item + if item is True: + key = BoolObj.TRUE + elif item is False: + key = BoolObj.FALSE + item_hash = hashes_all[key] except Exception as e: # pragma: no cover logger.error("Can not produce a hash for %s." "Not counting this object.\n %s" % diff --git a/tests/test_diff_text.py b/tests/test_diff_text.py index 57063628..a843817e 100755 --- a/tests/test_diff_text.py +++ b/tests/test_diff_text.py @@ -404,6 +404,18 @@ def test_list_difference_ignore_order(self): ddiff = DeepDiff(t1, t2, ignore_order=True) assert {} == ddiff + @pytest.mark.parametrize('t1_0, t2_0', [ + (1, 2), + (True, False), + ('a', 'b'), + ]) + def test_list_difference_of_bool_only_ignore_order(self, t1_0, t2_0): + t1 = [t1_0] + t2 = [t2_0] + ddiff = DeepDiff(t1, t2, ignore_order=True) + result = {'iterable_item_added': {'root[0]': t2_0}, 'iterable_item_removed': {'root[0]': t1_0}} + assert result == ddiff + def test_dictionary_difference_ignore_order(self): t1 = {"a": [[{"b": 2, "c": 4}, {"b": 2, "c": 3}]]} t2 = {"a": [[{"b": 2, "c": 3}, {"b": 2, "c": 4}]]} From 95c9f8335322f0178d838ec7d2bda1a4b1d065ae Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Thu, 30 Jan 2020 12:59:31 -0800 Subject: [PATCH 09/15] adding test case for hashing class. Ignore private keys when calculating hash. --- AUTHORS | 7 +++++-- README.md | 1 + deepdiff/deephash.py | 5 ++++- docs/index.rst | 2 ++ tests/test_hash.py | 15 ++++++++++++--- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index cc85a583..ff998b9d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,10 @@ -Authors: +Many thanks to the following people for their contributions to DeepDiff: + - Seperman - Victor Hahn Castell @ Flexoptix - nfvs for Travis-CI setup script. - brbsix for initial Py3 porting. -- WangFenjin for unicode support. +- WangFenjin for Unicode support. - timoilya for comparing list of sets when ignoring order. - Bernhard10 for significant digits comparison. - b-jazz for PEP257 cleanup, Standardize on full names, fixing line endings. @@ -21,3 +22,5 @@ Authors: - Juan Soler (Soleronline) for adding ignore_type_number - mthaddon for adding timedelta diffing support - Necrophagos for Hashing of the number 1 vs. True +- Hugo (hugovk) for fixes for Python 3.10 and dropping support for EOL Python 3.4 +- Andrey Gavrilin (gaal-dev) for hashing classes. diff --git a/README.md b/README.md index 19f08880..cc35c76e 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,7 @@ And then running # ChangeLog +- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. - v4-1-0: .json property is finally removed. - v4-0-9: Fixing the bug for hashing custom unhashable objects - v4-0-8: Adding ignore_nan_inequality for float('nan') diff --git a/deepdiff/deephash.py b/deepdiff/deephash.py index 6914c817..ed96e410 100644 --- a/deepdiff/deephash.py +++ b/deepdiff/deephash.py @@ -170,7 +170,7 @@ def __contains__(self, obj): return super().__contains__(key) def _prep_obj(self, obj, parent, parents_ids=EMPTY_FROZENSET, is_namedtuple=False): - """Difference of 2 objects""" + """prepping objects""" original_type = type(obj) if not isinstance(obj, type) else obj try: if is_namedtuple: @@ -208,6 +208,9 @@ def _prep_dict(self, obj, parent, parents_ids=EMPTY_FROZENSET, print_as_attribut key_text = "%s{}".format(INDEX_VS_ATTRIBUTE[print_as_attribute]) for key, item in obj.items(): + # ignore private variables + if isinstance(key, str) and key.startswith('__'): + continue key_formatted = "'%s'" % key if not print_as_attribute and isinstance(key, strings) else key key_in_report = key_text % (parent, key_formatted) diff --git a/docs/index.rst b/docs/index.rst index 3c1b6b8f..a239c737 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -281,6 +281,8 @@ Indices and tables Changelog ========= +- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. +- v4-1-0: .json property is finally removed. - v4-0-9: Fixing the bug for hashing custom unhashable objects - v4-0-8: Adding ignore_nan_inequality for float('nan') - v4-0-7: Hashing of the number 1 vs. True diff --git a/tests/test_hash.py b/tests/test_hash.py index 93601f54..9411ef09 100755 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -12,7 +12,9 @@ logging.disable(logging.CRITICAL) -class CustomClass: +class ClassC: + class_attr = 0 + def __init__(self, a, b=None): self.a = a self.b = b @@ -20,8 +22,7 @@ def __init__(self, a, b=None): def __str__(self): return "({}, {})".format(self.a, self.b) - def __repr__(self): - return self.__str__() + __repr__ = __str__ # Only the prep part of DeepHash. We don't need to test the actual hash function. @@ -537,6 +538,14 @@ def test_string_case(self): t1_hash = DeepHashPrep(t1, ignore_string_case=True) assert t1_hash == {'Hello': 'str:hello'} + def test_hash_class(self): + t1 = ClassC + t1_hash = DeepHashPrep(t1) + assert t1_hash['class_attr'] == 'str:class_attr' + assert t1_hash[0] == 'int:0' + # Note: we ignore private names in calculating hashes now. So you dont see __init__ here for example. + assert t1_hash[t1] == r'objClassC:{str:class_attr:int:0}' + class TestDeepHashSHA: """DeepHash with SHA Tests.""" From 8d20022196f5cd4d90a2a602f16242228285e20e Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Thu, 30 Jan 2020 13:15:26 -0800 Subject: [PATCH 10/15] fixing pypy3 fork of hashing test --- tests/test_hash.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/test_hash.py b/tests/test_hash.py index 9411ef09..6083e9d9 100755 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -207,20 +207,11 @@ class MyEnum(Enum): A = 1 B = 2 - # checking if pypy3 is running the test - # in that case due to a difference of string interning implementation - # the ids of strings change - if pypy3: - # only compare the hashes for the enum instances themselves - assert DeepHashPrep(MyEnum.A)[MyEnum.A] == r'objMyEnum:{str:__objclass__:EnumMeta:objMyEnum:{str:_name_:str:B;str:_value_:int:2};str:_name_:str:A;str:_value_:int:1}' - assert DeepHashPrep(MyEnum.B)[MyEnum.B] == r'objMyEnum:{str:__objclass__:EnumMeta:objMyEnum:{str:_name_:str:A;str:_value_:int:1};str:_name_:str:B;str:_value_:int:2}' - assert DeepHashPrep(MyEnum(1))[MyEnum.A] == r'objMyEnum:{str:__objclass__:EnumMeta:objMyEnum:{str:_name_:str:B;str:_value_:int:2};str:_name_:str:A;str:_value_:int:1}' - else: - assert DeepHashPrep(MyEnum.A) == DeepHashPrep(MyEnum.A) - assert DeepHashPrep(MyEnum.A) == DeepHashPrep(MyEnum(1)) - assert DeepHashPrep(MyEnum.A) != DeepHashPrep(MyEnum.A.name) - assert DeepHashPrep(MyEnum.A) != DeepHashPrep(MyEnum.A.value) - assert DeepHashPrep(MyEnum.A) != DeepHashPrep(MyEnum.B) + assert DeepHashPrep(MyEnum.A)[MyEnum.A] == r'objMyEnum:{str:_name_:str:A;str:_value_:int:1}' + assert DeepHashPrep(MyEnum.A) == DeepHashPrep(MyEnum(1)) + assert DeepHashPrep(MyEnum.A) != DeepHashPrep(MyEnum.A.name) + assert DeepHashPrep(MyEnum.A) != DeepHashPrep(MyEnum.A.value) + assert DeepHashPrep(MyEnum.A) != DeepHashPrep(MyEnum.B) def test_dict_hash(self): string1 = "a" From 0ff1aca9aa813c6cbef707cee8e70339baa8167b Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Thu, 30 Jan 2020 13:20:00 -0800 Subject: [PATCH 11/15] updating changelog --- README.md | 2 +- docs/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cc35c76e..6ef19837 100644 --- a/README.md +++ b/README.md @@ -417,7 +417,7 @@ And then running # ChangeLog -- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. +- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. Fix for #166 Problem with comparing lists, with an boolean as element. - v4-1-0: .json property is finally removed. - v4-0-9: Fixing the bug for hashing custom unhashable objects - v4-0-8: Adding ignore_nan_inequality for float('nan') diff --git a/docs/index.rst b/docs/index.rst index a239c737..6996ac5b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -281,7 +281,7 @@ Indices and tables Changelog ========= -- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. +- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. Fix for #166 Problem with comparing lists, with an boolean as element. - v4-1-0: .json property is finally removed. - v4-0-9: Fixing the bug for hashing custom unhashable objects - v4-0-8: Adding ignore_nan_inequality for float('nan') From f0051057923ecc9685bf06a5879b8d4b4d5e739f Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Thu, 30 Jan 2020 13:22:35 -0800 Subject: [PATCH 12/15] updating readme with changelog --- README.md | 3 +-- docs/index.rst | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6ef19837..bc5bdbac 100644 --- a/README.md +++ b/README.md @@ -417,8 +417,7 @@ And then running # ChangeLog -- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. Fix for #166 Problem with comparing lists, with an boolean as element. -- v4-1-0: .json property is finally removed. +- v4-2-0: .json property is finally removed. Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. Fix for #166 Problem with comparing lists, with an boolean as element. - v4-0-9: Fixing the bug for hashing custom unhashable objects - v4-0-8: Adding ignore_nan_inequality for float('nan') - v4-0-7: Hashing of the number 1 vs. True diff --git a/docs/index.rst b/docs/index.rst index 6996ac5b..0bbc9cdc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -281,7 +281,7 @@ Indices and tables Changelog ========= -- v4-2-0: Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. Fix for #166 Problem with comparing lists, with an boolean as element. +- v4-2-0: .json property is finally removed. Fix for Py3.10. Dropping support for EOL Python 3.4. Ignoring private keys when calculating hashes. For example __init__ is not a part of hash calculation anymore. Fix for #166 Problem with comparing lists, with an boolean as element. - v4-1-0: .json property is finally removed. - v4-0-9: Fixing the bug for hashing custom unhashable objects - v4-0-8: Adding ignore_nan_inequality for float('nan') From a0dca3f2f1f22141a2fa4187327c8c4ed3b41365 Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Thu, 30 Jan 2020 13:22:55 -0800 Subject: [PATCH 13/15] =?UTF-8?q?Bump=20version:=204.0.9=20=E2=86=92=204.1?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- deepdiff/__init__.py | 2 +- docs/conf.py | 4 ++-- docs/index.rst | 2 +- setup.cfg | 2 +- setup.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bc5bdbac..fe2c5b72 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# DeepDiff v 4.0.9 +# DeepDiff v 4.1.0 ![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat) diff --git a/deepdiff/__init__.py b/deepdiff/__init__.py index 24d63750..d401c1f4 100644 --- a/deepdiff/__init__.py +++ b/deepdiff/__init__.py @@ -1,6 +1,6 @@ """This module offers the DeepDiff, DeepSearch, grep and DeepHash classes.""" # flake8: noqa -__version__ = '4.0.9' +__version__ = '4.1.0' import logging if __name__ == '__main__': diff --git a/docs/conf.py b/docs/conf.py index ec55bde6..1991304e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -59,9 +59,9 @@ # built documents. # # The short X.Y version. -version = '4.0.9' +version = '4.1.0' # The full version, including alpha/beta/rc tags. -release = '4.0.9' +release = '4.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/index.rst b/docs/index.rst index 0bbc9cdc..e226a481 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. -DeepDiff 4.0.9 documentation! +DeepDiff 4.1.0 documentation! ============================= **DeepDiff: Deep Difference of dictionaries, iterables, strings and other objects. It will recursively look for all the changes.** diff --git a/setup.cfg b/setup.cfg index bf3f2e28..0838d6b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.0.9 +current_version = 4.1.0 commit = True tag = True tag_name = {new_version} diff --git a/setup.py b/setup.py index 3504de08..f1659674 100755 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ if os.environ.get('USER', '') == 'vagrant': del os.link -version = '4.0.9' +version = '4.1.0' def get_reqs(filename): From 6a9f3773716320ed7d759b73431f3e8d9829261c Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Thu, 30 Jan 2020 13:23:04 -0800 Subject: [PATCH 14/15] =?UTF-8?q?Bump=20version:=204.1.0=20=E2=86=92=204.2?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- deepdiff/__init__.py | 2 +- docs/conf.py | 4 ++-- docs/index.rst | 2 +- setup.cfg | 2 +- setup.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fe2c5b72..ceb6ae5a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# DeepDiff v 4.1.0 +# DeepDiff v 4.2.0 ![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat) diff --git a/deepdiff/__init__.py b/deepdiff/__init__.py index d401c1f4..fdd78b8f 100644 --- a/deepdiff/__init__.py +++ b/deepdiff/__init__.py @@ -1,6 +1,6 @@ """This module offers the DeepDiff, DeepSearch, grep and DeepHash classes.""" # flake8: noqa -__version__ = '4.1.0' +__version__ = '4.2.0' import logging if __name__ == '__main__': diff --git a/docs/conf.py b/docs/conf.py index 1991304e..9f5d5617 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -59,9 +59,9 @@ # built documents. # # The short X.Y version. -version = '4.1.0' +version = '4.2.0' # The full version, including alpha/beta/rc tags. -release = '4.1.0' +release = '4.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/index.rst b/docs/index.rst index e226a481..212a5270 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. -DeepDiff 4.1.0 documentation! +DeepDiff 4.2.0 documentation! ============================= **DeepDiff: Deep Difference of dictionaries, iterables, strings and other objects. It will recursively look for all the changes.** diff --git a/setup.cfg b/setup.cfg index 0838d6b2..8bc9727d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.1.0 +current_version = 4.2.0 commit = True tag = True tag_name = {new_version} diff --git a/setup.py b/setup.py index f1659674..460ae2e8 100755 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ if os.environ.get('USER', '') == 'vagrant': del os.link -version = '4.1.0' +version = '4.2.0' def get_reqs(filename): From 5709f229f4046b14d7c23a67540abbe153a55156 Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Thu, 30 Jan 2020 13:23:51 -0800 Subject: [PATCH 15/15] updating setup.py to remove 3.4 as supported --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 460ae2e8..c4cfe654 100755 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ def get_reqs(filename): long_description=long_description, long_description_content_type='text/markdown', install_requires=reqs, - python_requires='>=3.4', + python_requires='>=3.5', extras_require={ "murmur": ["mmh3"], }, @@ -50,7 +50,6 @@ def get_reqs(filename): "Intended Audience :: Developers", "Operating System :: OS Independent", "Topic :: Software Development", - "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7",