Move make_path logic to JsonPatch.from_diff class method. Some docstring fixes.
This commit is contained in:
172
jsonpatch.py
172
jsonpatch.py
@@ -99,7 +99,8 @@ def apply_patch(doc, patch, in_place=False):
|
|||||||
return patch.apply(doc, in_place)
|
return patch.apply(doc, in_place)
|
||||||
|
|
||||||
def make_patch(src, dst):
|
def make_patch(src, dst):
|
||||||
"""Generates patch by comparing of two document objects.
|
"""Generates patch by comparing of two document objects. Actually is
|
||||||
|
a proxy to :meth:`JsonPatch.from_diff` method.
|
||||||
|
|
||||||
:param src: Data source document object.
|
:param src: Data source document object.
|
||||||
:type src: dict
|
:type src: dict
|
||||||
@@ -114,6 +115,100 @@ def make_patch(src, dst):
|
|||||||
>>> new == dst
|
>>> new == dst
|
||||||
True
|
True
|
||||||
"""
|
"""
|
||||||
|
return JsonPatch.from_diff(src, dst)
|
||||||
|
|
||||||
|
|
||||||
|
class JsonPatch(object):
|
||||||
|
"""A JSON Patch is a list of Patch Operations.
|
||||||
|
|
||||||
|
>>> patch = JsonPatch([
|
||||||
|
... {'add': '/foo', 'value': 'bar'},
|
||||||
|
... {'add': '/baz', 'value': [1, 2, 3]},
|
||||||
|
... {'remove': '/baz/1'},
|
||||||
|
... {'test': '/baz', 'value': [1, 3]},
|
||||||
|
... {'replace': '/baz/0', 'value': 42},
|
||||||
|
... {'remove': '/baz/1'},
|
||||||
|
... ])
|
||||||
|
>>> doc = {}
|
||||||
|
>>> patch.apply(doc)
|
||||||
|
{'foo': 'bar', 'baz': [42]}
|
||||||
|
|
||||||
|
JsonPatch object is iterable, so you could easily access to each patch
|
||||||
|
statement in loop:
|
||||||
|
|
||||||
|
>>> lpatch = list(patch)
|
||||||
|
>>> lpatch[0]
|
||||||
|
{'add': '/foo', 'value': 'bar'}
|
||||||
|
>>> lpatch == patch.patch
|
||||||
|
True
|
||||||
|
|
||||||
|
Also JsonPatch could be converted directly to bool if it contains any
|
||||||
|
operation statements:
|
||||||
|
>>> bool(patch)
|
||||||
|
True
|
||||||
|
>>> bool(JsonPatch([]))
|
||||||
|
False
|
||||||
|
|
||||||
|
This behavior is very handy with :func:`make_patch` to write more readable
|
||||||
|
code:
|
||||||
|
>>> old = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
|
||||||
|
>>> new = {'baz': 'qux', 'numbers': [1, 4, 7]}
|
||||||
|
>>> patch = make_patch(old, new)
|
||||||
|
>>> if patch:
|
||||||
|
... # document have changed, do something useful
|
||||||
|
... patch.apply(old) #doctest: +ELLIPSIS
|
||||||
|
{...}
|
||||||
|
"""
|
||||||
|
def __init__(self, patch):
|
||||||
|
self.patch = patch
|
||||||
|
|
||||||
|
self.operations = {
|
||||||
|
'remove': RemoveOperation,
|
||||||
|
'add': AddOperation,
|
||||||
|
'replace': ReplaceOperation,
|
||||||
|
'move': MoveOperation,
|
||||||
|
'test': TestOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""str(self) -> self.to_string()"""
|
||||||
|
return self.to_string()
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self.patch)
|
||||||
|
|
||||||
|
__nonzero__ = __bool__
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.patch)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, patch_str):
|
||||||
|
"""Creates JsonPatch instance from string source."""
|
||||||
|
patch = json.loads(patch_str)
|
||||||
|
return cls(patch)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_diff(cls, src, dst):
|
||||||
|
"""Creates JsonPatch instance based on comparing of two document
|
||||||
|
objects. Json patch would be created for `src` argument against `dst`
|
||||||
|
one.
|
||||||
|
|
||||||
|
:param src: Data source document object.
|
||||||
|
:type src: dict
|
||||||
|
|
||||||
|
:param dst: Data source document object.
|
||||||
|
:type dst: dict
|
||||||
|
|
||||||
|
:return: :class:`JsonPatch` instance.
|
||||||
|
|
||||||
|
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
|
||||||
|
>>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]}
|
||||||
|
>>> patch = JsonPatch.from_diff(src, dst)
|
||||||
|
>>> new = patch.apply(src)
|
||||||
|
>>> new == dst
|
||||||
|
True
|
||||||
|
"""
|
||||||
def compare_values(path, value, other):
|
def compare_values(path, value, other):
|
||||||
if isinstance(value, dict) and isinstance(other, dict):
|
if isinstance(value, dict) and isinstance(other, dict):
|
||||||
for operation in compare_dict(path, value, other):
|
for operation in compare_dict(path, value, other):
|
||||||
@@ -149,80 +244,7 @@ def make_patch(src, dst):
|
|||||||
elif idx < lsrc:
|
elif idx < lsrc:
|
||||||
yield {'remove': '/'.join(path + [str(idx)])}
|
yield {'remove': '/'.join(path + [str(idx)])}
|
||||||
|
|
||||||
return JsonPatch(list(compare_dict([''], src, dst)))
|
return cls(list(compare_dict([''], src, dst)))
|
||||||
|
|
||||||
|
|
||||||
class JsonPatch(object):
|
|
||||||
"""A JSON Patch is a list of Patch Operations.
|
|
||||||
|
|
||||||
>>> patch = JsonPatch([
|
|
||||||
... {'add': '/foo', 'value': 'bar'},
|
|
||||||
... {'add': '/baz', 'value': [1, 2, 3]},
|
|
||||||
... {'remove': '/baz/1'},
|
|
||||||
... {'test': '/baz', 'value': [1, 3]},
|
|
||||||
... {'replace': '/baz/0', 'value': 42},
|
|
||||||
... {'remove': '/baz/1'},
|
|
||||||
... ])
|
|
||||||
>>> doc = {}
|
|
||||||
>>> patch.apply(doc)
|
|
||||||
{'foo': 'bar', 'baz': [42]}
|
|
||||||
|
|
||||||
JsonPatch object is iterable, so you could easily access to each patch
|
|
||||||
statement in loop:
|
|
||||||
|
|
||||||
>>> lpatch = list(patch)
|
|
||||||
>>> lpatch[0]
|
|
||||||
{'add': '/foo', 'value': 'bar'}
|
|
||||||
|
|
||||||
Also JsonPatch could be converted directly to bool if it contains any
|
|
||||||
statements:
|
|
||||||
>>> bool(patch)
|
|
||||||
True
|
|
||||||
>>> bool(JsonPatch([]))
|
|
||||||
False
|
|
||||||
|
|
||||||
This behavior is very handy with :func:`make_patch` to write more readable
|
|
||||||
code:
|
|
||||||
>>> src = {'foo': 'bar', 'numbers': [1, 3, 4, 8]}
|
|
||||||
>>> dst = {'baz': 'qux', 'numbers': [1, 4, 7]}
|
|
||||||
>>> patch = make_patch(src, dst)
|
|
||||||
>>> if patch:
|
|
||||||
... # document have changed, do something useful
|
|
||||||
... pass
|
|
||||||
|
|
||||||
Instead of:
|
|
||||||
>>> if patch.patch:
|
|
||||||
... pass
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, patch):
|
|
||||||
self.patch = patch
|
|
||||||
|
|
||||||
self.operations = {
|
|
||||||
'remove': RemoveOperation,
|
|
||||||
'add': AddOperation,
|
|
||||||
'replace': ReplaceOperation,
|
|
||||||
'move': MoveOperation,
|
|
||||||
'test': TestOperation
|
|
||||||
}
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""str(self) -> self.to_string()"""
|
|
||||||
return self.to_string()
|
|
||||||
|
|
||||||
def __bool__(self):
|
|
||||||
return bool(self.patch)
|
|
||||||
|
|
||||||
__nonzero__ = __bool__
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(self.patch)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_string(cls, patch_str):
|
|
||||||
"""Creates JsonPatch instance from string source."""
|
|
||||||
patch = json.loads(patch_str)
|
|
||||||
return cls(patch)
|
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
"""Returns patch set as JSON string."""
|
"""Returns patch set as JSON string."""
|
||||||
|
|||||||
Reference in New Issue
Block a user