Last active
May 18, 2016 12:43
-
-
Save brosner/842f6c32a958784c037a14cd35a6f3c5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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