Skip to main content

Apply Codex-style patch blocks (*** Begin Patch ... *** End Patch) to a file

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apply_patch_py-0.4.0.tar.gz (170.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apply_patch_py-0.4.0-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file apply_patch_py-0.4.0.tar.gz.

File metadata

  • Download URL: apply_patch_py-0.4.0.tar.gz
  • Upload date:
  • Size: 170.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.2

File hashes

Hashes for apply_patch_py-0.4.0.tar.gz
Algorithm Hash digest
SHA256 45ca547dfc883fe71726ef446e31dfbf041d232d3177c08937149b3b5483b494
MD5 5f6a8086b40e695f6738cffd909d26ac
BLAKE2b-256 e2191aa86be77253f278aeea8f037ab277d4ca05ba018a32c9ce88451086d15e

See more details on using hashes here.

File details

Details for the file apply_patch_py-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for apply_patch_py-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 aaaab7230ccf6ea4b3405947c60b0f4e9dc3676681af44b710b7333d74459b28
MD5 624de5f761c2a5797dd8aebe1a79f0ff
BLAKE2b-256 1d48a7ba0c4b58e218812c188b8c029d0e0d344624af3408b8fc78e8310ca69f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page