文档 / 验证方法
如何做差分测试
同一组输入分别交给项目实现与目标版本 CPython,再对比可观察结果。
为什么需要 CPython 对拍
实现本身不能作为唯一正确性的证明:如果测试只断言“程序按当前实现输出了某个结果”,实现与预期可能同时偏离 Python 语义。差分测试把同一输入交给目标版本 CPython 和 node-for-python,比较两边可以观察到的结果。CPython 3.15 是此项目的行为参照,版本固定可以避免本机 Python 更新后改变预期。对拍也不能证明完整兼容,但能把常见输入、边界值和异常行为转成可重复检查的契约。
测试结果应指出比较对象和适用范围。例如“某组字符串切片向量一致”只说明这组向量覆盖范围,不代表所有 Unicode 行为都一致。新发现的差异应成为明确的测试输入或文档边界,后续运行才能继续看见,而不是被宽泛断言掩盖。
固定 oracle 与向量
向量生成器使用 CPython 3.15 运行输入,记录稳定的 JSON 数据。向量提交到仓库后,Node 测试只读取固定结果,不在每次测试时启动 Python 子进程,因此在没有 Python 的构建环境中也能重复运行。生成阶段依赖 CPython,验证阶段则依赖 Node.js 与仓库向量;这两个阶段的依赖边界应保持清晰。向量应尽量使用 JSON 可表示的输入和输出,并为异常记录类型、消息中稳定的部分及触发条件。若错误文本包含路径、版本或实现相关细节,应先规范化不稳定字段,再进行比较。不要把整个错误字符串盲目截断到看似相等;应明确哪些字段属于语言契约,哪些是宿主环境信息。
四层分别比较什么
| 层 | 输入与结果 | 重点字段 |
|---|---|---|
| Tokenizer | 源码到 token 序列 | token 类型、原文、起止位置、行内容及缩进边界 |
| Parser | 源码到 AST 或语法错误 | 节点结构、运算优先级、位置与错误类别 |
| Interpreter | 程序执行 | stdout、退出状态、异常类型及可稳定比较的首末行 |
| 标准库 | 模块 API 调用 | 返回值、异常类型、消息契约、副作用和边界行为 |
比较时应避免把内部 JavaScript 对象身份当成 Python 语义。AST 可先归一化为结构数据,输出按字节或明确的换行规则比较,异常则区分类型和消息字段。遇到失败时,先判断输入是否进入了目标层,再确认两边比较的是同一项数据。
生成和维护向量
向量生成器通常放在对应模块的 test/ 目录,使用目标 CPython 产生 vectors.json。生成命令应能从仓库根目录执行,并明确 Python 版本;Node 测试文件从固定路径读取该 JSON,逐个检查输入与结果。修改向量时应保留输入含义和生成方式,使其他使用者能够重新生成并检查差异。
向量不是越多越好。应覆盖正常值、空值、类型边界、异常路径和容易混淆的语义分支。重复输入会增加运行时间却不增加覆盖信息;一个有代表性的反例通常比很多随机样本更容易定位契约。若使用随机测试,固定种子并记录最小复现输入。
失败判定与定位顺序
先看测试总数,再看失败数和具体失败名称。总数为零可能表示 glob 没匹配到文件,不能当作通过;有失败时先区分测试环境错误、向量加载错误和语义不一致。逐个检查输入、CPython 结果、项目结果和归一化规则,然后归类到 tokenizer、parser、interpreter 或模块层。失败摘要是定位入口,不应只看最终总数。
对于有意不支持的行为,应由明确的模块状态或能力边界表达,并避免把“不支持”误记为“结果一致”。对已支持范围内的差异,保留可复现输入和精确比较字段,让后续修正不改变无关行为。每次运行所用的 Node 版本、CPython 版本和测试命令也应保持可查。
新增一个模块的测试流程
- 查阅 CPython 3.15 的 API 文档,确定名称、参数、返回类型、异常和边界语义。
- 在
libstd/<module>/test/设计小而有区分度的输入,覆盖正常路径和关键边界。 - 用 CPython 生成并保存
vectors.json,检查 JSON 中没有临时路径或不稳定对象表示。 - 编写
*.test.mjs,逐项调用实现并比较返回值、副作用或结构化异常。 - 运行相关测试,再运行仓库标准测试命令;如果某些 API 未支持,在模块说明里写清可用范围。
这种流程既用于新增实现,也适用于使用者确认某个模块是否满足应用需求。若模块状态是部分实现,应优先查看对应测试和说明,而不是仅依据 API 名称做判断。
语法矩阵:parse 与 compile
scripts/syntax-matrix.mjs 按类别构造 545 个语料样例,提供 parse 与 compile 两个 tier。parse tier 检查源码能否被 parser 接受;compile tier 检查语法进入编译阶段时的接受与拒绝行为。两层关注点不同:语法树解析成功不自动说明编译阶段也会接受所有上下文规则。
矩阵将每个语料分别交给 CPython 3.15 和项目实现,统计接受/拒绝是否一致,并列出多收与漏收项目。报告中的综合一致率是当前语料中判断相同的比例,不等同于整个 Python 语言的兼容率。语料按类别组织,可以从某个具体不一致项定位未覆盖的语法边界。
node scripts/syntax-matrix.mjs
node scripts/syntax-matrix.mjs --md .tmp-syntax