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.3.0.tar.gz (162.5 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.3.0-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for apply_patch_py-0.3.0.tar.gz
Algorithm Hash digest
SHA256 43a929a3b1199d40576b4c91e991c6d42bd3dfad023c2e6a32848ac4079c3715
MD5 22cb49d649f2a83354f0e2f82588a1dc
BLAKE2b-256 c1637e3dbbe2ad3e8632885b380e4779d29f37039be1a2e7fb1508a1feff1b42

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for apply_patch_py-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d9fddd4b0b67932ac39a1ce491cd073aa75a6a46ce53b65409dbd8200cd7f902
MD5 3a121191d67a5787266cebc40248539d
BLAKE2b-256 b41b21cd9fca9b84277442e21d781bf5d7a2fb9f507ac6a7ff00781a74e93e04

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