Skip to main content

purepatch

CI PyPI Python License: MIT

The patch engine for code agents, in pure Python. Apply unified diffs and fuzzy search/replace edits with no git, no patch binary, no C extension — in sandboxes, Pyodide/WASM, Lambda, anywhere pip install works. And because it runs in-process, it applies a patch in ~25 µs where spawning a binary costs milliseconds.

pip install purepatch
import purepatch

new_text = purepatch.apply(diff_text, old_text)          # unified diff -> text
report   = purepatch.apply_files(diff_text, root=".")    # multi-file patch
new_text = purepatch.apply_edit(text, search, replace)   # fuzzy block edit
purepatch --dry-run < change.patch     # the familiar CLI, agent-friendly
purepatch -R < change.patch            # un-apply

Why

LLMs edit code by emitting unified diffs and SEARCH/REPLACE blocks — and both arrive slightly wrong: line numbers drifted, context rotted, indentation moved, trailing whitespace differs. The existing Python options either only parse diffs (unidiff) or are long abandoned (python-patch, last release 2019). So every agent framework re-implements patching, badly, or shells out to git.

purepatch is that missing engine:

  • GNU patch semantics for unified diffs: cumulative offset tracking, bidirectional position search, fuzz degradation — verified against the real thing (below).
  • A fuzzy edit ladder for LLM edit blocks: exact match → trailing whitespace tolerance → indentation transplant (the block the model wrote at top level gets re-indented to where it actually lives). Refuses to guess on ambiguity.
  • Errors an agent can act on: failed matches report the closest near-miss (closest match: line 41, 87% similar) so the model can correct its edit instead of retrying blind.
  • Git extended headers understood: new/deleted files, renames, quoted paths, \ No newline at end of file, CRLF content.

Verified against GNU patch and git apply

Following the pure* series methodology: behavior is checked by differential testing against the reference implementations, run in CI on every commit —

  • 500 random clean patches: purepatch ≡ GNU patch ≡ git apply ≡ expected output, byte for byte;
  • 200 drift scenarios (the file gained unrelated lines): offset behavior matches GNU patch exactly;
  • 200 rotted-context scenarios: fuzz behavior matches GNU patch's output wherever GNU patch succeeds;
  • 300 property cases: apply(diff(a,b), a) == b and apply(diff(a,b), b, reverse=True) == a.

Performance

Per-application latency — how a code agent actually uses a patcher: one patch at a time. Spawn cost is the binaries' real cost; in-process is purepatch's real cost. Median of 7, three independent rounds (spread <10%), outputs verified equal before timing. Reproduce: python tools/bench.py --verify.

workload purepatch (in-process) GNU patch (spawn) git apply (spawn)
200-line file, 5 edits 0.021 ms 2.6 ms (~120×) 7.8 ms (~370×)
2k-line file, 30 edits 0.17 ms 2.9 ms (17×) 8.8 ms (52×)
20k-line file, 200 edits 1.4 ms 6.0 ms (4.3×) 16.2 ms (12×)

Fuzzy apply_edit on a 400-line file: ~0.01 ms per call.

The slow paths are engineered too, because an agent waits on them: hunk placement uses tiered C-speed anchor scans (a patch drifted by 500 lines in a 20k-line file still applies in ~1.5 ms), and a failed fuzzy edit produces its closest-match diagnostics on a 10k-line file in ~10 ms (two-pass candidate scoring; 21× faster than naively diffing every window).

An agent loop applying hundreds of edits per session pays milliseconds total, not seconds — and needs no git in its sandbox.

API sketch

purepatch.parse(text) -> PatchSet               # inspect hunks/files
purepatch.apply(patch, source, reverse=False, max_fuzz=2) -> str
purepatch.apply_files(patch, root=".", strip=None,  # strip auto-detected
                      reverse=False, dry_run=False) -> ApplyReport
purepatch.apply_edit(content, search, replace) -> str
purepatch.find_block(content, search) -> (start, end, strategy)

ApplyReport.ok, per-file actions (patched/created/deleted/renamed/ failed), and per-hunk offset/fuzz are all inspectable — log them and an agent can explain exactly what happened.

Exceptions: ParseError, HunkApplyError, NoMatchError (with closest_line / closest_similarity), AmbiguousMatchError (with all locations).

Limitations (honest ones)

  • Binary patches are rejected, not applied.
  • File modes are parsed from git headers but not applied to the filesystem (chmod is on the roadmap).
  • purepatch the CLI covers the agent subset (-p -d -R --fuzz --dry-run), not every GNU patch flag.
  • Like GNU patch, fuzzy hunk placement can in principle pick a wrong spot in pathological inputs; --fuzz 0 disables tolerance entirely.

License

MIT

Release files for purepatch 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for purepatch 0.1.1
File Size Uploaded
purepatch-0.1.1.tar.gz 23.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for purepatch 0.1.1
File Interpreter ABI Platform
purepatch-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size:42.5 kB

Release files / purepatch-0.1.1.tar.gz

Download URL purepatch-0.1.1.tar.gz
Size 23.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6ef21e3adf0479caa58e5938700c186f98a0fe2857c3b4b5a568c58aa8d19048
BLAKE2b-256 checksum
How to use checksums
1166f3e7df43f9914541b07c1bfeb91448f5a7d8ffa45f727ed81dfb746aac69
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release files / purepatch-0.1.1-py3-none-any.whl

Download URL purepatch-0.1.1-py3-none-any.whl
Size 19.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2ca06da8107bce752357821120d0f9224b0cd312c8229266c0bc43cc03730fcd
BLAKE2b-256 checksum
How to use checksums
2adff4d984f588b1313231b3d7fda72730e247036386ef31cad9cc645a18bddd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page