RustPython 标准库同步检查清单:基于 scripts/checklist_template.md 的 CPython Lib 对齐追踪机制
2026/9/13 9:57:39 网站建设 项目流程

RustPython 标准库同步检查清单:基于 scripts/checklist_template.md 的 CPython Lib 对齐追踪机制

【免费下载链接】RustPythonA Python Interpreter written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/RustPython

RustPython 作为用 Rust 实现的 Python 解释器,其Lib/目录中的纯 Python 标准库直接复用并同步自 CPython。scripts/checklist_template.md正是这一同步工程的"作战地图"——它是一份 Jinja2 模板,由scripts/generate_checklist.py驱动,自动生成一份逐库、逐测试的对齐进度清单(含复选框与关联 PR 编号),用于追踪"哪些标准库模块已从 CPython 迁移、哪些尚未添加、哪些配套测试仍然缺失"。读完本文,你将理解这套清单的生成原理、四个分类板块的含义、判定"已完成/未完成"的源码级逻辑,以及 RustPython 维护者围绕它搭建的整套update_lib工具链。

一、清单要解决什么问题:RustPython 与 CPython 的 Lib 同步

RustPython 的架构中,解释器核心(crates/vm)用 Rust 实现,而大量标准库则以纯 Python 形式存放在仓库根目录的 Lib/ 下(例如Lib/json/Lib/email/Lib/asyncio/Lib/pathlib/等),这些文件绝大多数直接源自 CPython 的Lib/目录。

这种设计带来了一个持续的工程问题:CPython 的Lib/随版本不断演进,RustPython 需要回答三个问题——

  1. 哪些模块已经拷贝过来了?
  2. 哪些模块在 RustPython 中尚不存在(un-added)?
  3. 每个已同步模块对应的 CPython 测试(test_*.py)是否也同步了过来、并且没有与上游产生 diff?

checklist_template.md就是为回答这三个问题而设计的输出模板。它本身不含具体数据,而是通过 Jinja2 宏与循环,把generate_checklist.py计算出的数据渲染成一份人类可读、机器可解析的 Markdown 进度清单。

二、模板结构逐行解析:四个板块与一个宏

scripts/checklist_template.md全文仅 21 行,结构非常精炼。其骨架如下:

{% macro display_line(i) %}- {% if i.completed == True %}[x] {% elif i.completed == False %}[ ] {% endif %}{{ i.name }}{% if i.pr != None %} {{ i.pr }}{% endif %}{% endmacro %} # List of libraries {% for lib in update_libs %}{{ display_line(lib) }} {% endfor %} # List of un-added libraries These libraries are not added yet. Pure python one will be possible while others are not. {% for lib in add_libs %}{{ display_line(lib) }} {% endfor %} # List of tests without python libraries {% for lib in update_tests %}{{ display_line(lib) }} {% endfor %} # List of un-added tests without python libraries {% for lib in add_tests %}{{ display_line(lib) }} {% endfor %}

2.1 核心宏display_line:清单行的渲染规则

模板开头的display_line(i)宏定义了每一行清单条目的渲染规则,它接收一个数据对象i,输出形如- [x] json #1234的 Markdown 行。逐段拆解其逻辑:

  • -:Markdown 无序列表前缀;
  • {% if i.completed == True %}[x] {% elif i.completed == False %}[ ] {% endif %}:依据completed字段三态输出——True输出勾选[x]False输出未勾选[ ]注意completedNone(对应"已由 PR 完成但本地产物尚未同步"的场景)时两个分支都不命中,不输出任何复选框,这正好对应源码中Output.completedOptional[bool]的设计(详见 scripts/generate_checklist.py);
  • {{ i.name }}:模块或测试的名称(如jsontest_abc),源码中Output.namestr
  • {% if i.pr != None %} {{ i.pr }}{% endif %}:若关联了 PR 编号(如#5736),追加输出,便于维护者回溯到具体合并请求。

2.2 四个数据板块的含义

模板通过 Jinja2 循环遍历四组列表,分别渲染四个板块:

板块标题遍历变量含义典型条目
# List of librariesupdate_libsCPythonLib/已存在于 RustPython的模块/文件- [x] json- [ ] abc
# List of un-added librariesadd_libsCPythonLib/尚未添加到 RustPython 的模块- [ ] tomllib
# List of tests without python librariesupdate_testsCPythonLib/test/已存在于 RustPython的测试文件- [ ] test_ast
# List of un-added tests without python librariesadd_testsCPythonLib/test/尚未添加的测试文件- [ ] test_zipfile

注意 "un-added" 板块上方还有一句说明:"These libraries are not added yet. Pure python one will be possible while others are not."——即未添加的库中,纯 Python 实现的模块未来可以移植,而依赖 C 扩展的模块则暂时不可行。这一判断对应了 RustPython 生态的现实约束:Rust 侧只有用纯 Python 或 Rust 原生重写的能力,无法直接链接 CPython 的 C 扩展。

三、数据从哪来:generate_checklist.py 的完整数据流

模板本身只是"壳",真正的计算逻辑全部在 scripts/generate_checklist.py 中。整条流水线可以分为五个阶段。

3.1 命令行参数

脚本使用argparse定义了两个参数:

  • --cpython(必填,pathlib.Path):指向 CPython 源码根目录的路径,用于读取其Lib/Lib/test/作为比对基准;
  • --notes(可选,pathlib.Path):指向备注文件(即仓库中的 scripts/notes.txt),用于把模块与相关说明、关联 issue 关联起来。

启动后脚本还会做路径校验:--cpython必须存在且为目录,若不是绝对路径则用.resolve()转为绝对路径(见 scripts/generate_checklist.py);而 RustPython 侧的Lib/则通过pathlib.Path(__file__).parent.parent / "Lib"定位——即与脚本所在目录(scripts/)平级的仓库根目录下的Lib/,这保证了脚本无论从何处执行都能找到正确的本地库目录。

3.2 已更新库的状态来源:GitHub issue #5736

脚本通过两个 GitHub REST API 调用获取"哪些库已被 PR 更新过":

  • get_updated_libs()拉取issue #5736的正文(scripts/generate_checklist.py),issue 正文中每行形如- libname #PR编号的列表被解析为dict[str, LibUpdate]
  • check_pr(pr_id)对每个 PR 编号调用/pulls/{id}接口,检查merged_at字段是否为None,从而判断该 PR 是否已合并(scripts/generate_checklist.py)。

LibUpdate是一个仅含两个字段的 dataclass:pr(关联 PR 编号,可空)与done(是否已合并,默认True)。issue 正文行的解析规则是:去掉-前缀后按空格切分,每行最多两个 token(模块名 + 可选 PR 号),多余则触发断言失败(见 scripts/generate_checklist.py)。

3.3 Lib 与 test 的枚举规则

脚本遍历 CPython 的Lib/Lib/test/两个目录,遵循严格的过滤规则:

  • 顶层目录与*.py文件都会被计入libs列表,但显式忽略__pycache__test(常量ignored_objs,见 scripts/generate_checklist.py);
  • Lib/test/下的条目,仅收录名称以test_开头、且不是已有对应库的测试(即test_foo.pyfoo不在libs中时才会进入tests列表)——这正是模板中 "tests without python libraries"(没有对应 Python 库的测试)这一标题的由来。例如test_ast.py对应库ast已存在,就不会被算作"无库测试"。

3.4 完成度判定:difflib 统一 diff 作为"是否同步"的硬标准

对每个模块/测试,脚本用difflib.unified_diff逐行比较 RustPython 与 CPython 的对应文件,若 diff 行数大于 0 或读取出现UnicodeDecodeError,即判定为"未完成"(见check_diff,scripts/generate_checklist.py)。两条核心判定函数:

  • check_lib_completion(rustpython_path, cpython_path):一个库只有在它自己的文件与 CPython 完全一致、且它对应的test_<库名>.py也完整同步时才算完成(scripts/generate_checklist.py)——库与其测试是捆绑验收的;
  • check_test_completion:测试文件必须同时存在于两侧且 diff 为空,才算完成(scripts/generate_checklist.py)。

此外还有一个优先级逻辑:如果 issue #5736 中记录了某库已由已合并 PR 完成(completed=Truepr非空),则该条目即使本地尚未同步也会被归入update_libs而不是add_libs(scripts/generate_checklist.py),保证 PR 完成与本地落盘之间的窗口期不会造成清单误判。

3.5 备注(notes)机制与模板渲染

--notes指定的备注文件(如 scripts/notes.txt)格式为每行模块名 备注内容//开头的行为注释行。脚本将其解析为dict[str, list[str]],并在渲染时通过handle_notes把对应备注附着到条目上(scripts/generate_checklist.py);未被任何模块消费的备注会在结束时以Unattached Note警告形式输出(scripts/generate_checklist.py),防止备注"悬空"。

最后,脚本用jinja2.Environment(loader=FileSystemLoader("."))加载当前目录下的checklist_template.md,把四个列表(update_libsadd_libsupdate_testsadd_tests)渲染进模板并打印到标准输出(scripts/generate_checklist.py)。

四、notes.txt:维护者沉淀的同步知识库

scripts/notes.txt 是清单生成时默认使用的备注文件,其中沉淀了大量"无法用代码表达"的同步经验,格式为模块名 备注。典型条目包括:

  • 关联 PR / issue:如ctypes #5572importlib #4565warnings #4013
  • 需要额外拷贝的附属文件:如re Don't forget sre files(提示re模块还要带上sre_compile.pysre_constants.pysre_parse.py)、site Don't forget _sitebuiltins.pypydoc pydoc_data
  • 替代实现文件:如abc _collections_abc.pydatetime _pydatetime.pyio _pyio.py——这些是 RustPython 用纯 Python 重写的 CPython C 实现模块;
  • 阻塞性说明:如os Blocker: Some tests requires async comprehension
  • 关联测试文件:如pickle test/pickletester.py supports test_pickle.py

值得注意的是文件末尾用// test分节,//行在解析时会被跳过(见 scripts/generate_checklist.py),其后test_*前缀的条目对应测试类备注(如test_gc #4158test_mmap #3847),这些备注最终会挂到测试板块的对应条目上。

五、配套工具链:update_lib 从"看清单"到"动手同步"

清单回答"缺什么",而 scripts/update_lib/ 工具包回答"怎么补"。这是一个以python scripts/update_lib <command>形式调用的多子命令 CLI(入口见 scripts/update_lib/main.py),与清单模板构成完整的同步工作流:

子命令用途示例
quick推荐的一键流程:打补丁 + 自动标记失败python scripts/update_lib quick cpython/Lib/test/test_foo.py
copy-lib从 CPython 拷贝库文件/目录(先删除本地旧文件)python scripts/update_lib copy-lib cpython/Lib/dataclasses.py
migrate从 CPython 迁移测试文件,保留 RustPython 的标记python scripts/update_lib migrate cpython/Lib/test/test_foo.py
patches补丁管理(在文件间提取/应用补丁)python scripts/update_lib patches --from Lib/test/foo.py --to cpython/Lib/test/foo.py
auto-mark运行测试并自动给失败项打@expectedFailurepython scripts/update_lib auto-mark Lib/test/test_foo.py
deps展示模块依赖信息python scripts/update_lib deps
todo展示按优先级排序的待更新模块/测试列表python scripts/update_lib todo --limit 20

其中todo命令与清单模板的理念一脉相承:它依据DEPENDENCIES关系计算依赖评分——无纯 Python 依赖的模块得分-1,有依赖的按"未更新的依赖数量"计分,排序优先级依次为"被依赖数越多越靠前、原生依赖越少越靠前"(见 scripts/update_lib/cmd_todo.py);测试则按"对应库是否已就绪"分为 0(库已就绪,可立即更新)、1(无对应库)、2(需先等库)三档(scripts/update_lib/cmd_todo.py)。输出格式与模板保持一致的- [x]/- [ ]复选框风格,并附带依赖数、被依赖数、最近更新时间与 diff 行数等元信息。

todo输出还包含两个额外板块:Untracked Files(存在于 CPython 但本地Lib/缺失的非模块文件,如数据文件)与Original Files(本地Lib/中存在但 CPython 没有的 RustPython 原创文件,如_dummy_thread.py),进一步补全了清单模板的四板块覆盖范围。

六、互补机制:whats_left.py 与 not_impl.py 的覆盖度审计

除了逐库同步清单,仓库还提供了另一层"API 级覆盖度"审计:scripts/whats_left.py 会在 CPython 3.14+ 下运行,遍历 CPython 的 builtins 方法、标准库模块,生成一份包含expected_methodscpymodslibdir的数据文件extra_tests/not_impl.py,再用 RustPython 自身(cargo run --release)执行该文件,对比双方:

  • 整个模块缺失 →not_implemented
  • 模块存在但导入失败 →failed_to_import
  • 模块属性缺失 →missing_items
  • 函数签名不匹配 →mismatched_items(需--signature参数展示);
  • __doc__不一致 →mismatched_doc_items(需--doc参数展示)。

最终输出按# modules# builtin items# stdlib items分组打印,并附带# summary统计(见 scripts/whats_left.py)。如果说checklist_template.md追踪的是"文件级同步",whats_left.py追踪的就是"API 级对齐",两者从不同粒度共同支撑 RustPython 标准库的兼容性评估。

七、实战:如何阅读与使用这份清单

7.1 生成清单

在仓库根目录执行(需先准备一份 CPython 源码并安装requestsjinja2依赖):

python scripts/generate_checklist.py --cpython /path/to/cpython --notes scripts/notes.txt

脚本依赖 GitHub API 读取 issue #5736 与 PR 合并状态,网络不可用时相关状态会回退为本地 diff 判定。

7.2 解读输出

生成结果的四个板块对应模板的四段:

  • List of libraries[x]表示该库文件及对应测试与 CPython 完全一致;[ ]表示仍存在 diff 或测试缺失;行尾的#编号是对应的已合并 PR;
  • List of un-added libraries:CPython 有而 RustPython 没有的模块,纯 Python 的可期移植,依赖 C 扩展的短期内不可行;
  • List of tests without python libraries:已同步的、没有对应库的独立测试;
  • List of un-added tests without python libraries:尚未同步的独立测试。

7.3 从清单到行动

对照清单找到[ ]条目后,即可进入update_lib工作流:先todo看优先级 →copy-lib拉取缺失文件 →quick(打补丁 + 自动标记失败项)→ 用patches管理对 CPython 文件的 RustPython 定制修改,完成后重新运行generate_checklist.py验证该条目是否变为[x]。整个过程与 scripts/checklist_template.md 定义的四板块状态机一一对应,构成 RustPython 标准库同步工程的完整闭环。

【免费下载链接】RustPythonA Python Interpreter written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/RustPython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询