Skip to content

Commit 1405f6e

Browse files
committed
Fix: optimization bugs stefankoegl#55, stefankoegl#54
1 parent dd03e13 commit 1405f6e

File tree

2 files changed

+91
-58
lines changed

2 files changed

+91
-58
lines changed

jsonpatch.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,7 @@ def apply(self, obj):
563563

564564
def _compare_lists(path, src, dst):
565565
"""Compares two lists objects and return JSON patch about."""
566-
# TODO: commented while optimization has bugs
567-
# return _optimize(_compare(path, src, dst, *_split_by_common_seq(src, dst)))
568-
return _compare(path, src, dst, *_split_by_common_seq(src, dst))
566+
return _optimize(_compare(path, src, dst, *_split_by_common_seq(src, dst)))
569567

570568

571569
def _longest_common_subseq(src, dst):
@@ -770,9 +768,17 @@ def _optimize_using_replace(prev, cur):
770768
For nested strucures, tries to recurse replacement, see #36 """
771769
prev['op'] = 'replace'
772770
if cur['op'] == 'add':
773-
# make recursive patch
771+
# check case when dict "remove" is less than "add" and has a same key
774772
patch = make_patch(prev['value'], cur['value'])
775-
if len(patch.patch) == 1 and patch.patch[0]['op'] != 'remove':
773+
if isinstance(prev['value'], dict) and isinstance(cur['value'], dict) and len(prev['value'].keys()) == 1:
774+
prev_set = set(prev['value'].keys())
775+
cur_set = set(cur['value'].keys())
776+
if prev_set & cur_set == prev_set:
777+
patch = make_patch(cur['value'], prev['value'])
778+
779+
if len(patch.patch) == 1 and \
780+
patch.patch[0]['op'] != 'remove' and \
781+
patch.patch[0]['path'] and patch.patch[0]['path'].split('/')[1] in prev['value']:
776782
prev['path'] = prev['path'] + patch.patch[0]['path']
777783
prev['value'] = patch.patch[0]['value']
778784
else:

tests.py

Lines changed: 80 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -369,58 +369,85 @@ def test_issue40(self):
369369
patch = jsonpatch.make_patch(src, dest)
370370

371371

372-
# class OptimizationTests(unittest.TestCase):
373-
# def test_use_replace_instead_of_remove_add(self):
374-
# src = {'foo': [1, 2, 3]}
375-
# dst = {'foo': [3, 2, 3]}
376-
# patch = list(jsonpatch.make_patch(src, dst))
377-
# self.assertEqual(len(patch), 1)
378-
# self.assertEqual(patch[0]['op'], 'replace')
379-
# res = jsonpatch.apply_patch(src, patch)
380-
# self.assertEqual(res, dst)
381-
382-
# def test_use_replace_instead_of_remove_add_nested(self):
383-
# src = {'foo': [{'bar': 1, 'baz': 2}, {'bar': 2, 'baz': 3}]}
384-
# dst = {'foo': [{'bar': 1}, {'bar': 2, 'baz': 3}]}
385-
# patch = list(jsonpatch.make_patch(src, dst))
386-
# self.assertEqual(len(patch), 1)
387-
# self.assertEqual(patch[0]['op'], 'replace')
388-
# res = jsonpatch.apply_patch(src, patch)
389-
# self.assertEqual(res, dst)
390-
391-
# def test_use_move_instead_of_remove_add(self):
392-
# src = {'foo': [4, 1, 2, 3]}
393-
# dst = {'foo': [1, 2, 3, 4]}
394-
# patch = list(jsonpatch.make_patch(src, dst))
395-
# self.assertEqual(len(patch), 1)
396-
# self.assertEqual(patch[0]['op'], 'move')
397-
# res = jsonpatch.apply_patch(src, patch)
398-
# self.assertEqual(res, dst)
399-
400-
# def test_use_move_instead_of_add_remove(self):
401-
# src = {'foo': [1, 2, 3]}
402-
# dst = {'foo': [3, 1, 2]}
403-
# patch = list(jsonpatch.make_patch(src, dst))
404-
# self.assertEqual(len(patch), 1)
405-
# self.assertEqual(patch[0]['op'], 'move')
406-
# res = jsonpatch.apply_patch(src, patch)
407-
# self.assertEqual(res, dst)
408-
409-
# def test_minimal_patch(self):
410-
# """ Test whether a minimal patch is created, see #36 """
411-
# src = [{"foo": 1, "bar": 2}]
412-
# dst = [{"foo": 2, "bar": 2}]
413-
# patch = jsonpatch.make_patch(src, dst)
414-
415-
# exp = [
416-
# {
417-
# "path": "/0/foo",
418-
# "value": 2,
419-
# "op": "replace"
420-
# }
421-
# ]
422-
423-
# self.assertEqual(patch.patch, exp)
372+
class OptimizationTests(unittest.TestCase):
373+
def test_use_replace_instead_of_remove_add(self):
374+
src = {'foo': [1, 2, 3]}
375+
dst = {'foo': [3, 2, 3]}
376+
patch = list(jsonpatch.make_patch(src, dst))
377+
self.assertEqual(len(patch), 1)
378+
self.assertEqual(patch[0]['op'], 'replace')
379+
res = jsonpatch.apply_patch(src, patch)
380+
self.assertEqual(res, dst)
381+
382+
def test_use_replace_instead_of_remove_add_nested(self):
383+
src = {'foo': [{'bar': 1, 'baz': 2}, {'bar': 2, 'baz': 3}]}
384+
dst = {'foo': [{'bar': 1}, {'bar': 2, 'baz': 3}]}
385+
patch = list(jsonpatch.make_patch(src, dst))
386+
self.assertEqual(len(patch), 1)
387+
self.assertEqual(patch[0]['op'], 'replace')
388+
res = jsonpatch.apply_patch(src, patch)
389+
self.assertEqual(res, dst)
390+
391+
def test_use_move_instead_of_remove_add(self):
392+
src = {'foo': [4, 1, 2, 3]}
393+
dst = {'foo': [1, 2, 3, 4]}
394+
patch = list(jsonpatch.make_patch(src, dst))
395+
self.assertEqual(len(patch), 1)
396+
self.assertEqual(patch[0]['op'], 'move')
397+
res = jsonpatch.apply_patch(src, patch)
398+
self.assertEqual(res, dst)
399+
400+
def test_use_move_instead_of_add_remove(self):
401+
src = {'foo': [1, 2, 3]}
402+
dst = {'foo': [3, 1, 2]}
403+
patch = list(jsonpatch.make_patch(src, dst))
404+
self.assertEqual(len(patch), 1)
405+
self.assertEqual(patch[0]['op'], 'move')
406+
res = jsonpatch.apply_patch(src, patch)
407+
self.assertEqual(res, dst)
408+
409+
def test_minimal_patch(self):
410+
""" Test whether a minimal patch is created, see #36 """
411+
src = [{'a': 1, 'foo': {'b': 2, 'd': 5}}]
412+
dst = [{'a': 1, 'foo': {'b': 3, 'd': 6}}]
413+
patch = jsonpatch.make_patch(src, dst)
414+
self.assertEqual(patch.apply(src), dst)
415+
416+
src = [{'a': 1, 'b': 2, 'd': 5}]
417+
dst = [{'a': 1, 'c': 3, 'd': 5}]
418+
patch = jsonpatch.make_patch(src, dst)
419+
self.assertEqual(patch.apply(src), dst)
420+
421+
src = [{'a': 1, 'b': 2, 'd': 5}]
422+
dst = [{'d': 6}]
423+
patch = jsonpatch.make_patch(src, dst)
424+
self.assertEqual(patch.apply(src), dst)
425+
426+
src = [{'a': 1}, {'b': 2}]
427+
dst = [{'a': 1, 'b': 2}]
428+
patch = jsonpatch.make_patch(src, dst)
429+
self.assertEqual(patch.apply(src), dst)
430+
431+
src = [{"a": 1, "b": 2}]
432+
dst = [{"b": 2, "c": 2}]
433+
exp = [{u'path': u'/0', u'value': {u'c': 2, u'b': 2}, u'op': u'replace'}]
434+
patch = jsonpatch.make_patch(src, dst)
435+
self.assertEqual(patch.patch, exp)
436+
437+
src = [{"foo": 1, "bar": 2}]
438+
dst = [{"foo": 2, "bar": 2}]
439+
440+
patch = jsonpatch.make_patch(src, dst)
441+
442+
exp = [
443+
{
444+
"path": "/0/foo",
445+
"value": 2,
446+
"op": "replace"
447+
}
448+
]
449+
450+
self.assertEqual(patch.patch, exp)
424451

425452

426453
class InvalidInputTests(unittest.TestCase):
@@ -489,7 +516,7 @@ def get_suite():
489516
suite.addTest(unittest.makeSuite(MakePatchTestCase))
490517
suite.addTest(unittest.makeSuite(InvalidInputTests))
491518
suite.addTest(unittest.makeSuite(ConflictTests))
492-
# suite.addTest(unittest.makeSuite(OptimizationTests))
519+
suite.addTest(unittest.makeSuite(OptimizationTests))
493520
return suite
494521

495522

0 commit comments

Comments
 (0)