Skip to content

Commit 2cb66b9

Browse files
committed
Add generate_test_ops flag
1 parent 73c36f2 commit 2cb66b9

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

bin/jsondiff

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ parser.add_argument('-u', '--preserve-unicode', action='store_true',
1818
help='Output Unicode character as-is without using Code Point')
1919
parser.add_argument('-v', '--version', action='version',
2020
version='%(prog)s ' + jsonpatch.__version__)
21+
parser.add_argument('-t', '--test-ops', action='store_true',
22+
help='Generate before-state test ops for remove/replace')
2123

2224

2325
def main():
@@ -32,7 +34,7 @@ def diff_files():
3234
args = parser.parse_args()
3335
doc1 = json.load(args.FILE1)
3436
doc2 = json.load(args.FILE2)
35-
patch = jsonpatch.make_patch(doc1, doc2)
37+
patch = jsonpatch.make_patch(doc1, doc2, generate_test_ops=args.test_ops)
3638
if patch.patch:
3739
print(json.dumps(patch.patch, indent=args.indent, ensure_ascii=not(args.preserve_unicode)))
3840
sys.exit(1)

jsonpatch.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def apply_patch(doc, patch, in_place=False, pointer_cls=JsonPointer):
157157
return patch.apply(doc, in_place)
158158

159159

160-
def make_patch(src, dst, pointer_cls=JsonPointer):
160+
def make_patch(src, dst, generate_test_ops=False, pointer_cls=JsonPointer):
161161
"""Generates patch by comparing two document objects. Actually is
162162
a proxy to :meth:`JsonPatch.from_diff` method.
163163
@@ -170,6 +170,12 @@ def make_patch(src, dst, pointer_cls=JsonPointer):
170170
:param pointer_cls: JSON pointer class to use.
171171
:type pointer_cls: Type[JsonPointer]
172172
173+
:param generate_test_ops: While :const:`True` generate test operations
174+
capturing previous values of `replace`/`remove`
175+
operations. By default do not generate these
176+
operations.
177+
:type generate_test_ops: bool
178+
173179
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
174180
>>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]}
175181
>>> patch = make_patch(src, dst)
@@ -178,7 +184,7 @@ def make_patch(src, dst, pointer_cls=JsonPointer):
178184
True
179185
"""
180186

181-
return JsonPatch.from_diff(src, dst, pointer_cls=pointer_cls)
187+
return JsonPatch.from_diff(src, dst, generate_test_ops=generate_test_ops, pointer_cls=pointer_cls)
182188

183189

184190
class PatchOperation(object):
@@ -475,6 +481,11 @@ def apply(self, obj):
475481

476482
return obj
477483

484+
def _on_undo_add(self, path, key):
485+
return key
486+
487+
def _on_undo_remove(self, path, key):
488+
return key
478489

479490
class CopyOperation(PatchOperation):
480491
""" Copies an object property or an array element to a new location """
@@ -628,7 +639,7 @@ def from_string(cls, patch_str, loads=None, pointer_cls=JsonPointer):
628639

629640
@classmethod
630641
def from_diff(
631-
cls, src, dst, optimization=True, dumps=None,
642+
cls, src, dst, generate_test_ops=False, dumps=None,
632643
pointer_cls=JsonPointer,
633644
):
634645
"""Creates JsonPatch instance based on comparison of two document
@@ -641,6 +652,12 @@ def from_diff(
641652
:param dst: Data source document object.
642653
:type dst: dict
643654
655+
:param generate_test_ops: While :const:`True` generate test operations
656+
capturing previous values of `replace`/`remove`
657+
operations. By default do not generate these
658+
operations.
659+
:type generate_test_ops: bool
660+
644661
:param dumps: A function of one argument that produces a serialized
645662
JSON string.
646663
:type dumps: function
@@ -658,7 +675,7 @@ def from_diff(
658675
True
659676
"""
660677
json_dumper = dumps or cls.json_dumper
661-
builder = DiffBuilder(src, dst, json_dumper, pointer_cls=pointer_cls)
678+
builder = DiffBuilder(src, dst, json_dumper, generate_test_ops=generate_test_ops, pointer_cls=pointer_cls)
662679
builder._compare_values('', None, src, dst)
663680
ops = list(builder.execute())
664681
return cls(ops, pointer_cls=pointer_cls)
@@ -711,9 +728,10 @@ def _get_operation(self, operation):
711728

712729
class DiffBuilder(object):
713730

714-
def __init__(self, src_doc, dst_doc, dumps=json.dumps, pointer_cls=JsonPointer):
731+
def __init__(self, src_doc, dst_doc, dumps=json.dumps, generate_test_ops=False, pointer_cls=JsonPointer):
715732
self.dumps = dumps
716733
self.pointer_cls = pointer_cls
734+
self.generate_test_ops = generate_test_ops
717735
self.index_storage = [{}, {}]
718736
self.index_storage2 = [[], []]
719737
self.__root = root = []
@@ -819,6 +837,13 @@ def _item_added(self, path, key, item):
819837
self.store_index(item, new_index, _ST_ADD)
820838

821839
def _item_removed(self, path, key, item):
840+
if self.generate_test_ops:
841+
test_index = self.insert(TestOperation({
842+
'op': 'test',
843+
'path': _path_join(path, key),
844+
'value': item,
845+
}, pointer_cls=self.pointer_cls))
846+
822847
new_op = RemoveOperation({
823848
'op': 'remove',
824849
'path': _path_join(path, key),
@@ -847,11 +872,20 @@ def _item_removed(self, path, key, item):
847872

848873
else:
849874
self.remove(new_index)
875+
if self.generate_test_ops:
876+
self.remove(test_index)
850877

851878
else:
852879
self.store_index(item, new_index, _ST_REMOVE)
853880

854-
def _item_replaced(self, path, key, item):
881+
def _item_replaced(self, path, key, item, old_item):
882+
if self.generate_test_ops:
883+
self.insert(TestOperation({
884+
'op': 'test',
885+
'path': _path_join(path, key),
886+
'value': old_item,
887+
}, pointer_cls=self.pointer_cls))
888+
855889
self.insert(ReplaceOperation({
856890
'op': 'replace',
857891
'path': _path_join(path, key),
@@ -921,7 +955,7 @@ def _compare_values(self, path, key, src, dst):
921955
return
922956

923957
else:
924-
self._item_replaced(path, key, dst)
958+
self._item_replaced(path, key, dst, src)
925959

926960

927961
def _path_join(path, key):

0 commit comments

Comments
 (0)