@@ -896,8 +896,9 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
896
896
child_relationship_class = child_relationship_class ,
897
897
local_tree = local_tree_pass ,
898
898
)
899
+ has_moves = bool (local_tree_pass ['iterable_item_moved' ])
899
900
# Sometimes DeepDiff's old iterable diff does a better job than DeepDiff
900
- if len (local_tree_pass ) > 1 :
901
+ if len (local_tree_pass ) > 1 and not has_moves :
901
902
local_tree_pass2 = TreeResult ()
902
903
self ._diff_by_forming_pairs_and_comparing_one_by_one (
903
904
level ,
@@ -910,6 +911,8 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
910
911
local_tree_pass = local_tree_pass2
911
912
else :
912
913
self ._iterable_opcodes [level .path (force = FORCE_DEFAULT )] = opcodes_with_values
914
+ else :
915
+ self ._iterable_opcodes [level .path (force = FORCE_DEFAULT )] = opcodes_with_values
913
916
for report_type , levels in local_tree_pass .items ():
914
917
if levels :
915
918
self .tree [report_type ] |= levels
@@ -1015,32 +1018,28 @@ def _diff_ordered_iterable_by_difflib(
1015
1018
1016
1019
opcodes = seq .get_opcodes ()
1017
1020
opcodes_with_values = []
1021
+ replace_opcodes : List [Opcode ] = []
1018
1022
1019
- # TODO: this logic should be revisted so we detect reverse operations
1020
- # like when a replacement happens at index X and a reverse replacement happens at index Y
1021
- # in those cases we have a "iterable_item_moved" operation.
1022
1023
for tag , t1_from_index , t1_to_index , t2_from_index , t2_to_index in opcodes :
1023
1024
if tag == 'equal' :
1024
- opcodes_with_values .append (Opcode (
1025
- tag , t1_from_index , t1_to_index , t2_from_index , t2_to_index ,
1026
- ))
1025
+ opcodes_with_values .append (
1026
+ Opcode ( tag , t1_from_index , t1_to_index , t2_from_index , t2_to_index )
1027
+ )
1027
1028
continue
1028
- # print('{:7} t1[{}:{}] --> t2[{}:{}] {!r:>8} --> {!r}'.format(
1029
- # tag, t1_from_index, t1_to_index, t2_from_index, t2_to_index, level.t1[t1_from_index:t1_to_index], level.t2[t2_from_index:t2_to_index]))
1030
1029
1031
- opcodes_with_values .append (Opcode (
1032
- tag , t1_from_index , t1_to_index , t2_from_index , t2_to_index ,
1033
- old_values = level .t1 [t1_from_index : t1_to_index ],
1034
- new_values = level .t2 [t2_from_index : t2_to_index ],
1035
- ))
1030
+ opcode = Opcode (
1031
+ tag ,
1032
+ t1_from_index ,
1033
+ t1_to_index ,
1034
+ t2_from_index ,
1035
+ t2_to_index ,
1036
+ old_values = level .t1 [t1_from_index :t1_to_index ],
1037
+ new_values = level .t2 [t2_from_index :t2_to_index ],
1038
+ )
1039
+ opcodes_with_values .append (opcode )
1036
1040
1037
1041
if tag == 'replace' :
1038
- self ._diff_by_forming_pairs_and_comparing_one_by_one (
1039
- level , local_tree = local_tree , parents_ids = parents_ids ,
1040
- _original_type = _original_type , child_relationship_class = child_relationship_class ,
1041
- t1_from_index = t1_from_index , t1_to_index = t1_to_index ,
1042
- t2_from_index = t2_from_index , t2_to_index = t2_to_index ,
1043
- )
1042
+ replace_opcodes .append (opcode )
1044
1043
elif tag == 'delete' :
1045
1044
for index , x in enumerate (level .t1 [t1_from_index :t1_to_index ]):
1046
1045
change_level = level .branch_deeper (
@@ -1061,6 +1060,62 @@ def _diff_ordered_iterable_by_difflib(
1061
1060
child_relationship_param2 = index + t2_from_index ,
1062
1061
)
1063
1062
self ._report_result ('iterable_item_added' , change_level , local_tree = local_tree )
1063
+
1064
+ used : Set [int ] = set ()
1065
+ for i , opcode_a in enumerate (replace_opcodes ):
1066
+ if i in used :
1067
+ continue
1068
+ for j in range (i + 1 , len (replace_opcodes )):
1069
+ opcode_b = replace_opcodes [j ]
1070
+ if j in used :
1071
+ continue
1072
+ if (
1073
+ opcode_a .old_values == opcode_b .new_values
1074
+ and opcode_a .new_values == opcode_b .old_values
1075
+ and len (opcode_a .old_values or []) == len (opcode_b .old_values or [])
1076
+ ):
1077
+ length = len (opcode_a .old_values or [])
1078
+ for offset in range (length ):
1079
+ val_a = opcode_a .old_values [offset ]
1080
+ new_index_a = opcode_b .t2_from_index + offset
1081
+ change_level = level .branch_deeper (
1082
+ val_a ,
1083
+ val_a ,
1084
+ child_relationship_class = child_relationship_class ,
1085
+ child_relationship_param = opcode_a .t1_from_index + offset ,
1086
+ child_relationship_param2 = new_index_a ,
1087
+ )
1088
+ self ._report_result ('iterable_item_moved' , change_level , local_tree = local_tree )
1089
+
1090
+ val_b = opcode_b .old_values [offset ]
1091
+ new_index_b = opcode_a .t2_from_index + offset
1092
+ change_level = level .branch_deeper (
1093
+ val_b ,
1094
+ val_b ,
1095
+ child_relationship_class = child_relationship_class ,
1096
+ child_relationship_param = opcode_b .t1_from_index + offset ,
1097
+ child_relationship_param2 = new_index_b ,
1098
+ )
1099
+ self ._report_result ('iterable_item_moved' , change_level , local_tree = local_tree )
1100
+
1101
+ used .update ({i , j })
1102
+ break
1103
+
1104
+ for idx , opcode in enumerate (replace_opcodes ):
1105
+ if idx in used :
1106
+ continue
1107
+ self ._diff_by_forming_pairs_and_comparing_one_by_one (
1108
+ level ,
1109
+ local_tree = local_tree ,
1110
+ parents_ids = parents_ids ,
1111
+ _original_type = _original_type ,
1112
+ child_relationship_class = child_relationship_class ,
1113
+ t1_from_index = opcode .t1_from_index ,
1114
+ t1_to_index = opcode .t1_to_index ,
1115
+ t2_from_index = opcode .t2_from_index ,
1116
+ t2_to_index = opcode .t2_to_index ,
1117
+ )
1118
+
1064
1119
return opcodes_with_values
1065
1120
1066
1121
0 commit comments