先跟完一件事:把 order.py 交给 Runner,让 Python 查询价格与运费,算出订单结果,再把结果带回 Go。每到一个文件,先看它在这件事里负责什么,再读实现这项职责的源码块。
想先用一篇文章走完整条正常路径,可以读 order.py 函数流程。准备展开实现时,从 main 入口 开始。
先把一次执行装进脑子里
例子中的任务是查询 book 的单价与 shipping 运费,再计算总额。用户 Python 做业务计算;Go Host 提供查询工具;中间的 Wasm/C 边界负责把请求和结果传过去。
CLI:读源码、输入和 Wasm,声明工具 manifest
↓ New
Runner:编译 Wasm,安装 Host 回调
↓ Run
本轮状态 + 私有 Guest
↓ execute
C 入口 → Python bootstrap → 用户程序
↓ lookup(...)
具名 Python 工具函数
↓ JSON / Host ABI
Go Host 查工具并执行
↓ 返回价格或运费
Python 算出 result
↑ JSON 结果 ← C 返回地址与长度
Runner:读回结果,释放本轮资源
↓
CLI:显示结果,关闭 Runner
读第一圈时,始终保留三个对象:
- Runner 持有编译代码、工具声明和可选初始化基线,供后续 Run 使用。
- 一次 Run 持有自己的 Guest、输入输出和提前任务记录。
- Host 工具 接收参数并返回结果;Python 的乘法、分支和异常处理继续由 Guest 内的 CPython 执行。
这里的箭头表示实际调用与返回关系。PLM 和 Prefix 会调整合格工具工作的开始时机;这条普通调用路径仍是理解它们的基础。
一章怎样读
每章先说明当前已经具备什么、还缺什么,然后按职责展示真实源码。例如阅读 prepare 时,先理解“登记请求并启动工作,立即返回编号”,再看如何分配 handle、保存记录和创建 worker。
源码前后的说明要能回答:
- 为什么此时需要这一块?
- 它调用哪个函数完成子任务,关键值由谁持有?
- 成功后留下什么,下一步由谁继续?
- 失败或取消时,谁负责交付错误、停止工作和释放资源?
相邻声明、导入、配置和辅助函数按用途一起解释。关键语法在影响理解时展开,背景知识可以去 Go 组合教程 或 前置课程 查阅。
源码块从当前仓库自动提取,保留原始行号和完整文件入口。行号负责定位;正文沿调用、状态和设计原因组织。源码块可以按执行顺序出现,无须跟随物理文件的声明顺序。
第一圈:走完普通执行
这圈要回答:输入在哪里准备,谁真正执行 Python,工具怎样回到 Host,结果怎么离开 Guest?
- 入口:参数、工具与模式:把命令行选择装成一次运行请求。
- Runner:创建与运行:分清长期 Runner 与本轮 Guest,跟随输入、调用、输出和收尾。
- 初始化与内存恢复:先读普通 fresh 路径,理解实例化、C 初始化和 CPython 初始化。复用分支留到后面。
- C 与 CPython:跟随字节跨入 Guest、进入解释器,再带着结果返回。
- Python 执行约定:准备用户环境,按 manifest 注入具名工具,执行源码并收集
result。 - Host 工具边界:从一次 Python 查询回到 Go,再把响应交回 Python。
走完后,应能解释为什么查询发生在 Host、业务计算发生在 Python,以及 Runner 为什么可以复用而两轮 Python 状态仍彼此独立。
用例与测试:把调用链落到具体行为
先看一个完整场景,再看测试怎样造出需要检查的状态:
- 六个用户程序:从顺序计算、依赖、分支到错误处理。
- 交易演示:查询行情和余额,Python 决定是否下单,Host 检查许可并修改本轮模拟状态。
- 快捷脚本:以固定参数重复演示价格不合适、现金不足、Host 拒绝、批准和 Prefix 路径。
- 普通执行测试:怎样检查值、输出、异常、取消与隔离。
- CLI 示例测试:怎样让同一例子在不同运行方式下保持预期结果。
测试章围绕“如何证明这个行为”组织。握手、计数和断言都需要接回它们要排除的错误执行,而不只是认识测试 API。
第二圈:提前工作,仍在原位置交付
普通 Python 是顺序程序。PLM 希望让合格的独立读取先工作,等 Python 到达原调用位置再领取结果。这圈新增两个协作者:分析程序的 Guest pass,以及管理提前工作的 Go 状态。
- Future:本轮任务登记与交付:
prepare登记并启动,worker 发布响应,resolve领取,Run 退出时统一收尾。Python 得到的是整数 handle。 - PLM AST pass:从实际依赖和控制区域决定怎样改写调用。
- 真实 PLM 测试:验证提前工作、结果位置、错误和取消。
- AST 单元测试:检查哪些程序可改写,哪些保留普通执行。
读完应能画出一条读取的“启动 → 完成 → 交付”时序,并说明为什么完成顺序可以变化、程序观察到的结果位置仍受约束。
第三圈:源码还在到达时怎样准备
Prefix 把“接收源码”接在“执行完整程序”之前。它需要识别已经完整的候选语句,并把准备好的请求与最终执行中的调用对应起来。
- Host 接收源码片段:片段怎样进入同一个 Guest,源码结束后怎样继续执行。
- Guest 解析与领取:扫描前缀、登记准备请求,并在真正调用时匹配领取。
- 真实 Prefix 测试:怎样证明工具能在源码结束前启动,以及取消如何收尾。
- Prefix 解析测试:怎样处理半句、重复扫描、依赖与不支持区域。
这圈要分清源码片段到达、语句完整、工具开始工作和最终程序执行这几个事件。
第四圈:初始化状态怎样复用
这里从每轮都要准备的 Guest 出发,依次看哪些状态适合保留,怎样让新实例获得自己的修改空间,最后由谁释放。
- 回到 初始化与内存恢复,展开 full-copy 和 COW 分支。
- Full-copy 测试:核对恢复后的行为和跨轮隔离。
- COW 小接口:划清通用执行路径与平台内存实现的职责。
- Linux COW:跟随创建映射、捕获基线、私有写入、增长与关闭。
- 平台边界:不支持的系统如何明确返回错误。
- 映射与释放测试:检查共享、写隔离和资源回收。
- 真实 Python COW 测试:把内存机制接回真实 Guest 行为。
线性内存、Wasm 实例状态和 Host 资源分别讨论。理解当前初始化基线的复用范围后,再阅读 COW 深度解析。
工程旁支:从源码构建到离线演示
这条路线解释输入、工具链和产物如何流转:
Go 依赖 → Makefile 任务 → 构建输入 → Guest 编译链接 → 离线网络检查 → 本地阅读服务。
读完应能根据改动位置判断需要重建什么,并说明“已有依赖可用”和“子进程真的无法联网”分别如何验证。
网站旁支:这本带读怎样生成
沿文档和源码变成网站的路径阅读:
- 内容装配:决定哪些讲义和源码允许进入构建。
- 片段提取与覆盖检查:从真实文件提取区间,检查是否遗漏代码。
- 内容集合与页面路由:把生成内容交给 Astro 页面。
- 源码视图:提供完整文件、函数入口和行号定位。
- 页面外壳:把导航、正文和目录组织成阅读页面。
- CSS 布局:桌面和手机怎样呈现正文与代码。
- 站点配置:依赖、构建命令与搜索索引规则。
- 发布验证:检查页面、链接和公开边界。
完整覆盖如何保留
带读覆盖主线实现、测试、用户示例、交易演示、快捷脚本、本地构建与离线工具,以及原有站点教学源码。每个目标文件的全部非空行都进入带读源码块;讲解按职责归组,避免把一行语法当成一项独立知识。
目录与覆盖目标由 site/scripts/readings.mjs 显式维护。当前路线共 40 章,源码覆盖目标为 67 个文件。片段覆盖只能证明没有漏掉代码,机制解释仍需对照真实实现和测试审读。
第三方 CPython/wazero 源码、生成的 Wasm、工具链产物、运行日志和私有配置不纳入逐块带读。额外的有声课和 Go 组合教程有各自的维护范围。包含私有运行环境规则的站点源码会显示明确的脱敏标记,保留原始行号。
下一步:打开 main 入口,先看一次执行需要哪些输入,再看 CLI 怎样把它们交给 Runner。