def build_split_diff(lhs_repr, rhs_repr): lhs_out, rhs_out = "", "" matcher = difflib.SequenceMatcher(None, lhs_repr, rhs_repr) for op, i1, i2, j1, j2 in matcher.get_opcodes(): lhs_substring_lines = lhs_repr[i1:i2].splitlines() rhs_substring_lines = rhs_repr[j1:j2].splitlines() for i, lhs_substring in enumerate(lhs_substring_lines): if op == "replace": lhs_out += inserted_text(lhs_substring) elif op == "delete": lhs_out += inserted_text(lhs_substring) elif op == "insert": lhs_out += Colour.stop + lhs_substring elif op == "equal": lhs_out += Colour.stop + lhs_substring if i != len(lhs_substring_lines) - 1: lhs_out += "\n" for j, rhs_substring in enumerate(rhs_substring_lines): if op == "replace": rhs_out += deleted_text(rhs_substring) elif op == "insert": rhs_out += deleted_text(rhs_substring) elif op == "equal": rhs_out += Colour.stop + rhs_substring if j != len(rhs_substring_lines) - 1: rhs_out += "\n" return lhs_out.splitlines(), rhs_out.splitlines()
def hints_for(op, lhs, rhs): hints = [""] # immediate newline if op == "==": if direct_type_mismatch(lhs, rhs): lhs_type, rhs_type = inserted_text(type(lhs)), deleted_text(type(rhs)) hints += [ hint_text("left and right are different types:"), hint_body_text( " type(left) is {}, type(right) is {}".format(lhs_type, rhs_type) ), ] if has_differing_len(lhs, rhs): lhs_len, rhs_len = inserted_text(len(lhs)), deleted_text(len(rhs)) hints += [ hint_text("left and right have different lengths:"), hint_body_text( " len(left) == {}, len(right) == {}".format(lhs_len, rhs_len) ), ] if possibly_missing_eq(lhs, rhs): hints += [ hint_text("left and right are equal in data and in type: "), hint_body_text(" perhaps you forgot to implement __eq__ and __ne__?"), ] both_sequences = isinstance(lhs, Sequence) and isinstance(rhs, Sequence) both_dicts = isinstance(lhs, dict) and isinstance(rhs, dict) if both_dicts: lhs, rhs = lhs.items(), rhs.items() if both_sequences or both_dicts: num_extras, lines = find_extras(lhs, rhs, inserted_text, "+") hints += [ hint_text("{} items in left, but not right:".format(num_extras)) ] + lines num_extras, lines = find_extras(rhs, lhs, deleted_text, "-") hints += [ hint_text("{} items in right, but not left:".format(num_extras)) ] + lines return hints
def build_full_unidiff_output(lhs_repr, rhs_repr, op): left_key = inserted_text("L=left") right_key = deleted_text("R=right") return [ "left {} right failed. ".format(op), "{}Showing unified diff ({}, {}):".format(Colour.stop, left_key, right_key), "", ] + build_unified_diff(lhs_repr, rhs_repr)
def build_unified_diff(lhs_repr, rhs_repr): differ = difflib.Differ() lines_lhs, lines_rhs = lhs_repr.splitlines(), rhs_repr.splitlines() diff = differ.compare(lines_lhs, lines_rhs) output = [] for line in diff: # Differ instructs us how to transform left into right, but we want # our colours to indicate how to transform right into left if line.startswith("- "): output.append(inserted_text(" L " + line[2:])) elif line.startswith("+ "): output.append(deleted_text(" R " + line[2:])) elif line.startswith("? "): # We can use this to find the index of change in the # line above if required in the future pass else: output.append(non_formatted(line)) return output