Skip to content

Commit ca3c115

Browse files
authored
Merge pull request #230 from seperman/dev
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.
2 parents c6fa94f + 085e239 commit ca3c115

17 files changed

+121
-37
lines changed

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: [seperman]
2+
ko_fi: seperman

AUTHORS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ Authors in order of the contributions:
3333
- [MyrikLD](https://github.com/MyrikLD) for Bug Fix NoneType in ignore type groups
3434
- Stian Jensen [stianjensen](https://github.com/stianjensen) for improving ignoring of NoneType in diff
3535
- Florian Klien [flowolf](https://github.com/flowolf) for adding math_epsilon
36+
- Tim Klein [timjklein36](https://github.com/timjklein36) for retaining the order of multiple dictionary items added via Delta.
37+
- Wilhelm Schürmann[wbsch](https://github.com/wbsch) for fixing the typo with yml files.

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
DeepDiff Change log
1+
# DeepDiff Change log
22

3+
- 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.
34
- v5-2-2: Fixed Delta serialization when None type is present.
45
- 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.
56
- v5-0-2: Bug Fix NoneType in ignore type groups https://github.com/seperman/deepdiff/issues/207

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# DeepDiff v 5.2.2
1+
# DeepDiff v 5.2.3
22

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

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

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

2323

2424
## Installation
@@ -54,13 +54,13 @@ Note: if you want to use DeepDiff via commandline, make sure to run `pip install
5454

5555
DeepDiff gets the difference of 2 objects.
5656

57-
> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.2.2/diff.html)
58-
> - The full documentation of all modules can be found on <https://zepworks.com/deepdiff/5.2.2/>
57+
> - Please take a look at the [DeepDiff docs](https://zepworks.com/deepdiff/5.2.3/diff.html)
58+
> - The full documentation of all modules can be found on <https://zepworks.com/deepdiff/5.2.3/>
5959
> - Tutorials and posts about DeepDiff can be found on <https://zepworks.com/tags/deepdiff/>
6060
6161
## A few Examples
6262

63-
> Note: This is just a brief overview of what DeepDiff can do. Please visit <https://zepworks.com/deepdiff/5.2.2/> for full documentation.
63+
> Note: This is just a brief overview of what DeepDiff can do. Please visit <https://zepworks.com/deepdiff/5.2.3/> for full documentation.
6464
6565
### List difference ignoring order or duplicates
6666

@@ -264,8 +264,8 @@ Example:
264264
```
265265

266266

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

271271
# Deep Search
@@ -297,17 +297,17 @@ And you can pass all the same kwargs as DeepSearch to grep too:
297297
{'matched_paths': {"root['somewhere']": 'around'}, 'matched_values': {"root['long']": 'somewhere'}}
298298
```
299299

300-
> - Please take a look at the [DeepSearch docs](https://zepworks.com/deepdiff/5.2.2/dsearch.html)
301-
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.2/>
300+
> - Please take a look at the [DeepSearch docs](https://zepworks.com/deepdiff/5.2.3/dsearch.html)
301+
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.3/>
302302
303303
# Deep Hash
304304
(New in v4-0-0)
305305

306306
DeepHash is designed to give you hash of ANY python object based on its contents even if the object is not considered hashable!
307307
DeepHash is supposed to be deterministic in order to make sure 2 objects that contain the same data, produce the same hash.
308308

309-
> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.2.2/deephash.html)
310-
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.2/>
309+
> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.2.3/deephash.html)
310+
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.3/>
311311
312312
Let's say you have a dictionary object.
313313

@@ -355,8 +355,8 @@ Which you can write as:
355355
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.
356356

357357

358-
> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.2.2/deephash.html)
359-
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.2/>
358+
> - Please take a look at the [DeepHash docs](https://zepworks.com/deepdiff/5.2.3/deephash.html)
359+
> - The full documentation can be found on <https://zepworks.com/deepdiff/5.2.3/>
360360
361361

362362
# Using DeepDiff in unit tests

deepdiff/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""This module offers the DeepDiff, DeepSearch, grep, Delta and DeepHash classes."""
22
# flake8: noqa
3-
__version__ = '5.2.2'
3+
__version__ = '5.2.3'
44
import logging
55

66
if __name__ == '__main__':

deepdiff/delta.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,16 +267,23 @@ def _do_iterable_item_added(self):
267267
def _do_dictionary_item_added(self):
268268
dictionary_item_added = self.diff.get('dictionary_item_added')
269269
if dictionary_item_added:
270-
self._do_item_added(dictionary_item_added)
270+
self._do_item_added(dictionary_item_added, sort=False)
271271

272272
def _do_attribute_added(self):
273273
attribute_added = self.diff.get('attribute_added')
274274
if attribute_added:
275275
self._do_item_added(attribute_added)
276276

277-
def _do_item_added(self, items):
278-
# sorting the items by their path so that the items with smaller index are applied first.
279-
for path, new_value in sorted(items.items(), key=lambda x: x[0]):
277+
def _do_item_added(self, items, sort=True):
278+
if sort:
279+
# sorting items by their path so that the items with smaller index
280+
# are applied first (unless `sort` is `False` so that order of
281+
# added items is retained, e.g. for dicts).
282+
items = sorted(items.items(), key=lambda x: x[0])
283+
else:
284+
items = items.items()
285+
286+
for path, new_value in items:
280287
elem_and_details = self._get_elements_and_details(path)
281288
if elem_and_details:
282289
elements, parent, parent_to_obj_elem, parent_to_obj_action, obj, elem, action = elem_and_details

deepdiff/diff.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -450,16 +450,16 @@ def _diff_dict(self,
450450
rel_class = DictRelationship
451451

452452
if self.ignore_private_variables:
453-
t1_keys = {key for key in t1 if not(isinstance(key, str) and key.startswith('__'))}
454-
t2_keys = {key for key in t2 if not(isinstance(key, str) and key.startswith('__'))}
453+
t1_keys = OrderedSet([key for key in t1 if not(isinstance(key, str) and key.startswith('__'))])
454+
t2_keys = OrderedSet([key for key in t2 if not(isinstance(key, str) and key.startswith('__'))])
455455
else:
456-
t1_keys = set(t1.keys())
457-
t2_keys = set(t2.keys())
456+
t1_keys = OrderedSet(t1.keys())
457+
t2_keys = OrderedSet(t2.keys())
458458
if self.ignore_string_type_changes or self.ignore_numeric_type_changes:
459459
t1_clean_to_keys = self._get_clean_to_keys_mapping(keys=t1_keys, level=level)
460460
t2_clean_to_keys = self._get_clean_to_keys_mapping(keys=t2_keys, level=level)
461-
t1_keys = set(t1_clean_to_keys.keys())
462-
t2_keys = set(t2_clean_to_keys.keys())
461+
t1_keys = OrderedSet(t1_clean_to_keys.keys())
462+
t2_keys = OrderedSet(t2_clean_to_keys.keys())
463463
else:
464464
t1_clean_to_keys = t2_clean_to_keys = None
465465

deepdiff/search.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ def __search_iterable(self,
223223
parent="root",
224224
parents_ids=frozenset()):
225225
"""Search iterables except dictionaries, sets and strings."""
226-
227226
for i, thing in enumerate(obj):
228227
new_parent = "{}[{}]".format(parent, i)
229228
if self.__skip_this(thing, parent=new_parent):
@@ -271,7 +270,7 @@ def __search_tuple(self, obj, item, parent, parents_ids):
271270

272271
def __search(self, obj, item, parent="root", parents_ids=frozenset()):
273272
"""The main search method"""
274-
273+
# import pytest; pytest.set_trace()
275274
if self.__skip_this(item, parent):
276275
return
277276

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

302-
elif isinstance(obj, Iterable):
301+
elif isinstance(obj, Iterable) and not isinstance(obj, strings):
303302
self.__search_iterable(obj, item, parent, parents_ids)
304303

305304
else:

deepdiff/serialization.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def from_json_pickle(cls, value):
116116
else:
117117
logger.error('jsonpickle library needs to be installed in order to run from_json_pickle') # pragma: no cover. Json pickle is getting deprecated.
118118

119-
def to_json(self, default_mapping=None):
119+
def to_json(self, default_mapping=None, **kwargs):
120120
"""
121121
Dump json of the text view.
122122
**Parameters**
@@ -127,6 +127,8 @@ def to_json(self, default_mapping=None):
127127
If you have a certain object type that the json serializer can not serialize it, please pass the appropriate type
128128
conversion through this dictionary.
129129
130+
kwargs: Any other kwargs you pass will be passed on to Python's json.dumps()
131+
130132
**Example**
131133
132134
Serialize custom objects
@@ -147,7 +149,7 @@ def to_json(self, default_mapping=None):
147149
'{"type_changes": {"root": {"old_type": "A", "new_type": "B", "old_value": "obj A", "new_value": "obj B"}}}'
148150
"""
149151
dic = self.to_dict(view_override=TEXT_VIEW)
150-
return json.dumps(dic, default=json_convertor_default(default_mapping=default_mapping))
152+
return json.dumps(dic, default=json_convertor_default(default_mapping=default_mapping), **kwargs)
151153

152154
def to_dict(self, view_override=None):
153155
"""
@@ -364,7 +366,7 @@ def load_path_content(path, file_type=None):
364366
if file_type == 'json':
365367
with open(path, 'r') as the_file:
366368
content = json.load(the_file)
367-
elif file_type in {'yaml', '.yml'}:
369+
elif file_type in {'yaml', 'yml'}:
368370
if yaml is None: # pragma: no cover.
369371
raise ImportError('Pyyaml needs to be installed.') # pragma: no cover.
370372
with open(path, 'r') as the_file:
@@ -426,7 +428,7 @@ def _save_content(content, path, file_type, keep_backup=True):
426428
if file_type == 'json':
427429
with open(path, 'w') as the_file:
428430
content = json.dump(content, the_file)
429-
elif file_type in {'yaml', '.yml'}:
431+
elif file_type in {'yaml', 'yml'}:
430432
if yaml is None: # pragma: no cover.
431433
raise ImportError('Pyyaml needs to be installed.') # pragma: no cover.
432434
with open(path, 'w') as the_file:

docs/authors.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Thanks to the following people for their contributions:
4040
- `MyrikLD`_ for Bug Fix NoneType in ignore type groups
4141
- Stian Jensen `stianjensen`_ for improving ignoring of NoneType in diff
4242
- Florian Klien `flowolf`_ for adding math_epsilon
43+
- Tim Klein `timjklein36`_ for retaining the order of multiple dictionary items added via Delta
44+
- Wilhelm Schürmann `wbsch`_ for fixing the typo with yml files.
4345

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

7478

7579
Back to :doc:`/index`

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Changelog
55

66
DeepDiff Changelog
77

8+
- 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.
89
- v5-2-2: Fixed Delta serialization when None type is present.
910
- 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.
1011
- v5-0-2: Bug Fix NoneType in ignore type groups https://github.com/seperman/deepdiff/issues/207

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@
6060
# built documents.
6161
#
6262
# The short X.Y version.
63-
version = '5.2.2'
63+
version = '5.2.3'
6464
# The full version, including alpha/beta/rc tags.
65-
release = '5.2.2'
65+
release = '5.2.3'
6666

6767
load_dotenv(override=True)
6868
DOC_VERSION = os.environ.get('DOC_VERSION', version)

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
contain the root `toctree` directive.
55
66
7-
DeepDiff 5.2.2 documentation!
7+
DeepDiff 5.2.3 documentation!
88
=============================
99

1010
*****************

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 5.2.2
2+
current_version = 5.2.3
33
commit = True
44
tag = True
55
tag_name = {new_version}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
if os.environ.get('USER', '') == 'vagrant':
1111
del os.link
1212

13-
version = '5.2.2'
13+
version = '5.2.3'
1414

1515

1616
def get_reqs(filename):

tests/test_delta.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,41 @@ def test_list_difference_delta_raises_error_if_prev_value_changed(self):
335335
delta2 = Delta(diff, verify_symmetry=False, raise_errors=True)
336336
assert t1 + delta2 == t2
337337

338+
def test_delta_dict_items_added_retain_order(self):
339+
t1 = {
340+
6: 6
341+
}
342+
343+
t2 = {
344+
6: 6,
345+
7: 7,
346+
3: 3,
347+
5: 5,
348+
2: 2,
349+
4: 4
350+
}
351+
352+
expected_delta_dict = {
353+
'dictionary_item_added': {
354+
'root[7]': 7,
355+
'root[3]': 3,
356+
'root[5]': 5,
357+
'root[2]': 2,
358+
'root[4]': 4
359+
}
360+
}
361+
362+
diff = DeepDiff(t1, t2)
363+
delta_dict = diff._to_delta_dict()
364+
assert expected_delta_dict == delta_dict
365+
delta = Delta(diff, verify_symmetry=False, raise_errors=True)
366+
367+
result = t1 + delta
368+
assert result == t2
369+
370+
assert list(result.keys()) == [6, 7, 3, 5, 2, 4]
371+
assert list(result.keys()) == list(t2.keys())
372+
338373

339374
picklalbe_obj_without_item = PicklableClass(11)
340375
del picklalbe_obj_without_item.item

tests/test_search.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ def test_number_in_list(self):
2929
result = {"matched_values": {'root[1]'}}
3030
assert DeepSearch(obj, item, verbose_level=1) == result
3131

32+
def test_number_in_list2(self):
33+
obj = ["a", "10", 10, 20]
34+
item = 10
35+
result = {"matched_values": {'root[2]'}}
36+
assert DeepSearch(obj, item, verbose_level=1) == result
37+
38+
def test_number_in_list3(self):
39+
obj = ["a", "10", 10, 20]
40+
item = "10"
41+
result = {"matched_values": {'root[1]'}}
42+
assert DeepSearch(obj, item, verbose_level=1) == result
43+
3244
def test_string_in_root(self):
3345
obj = "long string somewhere"
3446
result = {"matched_values": {'root'}}
@@ -334,3 +346,22 @@ def test_grep_dict(self):
334346
}
335347
ds = obj | grep(item)
336348
assert ds == {'matched_values': {"root['ingredients'][3]"}}
349+
350+
def test_grep_dict_in_dict(self):
351+
obj = {
352+
"x": {
353+
"y": [
354+
"aaaaaa\u0142 bbbbb"
355+
]
356+
},
357+
"z": "z",
358+
}
359+
item = {"z": "z"}
360+
result = obj | grep(item)
361+
assert {} == result
362+
363+
def test_grep_with_non_utf8_chars(self):
364+
obj = "aaaaaa\u0142 bbbbb"
365+
item = {"z": "z"}
366+
result = obj | grep(item)
367+
assert {} == result

0 commit comments

Comments
 (0)