Skip to content

fork-tongue/patchdiff

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

90 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

PyPI version CI status

Patchdiff ๐Ÿ”

Bidirectional, JSON-patch-compliant diffs between Python data structures.

๐Ÿ“– Documentation | Quick Start | API Reference

Patchdiff diffs composite structures of dicts, lists, sets and tuples, and gives you both directions in one call: the patches to get from input to output, and the patches to get back. Patches are RFC 6902 JSON-patch style, serializable to JSON, and can be applied in place or to a copy, which makes undo/redo, change synchronization and state auditing one-liners.

from patchdiff import apply, diff, iapply, to_json

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          # patch a copy...
assert apply(output, reverse_ops) == input  # ...and it round-trips

iapply(input, ops)  # or patch in place
assert input == output

print(to_json(ops, indent=4))

Proxy based patch generation

When your own code makes the changes, produce() (inspired by Immer) skips the comparison entirely: it hands your recipe a draft, records every mutation, and returns the result plus both patch directions. Cost scales with the number of mutations instead of the size of the state:

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]}

With in_place=True, mutations (and patches applied with iapply) write straight through proxy-backed state, the natural companion to observ reactive objects, where mutating through the proxy is what triggers watchers. See Observ Integration for reactive state with undo/redo.

Install

pip install patchdiff  # or: uv add patchdiff

No dependencies, Python >= 3.9.

Learn more

The documentation covers diffing semantics, applying patches, JSON pointers, proxy-based patch generation, serialization and the gotchas, plus the internals for the curious.

About

Patchdiff ๐Ÿ” Bidirectional patch diffs

Resources

Stars

7 stars

Watchers

3 watching

Forks

Contributors

Languages