Move make_path logic to JsonPatch.from_diff class method. Some docstring fixes.

This commit is contained in:
Alexander Shorin
2012-06-21 02:04:36 +04:00
parent 495fa43116
commit 9f11add937

View File

@@ -99,7 +99,8 @@ def apply_patch(doc, patch, in_place=False):
return patch.apply(doc, in_place)
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.
:type src: dict
@@ -114,6 +115,100 @@ def make_patch(src, dst):
>>> new == dst
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):
if isinstance(value, dict) and isinstance(other, dict):
for operation in compare_dict(path, value, other):
@@ -149,80 +244,7 @@ def make_patch(src, dst):
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.
>>> 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)
return cls(list(compare_dict([''], src, dst)))
def to_string(self):
"""Returns patch set as JSON string."""