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
예제 #3
0
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