Skip to content

Commit d83520b

Browse files
committed
fix: update README files for improved clarity and add Chinese version
1 parent b1cc0da commit d83520b

File tree

3 files changed

+277
-4
lines changed

3 files changed

+277
-4
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# awepatch
22

3+
[English](https://github.com/fanck0605/awepatch/blob/main/README.md) | [中文](https://github.com/fanck0605/awepatch/blob/main/README_zh.md)
4+
35
**Awesome Patch** - A Python library for runtime function patching using AST manipulation.
46

57
[![Build Status](https://github.com/fanck0605/awepatch/workflows/Build/badge.svg)](https://github.com/fanck0605/awepatch/actions/workflows/build.yml)
@@ -8,18 +10,16 @@
810

911
## Overview
1012

11-
`awepatch` is a powerful Python library that allows you to dynamically patch callable objects at runtime by manipulating their Abstract Syntax Tree (AST). Unlike traditional monkey patching, `awepatch` modifies the actual code object of functions, providing a cleaner and more maintainable approach to runtime code modification.
13+
`awepatch` is a powerful Python library that allows you to dynamically patch source code at runtime by manipulating their Abstract Syntax Tree (AST). Unlike traditional monkey patching, `awepatch` modifies the actual code object of functions, providing a cleaner and more maintainable approach to runtime code modification.
1214

1315
## Features
1416

1517
- 🔧 **Runtime Function Patching**: Modify function behavior without changing source code
1618
- 🎯 **AST-Based Manipulation**: Clean and precise code modifications using AST
1719
- 🔄 **Automatic Restoration**: Context manager support for temporary patches
1820
- 🎭 **Multiple Patch Modes**: Insert code before, after, or replace existing statements
19-
- 📦 **Batch Patching**: Apply multiple patches to a function in a single call
2021
- 🧩 **Pattern Matching**: Use string, regex, or tuple patterns to locate code to patch
2122
- 🎯 **Nested Matching**: Target nested code blocks with tuple pattern syntax
22-
- 🔄 **Manual Control**: Apply and restore patches manually with `CallablePatcher`
2323
- 🔗 **Decorator Support**: Works with decorated functions, class methods, and static methods
2424
-**Type-Safe**: Full type hints support with strict type checking
2525

README_zh.md

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
# awepatch
2+
3+
[English](https://github.com/fanck0605/awepatch/blob/main/README.md) | [中文](https://github.com/fanck0605/awepatch/blob/main/README_zh.md)
4+
5+
**Awesome Patch** - 一个使用 AST 操作进行运行时函数补丁的 Python 库。
6+
7+
[![构建状态](https://github.com/fanck0605/awepatch/workflows/Build/badge.svg)](https://github.com/fanck0605/awepatch/actions/workflows/build.yml)
8+
[![Python 版本](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
9+
[![许可证](https://img.shields.io/github/license/fanck0605/awepatch)](https://github.com/fanck0605/awepatch/blob/main/LICENSE)
10+
11+
## 概述
12+
13+
`awepatch` 是一个强大的 Python 库,允许你通过操作抽象语法树(AST)在运行时动态修补源代码。与传统的猴子补丁不同,`awepatch` 修改的是函数的实际代码对象,提供了一种更清晰、更易维护的运行时代码修改方法。
14+
15+
## 特性
16+
17+
- 🔧 **运行时函数补丁**:无需更改源代码即可修改函数行为
18+
- 🎯 **基于 AST 的操作**:使用 AST 进行清晰而精确的代码修改
19+
- 🔄 **自动恢复**:支持上下文管理器进行临时补丁
20+
- 🎭 **多种补丁模式**:在现有语句之前、之后插入代码或替换语句
21+
- 🧩 **模式匹配**:使用字符串、正则表达式或元组模式定位要补丁的代码
22+
- 🎯 **嵌套匹配**:使用元组模式语法定位嵌套代码块
23+
- 🔗 **装饰器支持**:适用于装饰函数、类方法和静态方法
24+
-**类型安全**:完整的类型提示支持和严格的类型检查
25+
26+
## 安装
27+
28+
```bash
29+
pip install awepatch
30+
```
31+
32+
或使用 `uv`
33+
34+
```bash
35+
uv pip install awepatch
36+
```
37+
38+
## 快速开始
39+
40+
### 函数补丁
41+
42+
```python
43+
import re
44+
45+
from awepatch import FunctionPatcher
46+
47+
48+
def calculate(x: int, y: int) -> int:
49+
x = x + 10
50+
y = y * 2
51+
result = x + y
52+
return result
53+
54+
55+
patcher = FunctionPatcher()
56+
patcher.add_patch(
57+
calculate,
58+
target="x = x + 10",
59+
content="print(f'processing: {x=}')",
60+
mode="before",
61+
)
62+
patcher.add_patch(
63+
calculate,
64+
target="y = y * 2",
65+
content="y = y * 3",
66+
mode="replace",
67+
)
68+
patcher.add_patch(
69+
calculate,
70+
target=re.compile(r"result = x \+ y"),
71+
content="print(f'result: {result}')",
72+
mode="after",
73+
)
74+
with patcher:
75+
print(calculate(5, 10))
76+
77+
# 输出:
78+
# processing: x=5
79+
# result: 45
80+
# 45
81+
```
82+
83+
### 模块补丁
84+
85+
```python
86+
# foo.py
87+
from dataclasses import dataclass
88+
89+
@dataclass(slots=True)
90+
class User:
91+
name: str
92+
age: int
93+
94+
def greet(user: User) -> str:
95+
if hasattr(user, "gender"):
96+
return f"Hello, {user.name}! You are {user.age} years old. Your gender is {user.gender}."
97+
else:
98+
return f"Hello, {user.name}! You are {user.age} years old."
99+
100+
# example.py
101+
from awepatch.module import ModulePatcher
102+
103+
patcher = ModulePatcher()
104+
patcher.add_patch(
105+
"foo",
106+
target=(
107+
"class User:",
108+
"age: int",
109+
),
110+
content="""
111+
gender: str = "unspecified"
112+
""",
113+
mode="after",
114+
)
115+
with patcher:
116+
import foo
117+
user = foo.User(name="Bob", age=25)
118+
print(foo.greet(user))
119+
120+
# 输出: Hello, Bob! You are 25 years old. Your gender is unspecified.
121+
```
122+
123+
### 嵌套模式匹配
124+
125+
对于复杂的嵌套结构,你可以使用元组模式或行号偏移来匹配嵌套的 AST 节点:
126+
127+
```python
128+
from awepatch import FunctionPatcher
129+
from awepatch.utils import Ident
130+
131+
132+
def nested_function(x: int) -> int:
133+
if x > 0:
134+
x = x * 2
135+
x = x * 2
136+
return x
137+
138+
139+
# 匹配 if 块内的嵌套语句
140+
with FunctionPatcher().add_patch(
141+
nested_function,
142+
target=("if x > 0:", "x = x * 2"),
143+
content="x = x * 3",
144+
mode="replace",
145+
):
146+
print(nested_function(5)) # 输出: 30
147+
148+
149+
# 或通过行号偏移匹配
150+
with FunctionPatcher().add_patch(
151+
nested_function,
152+
target=Ident("x = x * 2", lineno="+2"),
153+
content="x = x * 3",
154+
mode="replace",
155+
):
156+
print(nested_function(5)) # 输出: 30
157+
```
158+
159+
## 高级用法
160+
161+
### 多进程应用补丁
162+
163+
对于生成多个进程的应用程序,你必须使用 `ModulePatcher` 来确保补丁在每个子进程中都被应用。
164+
165+
使用 `.pth` 文件在每个进程导入模块时自动应用补丁可能是一个不错的选择。
166+
167+
```python
168+
# loader.py
169+
patcher = ModulePatcher()
170+
patcher.add_patch(
171+
"foo",
172+
target=(
173+
"class User:",
174+
"age: int",
175+
),
176+
content="gender: str = 'unspecified'"
177+
)
178+
patcher.apply()
179+
180+
# xxx-awepatch.pth
181+
# xxx-awepatch.pth 必须放在 site-packages 目录中
182+
import loader
183+
```
184+
185+
参见:
186+
187+
- <https://github.com/tox-dev/pre-commit-uv/blob/main/src/pre_commit_uv_patch.pth>
188+
- <https://github.com/pypa/setuptools/blob/main/setup.py#L12>
189+
- <https://github.com/jawah/urllib3.future/blob/main/urllib3_future.pth>
190+
191+
## 使用场景
192+
193+
- **测试**:无需复杂的模拟框架即可模拟函数行为
194+
- **调试**:在运行时注入日志或调试代码
195+
- **热补丁**:无需重启应用程序即可应用修复或修改
196+
- **实验**:无需修改源文件即可快速测试代码更改
197+
- **仪器化**:动态添加监控或性能分析代码
198+
199+
## 限制
200+
201+
- Lambda 函数无法被补丁(它们缺少适当的源代码信息)
202+
- 函数必须通过 `inspect.getsourcelines()` 访问源代码
203+
- 模式匹配必须唯一标识函数中的目标语句
204+
- AST 中仅支持单个函数定义
205+
- 不允许冲突的补丁(例如,在同一目标上组合 'replace' 和 'before'/'after')
206+
207+
## 开发
208+
209+
### 设置开发环境
210+
211+
```bash
212+
# 克隆仓库
213+
git clone https://github.com/fanck0605/awepatch.git
214+
cd awepatch
215+
216+
# 安装开发依赖
217+
uv sync
218+
```
219+
220+
### 运行测试
221+
222+
```bash
223+
# 运行所有测试
224+
pytest
225+
226+
# 运行覆盖率测试
227+
pytest --cov=awepatch --cov-report=html
228+
229+
# 运行特定测试文件
230+
pytest tests/test_patch_callable.py
231+
```
232+
233+
### 代码质量
234+
235+
```bash
236+
# 格式化代码
237+
ruff format
238+
239+
# 检查代码
240+
ruff check
241+
242+
# 修复可自动修复的问题
243+
ruff check --fix
244+
```
245+
246+
## 贡献
247+
248+
欢迎贡献!请随时提交 Pull Request。对于重大更改,请先开启一个 issue 讨论你想要更改的内容。
249+
250+
1. Fork 仓库
251+
2. 创建你的特性分支 (`git checkout -b feature/amazing-feature`)
252+
3. 提交你的更改 (`git commit -m 'Add some amazing feature'`)
253+
4. 推送到分支 (`git push origin feature/amazing-feature`)
254+
5. 开启一个 Pull Request
255+
256+
## 许可证
257+
258+
本项目采用 MIT 许可证 - 详见 [LICENSE](https://github.com/fanck0605/awepatch/blob/main/LICENSE) 文件。
259+
260+
## 作者
261+
262+
**Chuck Fan** - [fanck0605@qq.com](mailto:fanck0605@qq.com)
263+
264+
## 致谢
265+
266+
- 受 Python 中更清晰的运行时代码修改需求启发
267+
- 使用现代 Python 工具和最佳实践构建
268+
269+
---
270+
271+
**注意**:此库在运行时修改函数代码对象。在生产环境中请谨慎使用,并务必充分测试。

src/awepatch/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING, Any, TypeAlias
3+
from typing import Any, TypeAlias
44

55
from awepatch._version import __commit_id__, __version__, __version_tuple__
66
from awepatch.function import FunctionPatcher
77
from awepatch.module import ModulePatcher
88
from awepatch.utils import AbstractPatcher, Ident, Patch
99

10+
TYPE_CHECKING = False
11+
1012
if TYPE_CHECKING:
1113
from collections.abc import Callable
1214

0 commit comments

Comments
 (0)