Bidirectional, JSON-patch-compliant diffs between Python data structures
Project description
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.
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
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file patchdiff-1.0.0.tar.gz.
File metadata
- Download URL: patchdiff-1.0.0.tar.gz
- Upload date:
- Size: 76.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b79b89d96d15bcbb58ea6df7c86fe91c62daf56837892b8d873800f1718352c
|
|
| MD5 |
84d630cd9f2e7633389ab5fec9f3f309
|
|
| BLAKE2b-256 |
9719b085b46a112dfcbf915a99197eb6d359842edc5a8383b30fb33bf6668caf
|
Provenance
The following attestation bundles were made for patchdiff-1.0.0.tar.gz:
Publisher:
ci.yml on fork-tongue/patchdiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
patchdiff-1.0.0.tar.gz -
Subject digest:
5b79b89d96d15bcbb58ea6df7c86fe91c62daf56837892b8d873800f1718352c - Sigstore transparency entry: 2167356368
- Sigstore integration time:
-
Permalink:
fork-tongue/patchdiff@9aaaa21c15352296226ca7a1cd9eeb2502e2bd04 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/fork-tongue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9aaaa21c15352296226ca7a1cd9eeb2502e2bd04 -
Trigger Event:
push
-
Statement type:
File details
Details for the file patchdiff-1.0.0-py3-none-any.whl.
File metadata
- Download URL: patchdiff-1.0.0-py3-none-any.whl
- Upload date:
- Size: 21.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2f9bc78aa262e01900ca03e67a0247ff8a12d51e988ba9b384ec21e34dc56fe
|
|
| MD5 |
e750f637c5a9808ce59c97d8f00ec8ca
|
|
| BLAKE2b-256 |
fd1d0f536221b0c385c417f53858d55a194cc0643fb4e5a77b030fab8025333a
|
Provenance
The following attestation bundles were made for patchdiff-1.0.0-py3-none-any.whl:
Publisher:
ci.yml on fork-tongue/patchdiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
patchdiff-1.0.0-py3-none-any.whl -
Subject digest:
e2f9bc78aa262e01900ca03e67a0247ff8a12d51e988ba9b384ec21e34dc56fe - Sigstore transparency entry: 2167356385
- Sigstore integration time:
-
Permalink:
fork-tongue/patchdiff@9aaaa21c15352296226ca7a1cd9eeb2502e2bd04 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/fork-tongue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@9aaaa21c15352296226ca7a1cd9eeb2502e2bd04 -
Trigger Event:
push
-
Statement type: