Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions jsonpatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,10 @@ def from_diff(
:param dst: Data target document object.
:type dst: dict

:param optimization: Generate move operations for matching values.
Set to False to use only add, remove and replace.
:type optimization: bool

:param dumps: A function of one argument that produces a serialized
JSON string.
:type dumps: Callable
Expand All @@ -614,7 +618,8 @@ def from_diff(
True
"""
json_dumper = dumps or cls.json_dumper
builder = DiffBuilder(src, dst, json_dumper, pointer_cls=pointer_cls)
builder = DiffBuilder(src, dst, json_dumper, pointer_cls=pointer_cls,
optimization=optimization)
builder._compare_values((), None, src, dst)
ops = list(builder.execute())
return cls(ops, pointer_cls=pointer_cls)
Expand Down Expand Up @@ -670,9 +675,11 @@ def _get_operation(self, operation):

class DiffBuilder(object):

def __init__(self, src_doc, dst_doc, dumps=json.dumps, pointer_cls=JsonPointer):
def __init__(self, src_doc, dst_doc, dumps=json.dumps, pointer_cls=JsonPointer,
optimization=True):
self.dumps = dumps
self.pointer_cls = pointer_cls
self.optimization = optimization
self.index_storage = [{}, {}]
self.__root = root = []
self.src_doc = src_doc
Expand All @@ -693,10 +700,14 @@ def _move_key(self, value):
return id(value)

def store_index(self, value, index, st):
if not self.optimization:
return
storage = self.index_storage[st]
storage.setdefault(self._move_key(value), []).append(index)

def take_index(self, value, st):
if not self.optimization:
return None
stored = self.index_storage[st].get(self._move_key(value))
if stored:
return stored.pop()
Expand Down
9 changes: 9 additions & 0 deletions property_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,15 @@ def test_diff_of_equal_documents_is_empty(self, doc):
def test_roundtrip(self, docs):
self.check_roundtrip(*docs)

@given(doc_pairs)
def test_roundtrip_without_move_optimization(self, docs):
src, dst = docs
patch = jsonpatch.JsonPatch.from_diff(src, dst, optimization=False)
note(patch.patch)
self.assertTrue(all(op['op'] in ('add', 'remove', 'replace')
for op in patch))
assert_json_equal(patch.apply(src), dst)

@given(safe_doc_pairs)
# the diff considered e.g. 1 and true equal, #180
@example(docs=([0], [False]))
Expand Down
19 changes: 19 additions & 0 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@

class ApplyPatchTestCase(unittest.TestCase):

def test_diff_without_optimization_uses_basic_operations(self):
documents = [
({'old': {'value': 1}}, {'new': {'value': 1}}),
([1, 2, 3], [3, 1, 2]),
([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}]),
({'a/b': [1, 2]}, {'~key': [1, 2]}),
]
for src, dst in documents:
with self.subTest(src=src, dst=dst):
original = copy.deepcopy(src)
patch = jsonpatch.JsonPatch.from_diff(
src, dst, optimization=False)
self.assertTrue(all(operation['op'] in
('add', 'remove', 'replace')
for operation in patch))
self.assertEqual(patch.apply(src), dst)
self.assertEqual(src, original)


def test_js_file(self):
with open('./tests.js', 'r') as f:
tests = json.load(f)
Expand Down
Loading