Patchdiff 🔍
Based on rfc6902 this library provides a simple API to generate bi-directional diffs between composite Python data structures composed out of lists, sets, tuples and dicts. The diffs are JSON-patch compliant, and can optionally be serialized to JSON format. Patchdiff has no dependencies and works on Python 3.9 and up.
A single call to diff gives you the patches in both directions:
from patchdiff import apply, diff
input = {"a": [5, 7, 9, {"a", "b", "c"}], "b": 6}
output = {"a": [5, 2, 9, {"b", "c"}], "b": 6, "c": 7}
ops, reverse_ops = diff(input, output)
assert apply(input, ops) == output
assert apply(output, reverse_ops) == input
Having both directions makes it easy to build undo/redo, to synchronize state between processes (send patches over the wire instead of whole documents), or to keep a log of exactly what changed.
As an alternative to diffing, patchdiff can also record patches while mutations are being made, using a proxy mechanism like Immer. See produce:
from patchdiff import produce
base = {"count": 0, "items": [1, 2, 3]}
def recipe(draft):
draft["count"] = 5
draft["items"].append(4)
result, patches, reverse_patches = produce(base, recipe)
assert base == {"count": 0, "items": [1, 2, 3]} # base is untouched
assert result == {"count": 5, "items": [1, 2, 3, 4]}
Where to start
- The quick start walks through the core API.
- The guide covers diffing, applying patches, pointers, produce, serialization and gotchas in more detail.
- If you use observ, have a look at observ integration.
- The complete public API is documented in the API reference.
- The internals page describes how everything works under the hood.