RFC 6902 JSON patches with jsonpatch + RFC 6901 pointers with jsonpointer: working versions (tested 2026-10-17)
Create, apply and hand-roll the inverse of RFC 6902 JSON patches with jsonpatch, and resolve or set RFC 6901 pointers (including ~0 and ~1 escapes) with jsonpointer.
passed6 passed · 0 failed2026-10-17 00:54 UTC- Status
- passed
- Last passed
- 2026-10-17 00:54 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions3
jsonpatch@1.35.0jsonpointer@3.2.1pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38988196687
The code
Exactly the files that passed. Your agent can pull them with get_recipe("jsonpatch-jsonpointer-apply") (install), or see them on GitHub.
requirements.txt
jsonpatch==1.35.0 jsonpointer==3.2.1 pytest==9.1.1
patching.py
"""Create, apply and invert RFC 6902 patches over RFC 6901 pointers."""
import copy
import jsonpatch
import jsonpointer
def make_patch(before, after):
"""Create the patch that turns before into after."""
return jsonpatch.make_patch(before, after)
def apply(document, patch, in_place=False):
"""Apply a patch; jsonpatch returns a copy unless in_place is set."""
return jsonpatch.JsonPatch(patch).apply(document, in_place=in_place)
def invert(document, patch):
"""Build the inverse patch; RFC 6902 does not define one.
Replay the patch over a scratch copy to capture the values it
overwrites, then emit inverse operations in reverse order.
"""
scratch = copy.deepcopy(document)
inverse = []
for op in patch:
kind, path = op["op"], op["path"]
if kind == "add":
inverse.append({"op": "remove", "path": path})
elif kind in ("remove", "replace"):
previous = jsonpointer.resolve_pointer(scratch, path)
inverse.append(
{
"op": "add" if kind == "remove" else "replace",
"path": path,
"value": previous,
}
)
else:
raise jsonpatch.JsonPatchException(f"cannot invert {kind!r}")
scratch = jsonpatch.apply_patch(scratch, [op])
inverse.reverse()
return inverse
def read(document, pointer):
"""Resolve an RFC 6901 pointer, decoding ~1 (slash) and ~0 (tilde)."""
return jsonpointer.resolve_pointer(document, pointer)
def write(document, pointer, value, in_place=True):
"""Set a pointer target; '-' appends. jsonpointer 3.x defaults to in place."""
return jsonpointer.set_pointer(document, pointer, value, inplace=in_place)
tests/test_patching.py
import jsonpatch
import pytest
from patching import apply, invert, make_patch, read, write
def test_make_patch_and_apply_return_copy():
before = {"name": "old", "tags": ["a"]}
after = {"name": "new", "tags": ["a", "b"]}
patch = make_patch(before, after)
assert apply(before, patch) == after
assert before == {"name": "old", "tags": ["a"]}
def test_apply_in_place_mutates_original():
document = {"name": "old"}
patch = [{"op": "replace", "path": "/name", "value": "new"}]
result = apply(document, patch, in_place=True)
assert result is document
assert document == {"name": "new"}
def test_invert_round_trips_and_keeps_removed_values():
before = {"flag": True, "meta": {"tags": ["x", "y"], "note": "keep"}}
patch = [
{"op": "replace", "path": "/flag", "value": False},
{"op": "add", "path": "/meta/tags/1", "value": "z"},
{"op": "remove", "path": "/meta/note"},
]
after = apply(before, patch)
inverse = invert(before, patch)
assert {"op": "add", "path": "/meta/note", "value": "keep"} in inverse
assert apply(after, inverse) == before
def test_pointer_escapes_and_set_pointer_default_is_in_place():
document = {"a/b": {"m~n": 1}, "xs": [1, 2]}
assert read(document, "/a~1b/m~0n") == 1
write(document, "/xs/-", 3)
assert document["xs"] == [1, 2, 3]
def test_set_pointer_can_copy():
document = {"xs": [1, 2]}
updated = write(document, "/xs/-", 3, in_place=False)
assert document == {"xs": [1, 2]}
assert updated == {"xs": [1, 2, 3]}
def test_test_op_failure_and_missing_path_conflict():
with pytest.raises(jsonpatch.JsonPatchTestFailed):
apply({"count": 1}, [{"op": "test", "path": "/count", "value": 2}])
with pytest.raises(jsonpatch.JsonPatchConflict):
apply({}, [{"op": "replace", "path": "/missing", "value": 1}])
pytest.ini
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.