Add make_patch function to generate JsonPatch by comparing of two documents.

This commit is contained in:
Alexander Shorin
2011-12-25 21:32:06 +04:00
parent a1729beaa1
commit d20e3a6f25
2 changed files with 77 additions and 0 deletions

View File

@@ -70,6 +70,54 @@ def apply_patch(doc, patch):
patch = JsonPatch(patch)
return patch.apply(doc)
def make_patch(src, dst):
"""Generates patch by comparing of two objects.
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
>>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]}
>>> patch = make_patch(src, dst)
>>> patch.apply(src) #doctest: +ELLIPSIS
{...}
>>> src == dst
True
"""
def compare_values(path, value, other):
if isinstance(value, dict) and isinstance(other, dict):
for operation in compare_dict(path, value, other):
yield operation
elif isinstance(value, list) and isinstance(other, list):
for operation in compare_list(path, value, other):
yield operation
else:
yield {'replace': '/'.join(path), 'value': other}
def compare_dict(path, src, dst):
for key in src:
if key not in dst:
yield {'remove': '/'.join(path + [key])}
elif src[key] != dst[key]:
current = path + [key]
for operation in compare_values(current, src[key], dst[key]):
yield operation
for key in dst:
if key not in src:
yield {'add': '/'.join(path + [key]), 'value': dst[key]}
def compare_list(path, src, dst):
lsrc, ldst = len(src), len(dst)
for idx in reversed(xrange(max(lsrc, ldst))):
if idx < lsrc and idx < ldst:
current = path + [str(idx)]
for operation in compare_values(current, src[idx], dst[idx]):
yield operation
elif idx < ldst:
yield {'add': '/'.join(path + [str(idx)]),
'value': dst[idx]}
elif idx < lsrc:
yield {'remove': '/'.join(path + [str(idx)])}
return JsonPatch(list(compare_dict([''], src, dst)))
class JsonPatch(object):
"""A JSON Patch is a list of Patch Operations.

View File

@@ -63,10 +63,39 @@ class ApplyPatchTestCase(unittest.TestCase):
obj, [{'test': '/bar', 'value': 'bar'}])
class MakePatchTestCase(unittest.TestCase):
def test_objects(self):
src = {'foo': 'bar', 'boo': 'qux'}
dst = {'baz': 'qux', 'foo': 'boo'}
patch = jsonpatch.make_patch(src, dst)
patch.apply(src)
self.assertEqual(src, dst)
def test_arrays(self):
src = {'numbers': [1, 2, 3], 'other': [1, 3, 4, 5]}
dst = {'numbers': [1, 3, 4, 5], 'other': [1, 3, 4]}
patch = jsonpatch.make_patch(src, dst)
patch.apply(src)
self.assertEqual(src, dst)
def test_complex_object(self):
src = {'data': [
{'foo': 1}, {'bar': [1, 2, 3]}, {'baz': {'1': 1, '2': 2}}
]}
dst = {'data': [
{'foo': [42]}, {'bar': []}, {'baz': {'boo': 'oom!'}}
]}
patch = jsonpatch.make_patch(src, dst)
patch.apply(src)
self.assertEqual(src, dst)
def suite():
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite(jsonpatch))
suite.addTest(unittest.makeSuite(ApplyPatchTestCase))
suite.addTest(unittest.makeSuite(MakePatchTestCase))
return suite
if __name__ == '__main__':