Skip to main content

apply-patch-py

Apply Codex-style patch blocks to a working directory.

This project is heavily inspired by (and effectively a Python port of) the patching mechanism used by Codex. Some tools (including Codex and opencode) already emit this patch format; this implementation aims to be more forgiving and will try harder to apply slightly malformed patches by using whitespace/normalization fallbacks and an additional “anchor line” fallback.

The CLI exists and is useful for quick manual runs, but the primary intent is to use this package as the patch-application backend for agent tools and MCP


apply-patch-py consumes patch text shaped like:

*** Begin Patch
*** Update File: path/to/file.txt
@@
-old line
+new line
*** End Patch

Supported operations:

  • *** Add File: <path>
  • *** Delete File: <path>
  • *** Update File: <path> (optionally with *** Move to: <new_path>)

LLMs sometimes emit patches that are “almost correct” but fail strict application due to:

  • whitespace drift
  • minor punctuation differences (Unicode quotes/dashes)
  • slightly malformed hunks

This library tries strict matching first (OpenAI models will match almost everytime, strictly), then progressively relaxes how it matches context lines:

  1. Exact match
  2. Right-stripped match (rstrip)
  3. Trimmed match (strip)
  4. Normalized match (Unicode punctuation normalization + whitespace normalization)

Install

From PyPI:

uv add apply-patch-py

Or run without installing:

uvx apply-patch-py "*** Begin Patch
*** End Patch"

PydanticAI tool Example

You can try it from examples folder:

# 1) Clone and run the example
git clone https://github.com/marcius-llmus/apply-patch-py
cd apply-patch-py

# Provide your LLM key (example: OpenAI)
export OPENAI_API_KEY="sk-proj-..."

# Run the example with uv
uv run examples/pydantic_example/pydantic_example.py

Then you can start asking edits to files inside example_repo folder.

For example:

Request> Create a new file named "notes/hello.txt" with the content:
hello from the patch tool

or

Request> Remove content "xyz" from existing_file.txt 123

or

Request> Edit file xyz, remove the middle block.

Direct usage

You can also call the library directly:

import asyncio

from apply_patch_py import apply_patch

patch = """\
*** Begin Patch
*** Add File: hello.txt
+hello
*** End Patch
"""

async def main() -> None:
    affected = await apply_patch(patch)
    assert affected.success

asyncio.run(main())

CLI usage

Apply a patch provided as a command-line argument:

apply-patch-py "*** Begin Patch
*** Add File: hello.txt
+hello\n
*** End Patch"

Apply a patch from stdin:

cat patch.txt | apply-patch-py

The CLI prints a summary of affected files:

Success. Updated the following files:
A hello.txt
M existing.txt
D obsolete.txt

Add a file

*** Begin Patch
*** Add File: nested/new.txt
+created
*** End Patch

Delete a file

*** Begin Patch
*** Delete File: obsolete.txt
*** End Patch

Update a file (single hunk)

*** Begin Patch
*** Update File: modify.txt
@@
-line2
+changed
*** End Patch

Update a file (multiple hunks)

*** Begin Patch
*** Update File: multi.txt
@@
-line2
+changed2
@@
-line4
+changed4
*** End Patch

Rename/move a file while updating

*** Begin Patch
*** Update File: old/name.txt
*** Move to: renamed/dir/name.txt
@@
-old content
+new content
*** End Patch

Run tests

uv run pytest

Integration tests (LLM providers)

This repo also contains integration tests that validate patch generation via real LLM providers. They are skipped by default unless explicitly selected:

uv run pytest -m integration

See tests/integration/ for provider configuration.


Notes

  • The patch format and workflow are directly inspired by OpenAI Codex diff patching.
  • Some other tools (e.g. opencode) emit the same format.
  • This project is essentially a port from their rust patcher with a few changes to improve success rates on imperfect LLM output.

License

MIT. See LICENSE.

Metadata

Release files for apply-patch-py 0.4.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 apply-patch-py 0.4.1
File Size Uploaded Provenance
apply_patch_py-0.4.1.tar.gz 171.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for apply-patch-py 0.4.1
File Interpreter ABI Platform
apply_patch_py-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 186.7 kB

Release files / apply_patch_py-0.4.1.tar.gz

Download URL apply_patch_py-0.4.1.tar.gz
Size 171.1 kB
Tags Source
SHA-256 checksum
How to use checksums
8962aa19d12a2b48a92175f7c42e9ce90c54e3d9bcc39210549487d549c36d20
BLAKE2b-256 checksum
How to use checksums
3cad5b1cc95dc6e005b52c8f9f53fd02a34569bbce80ad3ca140e2f19f900a9c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / apply_patch_py-0.4.1-py3-none-any.whl

Download URL apply_patch_py-0.4.1-py3-none-any.whl
Size 15.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c66abf766051a527a6e3242d7ca157a070fbaf1c1c22701a78f347da3c0b5838
BLAKE2b-256 checksum
How to use checksums
9646bdb2ec889f639b5129c6424fc6b2fea7e3d3c22243e3bb7537a43d048df2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for apply-patch-py 0.4.1

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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