Recursive diff for nested structures
Project description
Nested-Diff.py
Recursive diff and patch for nested structures Live Demo
Install
pip install nested_diff
For extra formats support (YAML, TOML) in cli tools, use
pip install nested_diff[cli]
Command line tools
mixas:~/$ cat a.json b.json
[0, [1], 3]
[0, [1, 2], 3]
mixas:~/$ nested_diff a.json b.json
[1]
+ [1]
+ 2
mixas:~/$
mixas:~/$ nested_diff a.json b.json --ofmt json > patch.json
mixas:~/$ nested_patch a.json patch.json
Library usage
>>> from nested_diff import diff, patch
>>>
>>> a = {'one': 1, 'two': 2, 'three': 3}
>>> b = {'one': 1, 'two': 42}
>>>
>>> diff(a, b)
{'D': {'three': {'R': 3}, 'two': {'N': 42, 'O': 2}, 'one': {'U': 1}}}
>>>
>>> diff(a, b, O=False, U=False)
{'D': {'three': {'R': 3}, 'two': {'N': 42}}}
>>>
>>>
>>> c = [0,1,2,3]
>>> d = [ 1,2,4,5]
>>>
>>> c = patch(c, diff(c, d))
>>> assert c == d
>>>
Formatting diffs
>>> from nested_diff import diff
>>> from nested_diff.fmt import TextFormatter
>>>
>>> a = {'one': 1, 'two': 'some\ntext\ninside'}
>>> b = {'one': 0, 'two': 'some\ntext'}
>>>
>>> d = diff(a, b, U=False, text_diff_ctx=3)
>>> print(TextFormatter().format(d))
{'two'}
# <str>
@@ -1,3 +1,2 @@
some
text
- inside
{'one'}
- 1
+ 0
<BLANKLINE>
>>>
Subclassing
from nested_diff import Differ
class CustomDiffer(Differ):
"""Differ with custom precision for floats."""
def __init__(self, float_precision=2, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_differ(float, self.diff_float)
self.float_precision = float_precision
def diff_float(self, a, b):
if round(a, self.float_precision) == round(b, self.float_precision):
return {'U': a} if self.op_u else {}
return super().diff__default(a, b)
differ = CustomDiffer(float_precision=1, U=False)
a = [0.001, 0.01, 0.1]
b = [0.002, 0.02, 0.2]
assert {'D': [{'I': 2, 'N': 0.2, 'O': 0.1}]} == differ.diff(a, b)
Diff structure
Diff is a dict and may contain following keys:
A
stands for 'added', it's value - added item.C
is for comments; optional, value - arbitrary string.D
means 'different' and contains subdiff.E
diffed entity (optional), value - empty instance of entity's class.I
index for sequence item, used only when prior item was omitted.N
is a new value for changed item.O
is a changed item's old value.R
key used for removed item.U
represent unchanged item.
Diff metadata alternates with actual data; simple types specified as is, dicts,
lists and tuples contain subdiffs for their items with native for such types
addressing: indexes for lists and tuples and keys for dictionaries. Any status
type, except D
, E
and I
may be omitted during diff computation. E
tag
is used with D
when entity unable to contain diff by itself (set, frozenset);
D
contain a list of subdiffs in this case.
Annotated example:
a: {"one": [5,7]}
b: {"one": [5], "two": 2}
opts: U=False # omit unchanged items
diff:
{"D": {"one": {"D": [{"I": 1, "R": 7}]}, "two": {"A": 2}}}
| | | | | | || | | | | | | | |
| | | | | | || | | | | | | | +- with value 2
| | | | | | || | | | | | | +- key 'two' was added
| | | | | | || | | | | | +- subdiff for it
| | | | | | || | | | | +- another key from top-level
| | | | | | || | | | +- what it was (item's value: 7)
| | | | | | || | | +- what happened to item (removed)
| | | | | | || | +- list item's actual index
| | | | | | || +- prior item was omitted
| | | | | | |+- subdiff for list item
| | | | | | +- it's value - list
| | | | | +- it is deeply changed
| | | | +- subdiff for key 'one'
| | | +- it has key 'one'
| | +- top-level thing is a dict
| +- changes somewhere deeply inside
+- diff is always a dict
License
Licensed under the terms of the Apache License, Version 2.0.
See Also
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for nested_diff-0.9-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e0c60bd2c681ccd880b0d1f56599d2dced5f0026fe4136366cb15af673a9eb85 |
|
MD5 | e2d45b8d838752a5d689580afd3e33f8 |
|
BLAKE2b-256 | 33a24d72a1b285c27990bec91eb71467c68f48f103e6b4e9564ecbd2df6e0230 |