A library for runtime Python bytecode patching.
  • Python 98.4%
  • Nix 1%
  • Shell 0.6%
Find a file
Rustam Efimov 76df8e8e3c
All checks were successful
CI / test (push) Successful in 1m11s
Publish Package / build-and-publish (push) Successful in 19s
initial commit
2026-09-26 19:07:34 +03:00
.forgejo/workflows initial commit 2026-09-26 19:07:34 +03:00
bpatch initial commit 2026-09-26 19:07:34 +03:00
scripts initial commit 2026-09-26 19:07:34 +03:00
tests initial commit 2026-09-26 19:07:34 +03:00
.gitignore initial commit 2026-09-26 19:07:34 +03:00
.python-version initial commit 2026-09-26 19:07:34 +03:00
flake.lock initial commit 2026-09-26 19:07:34 +03:00
flake.nix initial commit 2026-09-26 19:07:34 +03:00
LICENSE initial commit 2026-09-26 19:07:34 +03:00
pyproject.toml initial commit 2026-09-26 19:07:34 +03:00
README.md initial commit 2026-09-26 19:07:34 +03:00
uv.lock initial commit 2026-09-26 19:07:34 +03:00

bpatch

A library for runtime Python bytecode patching.

Usage

Before patches take effect, you must call manager.apply_all() to apply the registered patches.

Prefix and Postfix (InjectPatcher)

The prefix and postfix decorators provide a high-level way to inject code before or after a target function's execution.

Prefix

A prefix hook runs before the target function. It must return a FlowType indicating whether to continue execution or return early.

from bpatch.patch import Flow, FlowType, manager, prefix

def add(x: int, y: int) -> int:
    return x + y

@prefix(add)
def before_add(x: int, y: int) -> FlowType[int]:
    if x < 0:
        return (Flow.RETURN, -1)
    return (Flow.CONTINUE,)

manager.apply_all()

assert add(5, 7) == 12
assert add(-5, 7) == -1

Postfix

A postfix hook runs after the target function and can modify its return value.

from bpatch.patch import manager, postfix

def multiply(x: int, y: int) -> int:
    return x * y

@postfix(multiply)
def after_multiply(result: int) -> int:
    return result * 2

manager.apply_all()

assert multiply(5, 5) == 50

Transform (BytecodePatcher)

The transform decorator and BytecodePatcher allow for fine-grained manipulation of bytecode instructions. You can use matchers to replace, insert, or remove instructions.

from bpatch.dis import Bytecode, Instruction, Opcode
from bpatch.patch import BytecodePatcher, manager, transform

def target_func() -> int:
    x = 42
    return x

def find_return(_ctx: Bytecode, inst: Instruction) -> bool:
    return inst.opcode == Opcode.RETURN_VALUE

nop = Instruction(Opcode.NOP, 0)
patcher = BytecodePatcher().replace(find_return, nop)

@transform(target_func, patcher=patcher)
def _patch(_p: BytecodePatcher) -> None:
    pass

manager.apply_all()

Inline Labels

You can place inline_label inside a function to mark specific points in the bytecode, making it easier to target with BytecodePatcher using strings instead of custom finder functions.

from bpatch.dis import Instruction, Opcode
from bpatch.patch import BytecodePatcher, inline_label, manager, transform

def labeled_func() -> int:
    inline_label("my_label")
    return 1

nop = Instruction(Opcode.NOP, 0)
patcher = BytecodePatcher().replace("my_label", nop)

@transform(labeled_func, patcher=patcher)
def _patch(_p: BytecodePatcher) -> None:
    pass

manager.apply_all()

Executor

Executor allows you to trace and step through the bytecode execution of a function. The underlying implementation automatically selects the optimal backend for your Python version (MonitoringExecutor for 3.12+ or TraceExecutor for older versions).

You can use the executor to manually step through execution or yield instructions as they are executed.

from bpatch.dis import Bytecode
from bpatch.executor import Executor

def simple_func() -> int:
    x = 100
    return x

bc = Bytecode.from_callable(simple_func)

# Method 1: Iterating over instructions as they execute
for inst in bc.exec():
    print(inst.opcode)

# Method 2: Manual stepping
exec_instance = Executor(bc)
while exec_instance.step():
    if exec_instance.current_instruction:
        print(exec_instance.current_instruction.opcode)