Skip to content

Instantly share code, notes, and snippets.

@brosner
Last active May 18, 2016 12:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save brosner/842f6c32a958784c037a14cd35a6f3c5 to your computer and use it in GitHub Desktop.
Save brosner/842f6c32a958784c037a14cd35a6f3c5 to your computer and use it in GitHub Desktop.
from itertools import chain
empty = object()
def lookup_merge_strategy(tv, path):
pass
def are_equal_types(*iterables):
types = chain(*(map(type, x) for x in iterables))
try:
first = next(types)
return all(first == rest for rest in types)
except StopIteration:
return True
def diff_list_of_dicts():
pass
def diff_list_of_scalars(original, modified):
if not modified:
# There is no need to check the length of original because there is no way to create
# a patch that deletes a scalar from a list of scalars with merge semantics.
return []
patch = []
def diff_list(original, modified, merge_key, ignore_mods, ignore_dels):
if not original:
if not modified or ignore_mods:
return []
return modified
assert are_equal_types(original, modified), "not equal types"
if isinstance(original[0], dict):
return diff_list_of_dicts(original, modified, merge_key, ignore_mods, ignore_dels)
elif not ignore_mods:
return diff_list_of_scalars(original, modified)
return []
def diff_dict(original, modified, tv, ignore_mods, ignore_dels, path=""):
patch = {}
for k, mv in modified.items():
path = path + "." + k if path else k
ov = original.get(k, empty)
if ov is empty:
# key was added, so add to patch
if not ignore_mods:
patch[k] = mv
continue
if k == "$patch":
assert all([isinstance(ov, str), isinstance(mv, str)]), "invalid value for special key '{}'".format(k)
if mv != ov:
patch["$patch"] = mv
if isinstance(ov, dict):
pv = diff_dict(ov, mv, ignore_mods, ignore_dels)
if pv:
patch[k] = pv
continue
if isinstance(ov, list):
ms = lookup_merge_strategy(tv, path)
if ms is not None:
if ms.patch_strategy == "merge":
pv = diff_list(ov, mv, ms.merge_key, ignore_mods, ignore_dels)
if pv:
patch[k] = pv
continue
if not ignore_mods:
if ov != mv:
# values are different, so add to patch
patch[k] = mv
if not ignore_dels:
for k in original:
if k not in modified:
patch[k] = None
return patch
def merge_dict():
pass
def create_three_way_merge_patch(original, modified, current, tv):
# The patch is the difference from current to modified without deletions, plus deletions
# from original to modified. To find it, we compute deletions, which are the deletions from
# original to modified, and delta, which is the difference from current to modified without
# deletions, and then apply delta to deletions as a patch, which should be strictly additive.
delta = diff_dict(current, modified, tv, False, True)
deletions = diff_dict(original, modified, tv, True, False)
patch = merge_dict(deletions, delta, tv)
return patch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment