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.2.0.tar.gz (162.1 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.2.0-py3-none-any.whl (12.8 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for apply_patch_py-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c16efaf0d64a0a45acb4ae0ff9a1611be713e6cc8ba3b6d01de29f62b80c4f69
MD5 b8b486d973a431df4340bd937e43f509
BLAKE2b-256 f44b49fa13ce9efa114afedc81460fd5a8704e8c6ed0d23c56c82633dcdadfde

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for apply_patch_py-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6f3a881ff4172c4ddf2c9a014ab394f27063b9093add8f9bc751cfcdb617f339
MD5 4494b03797c0187d90f028c3d1984ea2
BLAKE2b-256 f3ead249397e0cc8b10eb78904109d488eff40d9cc94c65d6e714c325e1a5f7d

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