Skip to content

Commit 2063331

Browse files
committed
fix for sort comparison of elements that are not the same type
1 parent dd0d257 commit 2063331

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

deepdiff/delta.py

+46-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import copy
22
import logging
33
from typing import List, Dict, IO, Callable, Set, Union, Optional
4-
from functools import partial
4+
from functools import partial, cmp_to_key
55
from collections.abc import Mapping
66
from copy import deepcopy
77
from ordered_set import OrderedSet
@@ -399,12 +399,51 @@ def _sort_key_for_item_added(path_and_value):
399399
# We only care about the values in the elements not how to get the values.
400400
return [i[0] for i in elements]
401401

402+
@staticmethod
403+
def _sort_comparison(left, right):
404+
"""
405+
We use sort comparison instead of _sort_key_for_item_added when we run into comparing element types that can not
406+
be compared with each other, such as None to None. Or integer to string.
407+
"""
408+
# Example elements: [(4.3, 'GET'), ('b', 'GETATTR'), ('a3', 'GET')]
409+
# We only care about the values in the elements not how to get the values.
410+
left_path = [i[0] for i in _path_to_elements(left[0], root_element=None)]
411+
right_path = [i[0] for i in _path_to_elements(right[0], root_element=None)]
412+
try:
413+
if left_path < right_path:
414+
return -1
415+
elif left_path > right_path:
416+
return 1
417+
else:
418+
return 0
419+
except TypeError:
420+
if len(left_path) > len(right_path):
421+
left_path = left_path[:len(right_path)]
422+
elif len(right_path) > len(left_path):
423+
right_path = right_path[:len(left_path)]
424+
for l_elem, r_elem in zip(left_path, right_path):
425+
if type(l_elem) != type(r_elem) or type(l_elem) in None:
426+
l_elem = str(l_elem)
427+
r_elem = str(r_elem)
428+
try:
429+
if l_elem < r_elem:
430+
return -1
431+
elif l_elem > r_elem:
432+
return 1
433+
except TypeError:
434+
continue
435+
return 0
436+
437+
402438
def _do_item_added(self, items, sort=True, insert=False):
403439
if sort:
404440
# sorting items by their path so that the items with smaller index
405441
# are applied first (unless `sort` is `False` so that order of
406442
# added items is retained, e.g. for dicts).
407-
items = sorted(items.items(), key=self._sort_key_for_item_added)
443+
try:
444+
items = sorted(items.items(), key=self._sort_key_for_item_added)
445+
except TypeError:
446+
items = sorted(items.items(), key=cmp_to_key(self._sort_comparison))
408447
else:
409448
items = items.items()
410449

@@ -526,7 +565,11 @@ def _do_item_removed(self, items):
526565
"""
527566
# Sorting the iterable_item_removed in reverse order based on the paths.
528567
# So that we delete a bigger index before a smaller index
529-
for path, expected_old_value in sorted(items.items(), key=self._sort_key_for_item_added, reverse=True):
568+
try:
569+
sorted_item = sorted(items.items(), key=self._sort_key_for_item_added, reverse=True)
570+
except TypeError:
571+
sorted_item = sorted(items.items(), key=cmp_to_key(self._sort_comparison), reverse=True)
572+
for path, expected_old_value in sorted_item:
530573
elem_and_details = self._get_elements_and_details(path)
531574
if elem_and_details:
532575
elements, parent, parent_to_obj_elem, parent_to_obj_action, obj, elem, action = elem_and_details

tests/test_delta.py

+9
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,15 @@ def test_ignore_order_delta_cases(
13161316
},
13171317
'expected_result': 't2'
13181318
},
1319+
'delta_with_null_as_key': {
1320+
't1': { None: [1, 2], 'foo': [1, 2] },
1321+
't2': { None: [1], 'foo': [1] },
1322+
'deepdiff_kwargs': {},
1323+
'to_delta_kwargs': {},
1324+
'expected_delta_dict': {
1325+
},
1326+
'expected_result': 't2'
1327+
},
13191328
}
13201329

13211330

0 commit comments

Comments
 (0)