@@ -896,8 +896,9 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
896896 child_relationship_class = child_relationship_class ,
897897 local_tree = local_tree_pass ,
898898 )
899+ has_moves = bool (local_tree_pass ['iterable_item_moved' ])
899900 # 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 :
901902 local_tree_pass2 = TreeResult ()
902903 self ._diff_by_forming_pairs_and_comparing_one_by_one (
903904 level ,
@@ -910,6 +911,8 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
910911 local_tree_pass = local_tree_pass2
911912 else :
912913 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
913916 for report_type , levels in local_tree_pass .items ():
914917 if levels :
915918 self .tree [report_type ] |= levels
@@ -1015,32 +1018,28 @@ def _diff_ordered_iterable_by_difflib(
10151018
10161019 opcodes = seq .get_opcodes ()
10171020 opcodes_with_values = []
1021+ replace_opcodes : List [Opcode ] = []
10181022
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.
10221023 for tag , t1_from_index , t1_to_index , t2_from_index , t2_to_index in opcodes :
10231024 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+ )
10271028 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]))
10301029
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 )
10361040
10371041 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 )
10441043 elif tag == 'delete' :
10451044 for index , x in enumerate (level .t1 [t1_from_index :t1_to_index ]):
10461045 change_level = level .branch_deeper (
@@ -1061,6 +1060,62 @@ def _diff_ordered_iterable_by_difflib(
10611060 child_relationship_param2 = index + t2_from_index ,
10621061 )
10631062 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+
10641119 return opcodes_with_values
10651120
10661121
0 commit comments