|
1 | 1 | import copy
|
2 | 2 | import logging
|
3 | 3 | from typing import List, Dict, IO, Callable, Set, Union, Optional
|
4 |
| -from functools import partial |
| 4 | +from functools import partial, cmp_to_key |
5 | 5 | from collections.abc import Mapping
|
6 | 6 | from copy import deepcopy
|
7 | 7 | from ordered_set import OrderedSet
|
@@ -399,12 +399,51 @@ def _sort_key_for_item_added(path_and_value):
|
399 | 399 | # We only care about the values in the elements not how to get the values.
|
400 | 400 | return [i[0] for i in elements]
|
401 | 401 |
|
| 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 | + |
402 | 438 | def _do_item_added(self, items, sort=True, insert=False):
|
403 | 439 | if sort:
|
404 | 440 | # sorting items by their path so that the items with smaller index
|
405 | 441 | # are applied first (unless `sort` is `False` so that order of
|
406 | 442 | # 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)) |
408 | 447 | else:
|
409 | 448 | items = items.items()
|
410 | 449 |
|
@@ -526,7 +565,11 @@ def _do_item_removed(self, items):
|
526 | 565 | """
|
527 | 566 | # Sorting the iterable_item_removed in reverse order based on the paths.
|
528 | 567 | # 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: |
530 | 573 | elem_and_details = self._get_elements_and_details(path)
|
531 | 574 | if elem_and_details:
|
532 | 575 | elements, parent, parent_to_obj_elem, parent_to_obj_action, obj, elem, action = elem_and_details
|
|
0 commit comments