Skip to content

Dev #230

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 9 commits into from
Feb 16, 2021
Merged

Dev #230

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
2 changes: 2 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github: [seperman]
ko_fi: seperman
2 changes: 2 additions & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ Authors in order of the contributions:
- [MyrikLD](https://github.com/MyrikLD) for Bug Fix NoneType in ignore type groups
- Stian Jensen [stianjensen](https://github.com/stianjensen) for improving ignoring of NoneType in diff
- Florian Klien [flowolf](https://github.com/flowolf) for adding math_epsilon
- Tim Klein [timjklein36](https://github.com/timjklein36) for retaining the order of multiple dictionary items added via Delta.
- Wilhelm Schürmann[wbsch](https://github.com/wbsch) for fixing the typo with yml files.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
DeepDiff Change log
# DeepDiff Change log

- v5-2-3: Retaining the order of multiple dictionary items added via Delta. Fixed the typo with yml files in deep cli. Fixing Grep RecursionError where using non UTF-8 character. Allowing kwargs to be passed to to_json method.
- v5-2-2: Fixed Delta serialization when None type is present.
- v5-2-0: Removed Murmur3 as the preferred hashing method. Using SHA256 by default now. Added commandline for deepdiff. Added group_by. Added math_epsilon. Improved ignoring of NoneType.
- v5-0-2: Bug Fix NoneType in ignore type groups https://github.com/seperman/deepdiff/issues/207
Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# DeepDiff v 5.2.2
# DeepDiff v 5.2.3

![Downloads](https://img.shields.io/pypi/dm/deepdiff.svg?style=flat)
![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat)
Expand All @@ -18,7 +18,7 @@ Tested on Python 3.6+ and PyPy3.

**NOTE: The last version of DeepDiff to work on Python 3.5 was DeepDiff 5-0-2**

- [Documentation](https://zepworks.com/deepdiff/5.2.2/)
- [Documentation](https://zepworks.com/deepdiff/5.2.3/)


## Installation
Expand Down Expand Up @@ -54,13 +54,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.2.2/diff.html)
> - The full documentation of all modules can be found on <https://zepworks.com/deepdiff/5.2.2/>
> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.2.3/diff.html)
> - The full documentation of all modules can be found on <https://zepworks.com/deepdiff/5.2.3/>
> - Tutorials and posts about DeepDiff can be found on <https://zepworks.com/tags/deepdiff/>

## A few Examples

> Note: This is just a brief overview of what DeepDiff can do. Please visit <https://zepworks.com/deepdiff/5.2.2/> for full documentation.
> Note: This is just a brief overview of what DeepDiff can do. Please visit <https://zepworks.com/deepdiff/5.2.3/> for full documentation.

### List difference ignoring order or duplicates

Expand Down Expand Up @@ -264,8 +264,8 @@ Example:
```


> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.2.2/diff.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.2/>
> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.2.3/diff.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.3/>


# Deep Search
Expand Down Expand Up @@ -297,17 +297,17 @@ 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.2.2/dsearch.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.2/>
> - Please take a look at the [DeepSearch docs](https://zepworks.com/deepdiff/5.2.3/dsearch.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.3/>

# Deep Hash
(New in v4-0-0)

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.2.2/deephash.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.2/>
> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.2.3/deephash.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.3/>

Let's say you have a dictionary object.

Expand Down Expand Up @@ -355,8 +355,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.2.2/deephash.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.2/>
> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.2.3/deephash.html)
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.3/>


# Using DeepDiff in unit tests
Expand Down
2 changes: 1 addition & 1 deletion deepdiff/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""This module offers the DeepDiff, DeepSearch, grep, Delta and DeepHash classes."""
# flake8: noqa
__version__ = '5.2.2'
__version__ = '5.2.3'
import logging

if __name__ == '__main__':
Expand Down
15 changes: 11 additions & 4 deletions deepdiff/delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,16 +267,23 @@ def _do_iterable_item_added(self):
def _do_dictionary_item_added(self):
dictionary_item_added = self.diff.get('dictionary_item_added')
if dictionary_item_added:
self._do_item_added(dictionary_item_added)
self._do_item_added(dictionary_item_added, sort=False)

def _do_attribute_added(self):
attribute_added = self.diff.get('attribute_added')
if attribute_added:
self._do_item_added(attribute_added)

def _do_item_added(self, items):
# sorting the items by their path so that the items with smaller index are applied first.
for path, new_value in sorted(items.items(), key=lambda x: x[0]):
def _do_item_added(self, items, sort=True):
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])
else:
items = items.items()

for path, new_value in items:
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
Expand Down
12 changes: 6 additions & 6 deletions deepdiff/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,16 +450,16 @@ def _diff_dict(self,
rel_class = DictRelationship

if self.ignore_private_variables:
t1_keys = {key for key in t1 if not(isinstance(key, str) and key.startswith('__'))}
t2_keys = {key for key in t2 if not(isinstance(key, str) and key.startswith('__'))}
t1_keys = OrderedSet([key for key in t1 if not(isinstance(key, str) and key.startswith('__'))])
t2_keys = OrderedSet([key for key in t2 if not(isinstance(key, str) and key.startswith('__'))])
else:
t1_keys = set(t1.keys())
t2_keys = set(t2.keys())
t1_keys = OrderedSet(t1.keys())
t2_keys = OrderedSet(t2.keys())
if self.ignore_string_type_changes or self.ignore_numeric_type_changes:
t1_clean_to_keys = self._get_clean_to_keys_mapping(keys=t1_keys, level=level)
t2_clean_to_keys = self._get_clean_to_keys_mapping(keys=t2_keys, level=level)
t1_keys = set(t1_clean_to_keys.keys())
t2_keys = set(t2_clean_to_keys.keys())
t1_keys = OrderedSet(t1_clean_to_keys.keys())
t2_keys = OrderedSet(t2_clean_to_keys.keys())
else:
t1_clean_to_keys = t2_clean_to_keys = None

Expand Down
5 changes: 2 additions & 3 deletions deepdiff/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ def __search_iterable(self,
parent="root",
parents_ids=frozenset()):
"""Search iterables except dictionaries, sets and strings."""

for i, thing in enumerate(obj):
new_parent = "{}[{}]".format(parent, i)
if self.__skip_this(thing, parent=new_parent):
Expand Down Expand Up @@ -271,7 +270,7 @@ def __search_tuple(self, obj, item, parent, parents_ids):

def __search(self, obj, item, parent="root", parents_ids=frozenset()):
"""The main search method"""

# import pytest; pytest.set_trace()
if self.__skip_this(item, parent):
return

Expand Down Expand Up @@ -299,7 +298,7 @@ def __search(self, obj, item, parent="root", parents_ids=frozenset()):
self.warning_num += 1
self.__search_iterable(obj, item, parent, parents_ids)

elif isinstance(obj, Iterable):
elif isinstance(obj, Iterable) and not isinstance(obj, strings):
self.__search_iterable(obj, item, parent, parents_ids)

else:
Expand Down
10 changes: 6 additions & 4 deletions deepdiff/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def from_json_pickle(cls, value):
else:
logger.error('jsonpickle library needs to be installed in order to run from_json_pickle') # pragma: no cover. Json pickle is getting deprecated.

def to_json(self, default_mapping=None):
def to_json(self, default_mapping=None, **kwargs):
"""
Dump json of the text view.
**Parameters**
Expand All @@ -127,6 +127,8 @@ def to_json(self, default_mapping=None):
If you have a certain object type that the json serializer can not serialize it, please pass the appropriate type
conversion through this dictionary.

kwargs: Any other kwargs you pass will be passed on to Python's json.dumps()

**Example**

Serialize custom objects
Expand All @@ -147,7 +149,7 @@ def to_json(self, default_mapping=None):
'{"type_changes": {"root": {"old_type": "A", "new_type": "B", "old_value": "obj A", "new_value": "obj B"}}}'
"""
dic = self.to_dict(view_override=TEXT_VIEW)
return json.dumps(dic, default=json_convertor_default(default_mapping=default_mapping))
return json.dumps(dic, default=json_convertor_default(default_mapping=default_mapping), **kwargs)

def to_dict(self, view_override=None):
"""
Expand Down Expand Up @@ -364,7 +366,7 @@ def load_path_content(path, file_type=None):
if file_type == 'json':
with open(path, 'r') as the_file:
content = json.load(the_file)
elif file_type in {'yaml', '.yml'}:
elif file_type in {'yaml', 'yml'}:
if yaml is None: # pragma: no cover.
raise ImportError('Pyyaml needs to be installed.') # pragma: no cover.
with open(path, 'r') as the_file:
Expand Down Expand Up @@ -426,7 +428,7 @@ def _save_content(content, path, file_type, keep_backup=True):
if file_type == 'json':
with open(path, 'w') as the_file:
content = json.dump(content, the_file)
elif file_type in {'yaml', '.yml'}:
elif file_type in {'yaml', 'yml'}:
if yaml is None: # pragma: no cover.
raise ImportError('Pyyaml needs to be installed.') # pragma: no cover.
with open(path, 'w') as the_file:
Expand Down
4 changes: 4 additions & 0 deletions docs/authors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Thanks to the following people for their contributions:
- `MyrikLD`_ for Bug Fix NoneType in ignore type groups
- Stian Jensen `stianjensen`_ for improving ignoring of NoneType in diff
- Florian Klien `flowolf`_ for adding math_epsilon
- Tim Klein `timjklein36`_ for retaining the order of multiple dictionary items added via Delta
- Wilhelm Schürmann `wbsch`_ for fixing the typo with yml files.

.. _Sep Dehpour (Seperman): http://www.zepworks.com
.. _Victor Hahn Castell: http://hahncastell.de
Expand Down Expand Up @@ -70,6 +72,8 @@ Thanks to the following people for their contributions:
.. _MyrikLD: https://github.com/MyrikLD
.. _stianjensen: https://github.com/stianjensen
.. _flowolf: https://github.com/flowolf
.. _timjklein36: https://github.com/timjklein36
.. _wbsch: https://github.com/wbsch


Back to :doc:`/index`
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog

DeepDiff Changelog

- v5-2-3: Retaining the order of multiple dictionary items added via Delta. Fixed the typo with yml files in deep cli. Fixing Grep RecursionError where using non UTF-8 character. Allowing kwargs to be passed to to_json method.
- v5-2-2: Fixed Delta serialization when None type is present.
- v5-2-0: Removed Murmur3 as the preferred hashing method. Using SHA256 by default now. Added commandline for deepdiff. Added group_by. Added math_epsilon. Improved ignoring of NoneType.
- v5-0-2: Bug Fix NoneType in ignore type groups https://github.com/seperman/deepdiff/issues/207
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@
# built documents.
#
# The short X.Y version.
version = '5.2.2'
version = '5.2.3'
# The full version, including alpha/beta/rc tags.
release = '5.2.2'
release = '5.2.3'

load_dotenv(override=True)
DOC_VERSION = os.environ.get('DOC_VERSION', version)
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
contain the root `toctree` directive.


DeepDiff 5.2.2 documentation!
DeepDiff 5.2.3 documentation!
=============================

*****************
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 5.2.2
current_version = 5.2.3
commit = True
tag = True
tag_name = {new_version}
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
if os.environ.get('USER', '') == 'vagrant':
del os.link

version = '5.2.2'
version = '5.2.3'


def get_reqs(filename):
Expand Down
35 changes: 35 additions & 0 deletions tests/test_delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,41 @@ def test_list_difference_delta_raises_error_if_prev_value_changed(self):
delta2 = Delta(diff, verify_symmetry=False, raise_errors=True)
assert t1 + delta2 == t2

def test_delta_dict_items_added_retain_order(self):
t1 = {
6: 6
}

t2 = {
6: 6,
7: 7,
3: 3,
5: 5,
2: 2,
4: 4
}

expected_delta_dict = {
'dictionary_item_added': {
'root[7]': 7,
'root[3]': 3,
'root[5]': 5,
'root[2]': 2,
'root[4]': 4
}
}

diff = DeepDiff(t1, t2)
delta_dict = diff._to_delta_dict()
assert expected_delta_dict == delta_dict
delta = Delta(diff, verify_symmetry=False, raise_errors=True)

result = t1 + delta
assert result == t2

assert list(result.keys()) == [6, 7, 3, 5, 2, 4]
assert list(result.keys()) == list(t2.keys())


picklalbe_obj_without_item = PicklableClass(11)
del picklalbe_obj_without_item.item
Expand Down
31 changes: 31 additions & 0 deletions tests/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ def test_number_in_list(self):
result = {"matched_values": {'root[1]'}}
assert DeepSearch(obj, item, verbose_level=1) == result

def test_number_in_list2(self):
obj = ["a", "10", 10, 20]
item = 10
result = {"matched_values": {'root[2]'}}
assert DeepSearch(obj, item, verbose_level=1) == result

def test_number_in_list3(self):
obj = ["a", "10", 10, 20]
item = "10"
result = {"matched_values": {'root[1]'}}
assert DeepSearch(obj, item, verbose_level=1) == result

def test_string_in_root(self):
obj = "long string somewhere"
result = {"matched_values": {'root'}}
Expand Down Expand Up @@ -334,3 +346,22 @@ def test_grep_dict(self):
}
ds = obj | grep(item)
assert ds == {'matched_values': {"root['ingredients'][3]"}}

def test_grep_dict_in_dict(self):
obj = {
"x": {
"y": [
"aaaaaa\u0142 bbbbb"
]
},
"z": "z",
}
item = {"z": "z"}
result = obj | grep(item)
assert {} == result

def test_grep_with_non_utf8_chars(self):
obj = "aaaaaa\u0142 bbbbb"
item = {"z": "z"}
result = obj | grep(item)
assert {} == result