Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions patchdiff/diff.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from functools import reduce
from functools import partial, reduce
from typing import Dict, List, Set, Tuple

from .pointer import Pointer
Expand All @@ -17,7 +17,7 @@ def dist(i, j):
if i > 0:
base = dist(i - 1, j)
op = {"op": "remove", "idx": i - 1}
rop = {"op": "add", "idx": i - 1, "value": input[i - 1]}
rop = {"op": "add", "idx": j - 1, "value": input[i - 1]}
paths.append(
{
"ops": base["ops"] + [op],
Expand All @@ -27,7 +27,7 @@ def dist(i, j):
)
if j > 0:
base = dist(i, j - 1)
op = {"op": "add", "idx": j - 1, "value": output[j - 1]}
op = {"op": "add", "idx": i - 1, "value": output[j - 1]}
rop = {"op": "remove", "idx": j - 1}
paths.append(
{
Expand All @@ -46,7 +46,7 @@ def dist(i, j):
}
rop = {
"op": "replace",
"idx": i - 1,
"idx": j - 1,
"original": output[j - 1],
"value": input[i - 1],
}
Expand All @@ -61,11 +61,11 @@ def dist(i, j):
memory[(i, j)] = step
return memory[(i, j)]

def pad(state, op):
def pad(state, op, target=None):
ops, padding = state
if op["op"] == "add":
padded_idx = op["idx"] + 1 + padding
idx_token = padded_idx if padded_idx < len(input) + padding else "-"
idx_token = padded_idx if padded_idx < len(target) + padding else "-"
full_op = {
"op": "add",
"path": ptr.append(idx_token),
Expand All @@ -84,8 +84,8 @@ def pad(state, op):
return [ops + replace_ops, padding]

solution = dist(len(input), len(output))
padded_ops, _ = reduce(pad, solution["ops"], [[], 0])
padded_rops, _ = reduce(pad, reversed(solution["rops"]), [[], 0])
padded_ops, _ = reduce(partial(pad, target=input), solution["ops"], [[], 0])
padded_rops, _ = reduce(partial(pad, target=output), solution["rops"], [[], 0])

return padded_ops, padded_rops

Expand Down
93 changes: 81 additions & 12 deletions tests/test_apply.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
from patchdiff import apply, diff
from patchdiff.pointer import Pointer


def test_apply():
a = {
"a": [5, 7, 9, {"a", "b", "c"}],
"b": 6,
}
b = {"a": [5, 2, 9, {"b", "c"}], "b": 6, "c": 7}
b = {
"a": [5, 2, 9, {"b", "c"}],
"b": 6,
"c": 7,
}

ops, rops = diff(a, b)

assert ops == [
{"op": "add", "path": Pointer(["c"]), "value": 7},
{"op": "replace", "path": Pointer(["a", 1]), "value": 2},
{"op": "remove", "path": Pointer(["a", 3, "a"])},
]
c = apply(a, ops)
assert c == b

d = apply(b, rops)
assert a == d


def test_apply_list():
a = [1, 5, 9, "sdfsdf", "fff"]
b = ["sdf", 5, 9, "c"]

assert rops == [
{"op": "add", "path": Pointer(["a", 3, "-"]), "value": "a"},
{"op": "replace", "path": Pointer(["a", 1]), "value": 7},
{"op": "remove", "path": Pointer(["c"])},
]
ops, rops = diff(a, b)

c = apply(a, ops)
assert c == b
Expand All @@ -40,3 +45,67 @@ def test_add_remove_list():

d = apply(b, rops)
assert a == d


def test_add_remove_list_extended():
a = []
b = [1, 2, 3]

ops, rops = diff(a, b)

c = apply(a, ops)
assert c == b

d = apply(b, rops)
assert a == d


def test_insertion_in_list_front():
a = [1, 2]
b = [3, 1, 2]
ops, rops = diff(a, b)

c = apply(a, ops)
assert c == b

d = apply(b, rops)
assert a == d


def test_add_remove_list_extended_inverse():
a = [1, 2, 3]
b = []

ops, rops = diff(a, b)

c = apply(a, ops)
assert c == b

d = apply(b, rops)
assert a == d


def test_add_remove_list_extended_inverse_leaving_start():
a = [1, 2, 3, 4]
b = [1]

ops, rops = diff(a, b)

c = apply(a, ops)
assert c == b

d = apply(b, rops)
assert a == d


def test_add_remove_list_extended_inverse_leaving_end():
a = [1, 2, 3, 4]
b = [4]

ops, rops = diff(a, b)

c = apply(a, ops)
assert c == b

d = apply(b, rops)
assert a == d
100 changes: 89 additions & 11 deletions tests/test_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,76 @@
from patchdiff.pointer import Pointer


def test_basic_list_insertion():
a = []
b = [1]
ops, rops = diff(a, b)

assert ops == [{"op": "add", "path": Pointer(["-"]), "value": 1}]
assert rops == [{"op": "remove", "path": Pointer([0])}]


def test_basic_list_deletion():
a = [1]
b = []
ops, rops = diff(a, b)

assert ops == [{"op": "remove", "path": Pointer([0])}]
assert rops == [{"op": "add", "path": Pointer(["-"]), "value": 1}]


def test_basic_list_insertion_half_way():
a = [1, 3]
b = [1, 2, 3]
ops, rops = diff(a, b)

assert ops == [{"op": "add", "path": Pointer([1]), "value": 2}]
assert rops == [{"op": "remove", "path": Pointer([1])}]


def test_basic_list_deletion_half_way():
a = [1, 2, 3]
b = [1, 3]
ops, rops = diff(a, b)

assert ops == [{"op": "remove", "path": Pointer([1])}]
assert rops == [{"op": "add", "path": Pointer([1]), "value": 2}]


def test_basic_list_multiple_insertion():
a = []
b = [1, 2, 3]
ops, rops = diff(a, b)

assert ops == [
{"op": "add", "path": Pointer(["-"]), "value": 1},
{"op": "add", "path": Pointer(["-"]), "value": 2},
{"op": "add", "path": Pointer(["-"]), "value": 3},
]
assert rops == [
{"op": "remove", "path": Pointer([0])},
{"op": "remove", "path": Pointer([0])},
{"op": "remove", "path": Pointer([0])},
]


def test_basic_list_multiple_deletion():
a = [1, 2, 3]
b = []
ops, rops = diff(a, b)

assert ops == [
{"op": "remove", "path": Pointer([0])},
{"op": "remove", "path": Pointer([0])},
{"op": "remove", "path": Pointer([0])},
]
assert rops == [
{"op": "add", "path": Pointer(["-"]), "value": 1},
{"op": "add", "path": Pointer(["-"]), "value": 2},
{"op": "add", "path": Pointer(["-"]), "value": 3},
]


def test_list():
a = [1, 5, 9, "sdfsdf", "fff"]
b = ["sdf", 5, 9, "c"]
Expand All @@ -13,28 +83,32 @@ def test_list():
{"op": "remove", "path": Pointer([4])},
]
assert rops == [
{"op": "replace", "path": Pointer([0]), "value": 1},
{"op": "replace", "path": Pointer([3]), "value": "sdfsdf"},
{"op": "add", "path": Pointer(["-"]), "value": "fff"},
{"op": "replace", "path": Pointer([4]), "value": "sdfsdf"},
{"op": "replace", "path": Pointer([1]), "value": 1},
]


def test_list_begin():
a = [1, 2]
b = [3, 1, 2]
ops, rops = diff(a, b)

assert ops == [{"op": "add", "path": Pointer([0]), "value": 3}]
assert rops == [{"op": "remove", "path": Pointer([0])}]


def test_list_end():
a = [1, 2, 3]
b = [1, 2, 3, 4]
ops, rops = diff(a, b)

assert ops == [
{"op": "add", "path": Pointer(["-"]), "value": 4},
]
assert ops == [{"op": "add", "path": Pointer(["-"]), "value": 4}]
assert rops == [{"op": "remove", "path": Pointer([3])}]


def test_dicts():
a = {
"a": 5,
"b": 6,
}
a = {"a": 5, "b": 6}
b = {"a": 3, "b": 6, "c": 7}
ops, rops = diff(a, b)

Expand Down Expand Up @@ -68,7 +142,11 @@ def test_mixed():
"a": [5, 7, 9, {"a", "b", "c"}],
"b": 6,
}
b = {"a": [5, 2, 9, {"b", "c"}], "b": 6, "c": 7}
b = {
"a": [5, 2, 9, {"b", "c"}],
"b": 6,
"c": 7,
}
ops, rops = diff(a, b)

assert ops == [
Expand All @@ -77,7 +155,7 @@ def test_mixed():
{"op": "remove", "path": Pointer(["a", 3, "a"])},
]
assert rops == [
{"op": "add", "path": Pointer(["a", 3, "-"]), "value": "a"},
{"op": "replace", "path": Pointer(["a", 1]), "value": 7},
{"op": "add", "path": Pointer(["a", 3, "-"]), "value": "a"},
{"op": "remove", "path": Pointer(["c"])},
]