Python 00:从哪个 Python 开始复现
一段词频脚本在终端运行成功,换一个终端却提示 No module named tasklab。代码没有变,执行它的命令、工作目录和模块搜索路径可能已经变了。还没讨论对象或并发,实验的第一条输入就应当是“由哪个解释器、在什么条件下运行”,而不是仅仅保存一段源码。
本系列使用一个累计扩展的任务应用 tasklab:提交人工生成的文本、执行白名单词频任务、查询状态、请求取消。00 只做进程内顺序执行。数据库、安装包与服务接口在后续章节加入,不能用此处的一次运行推断它们已经可用。
运行的起点不在源文件里
下面这条命令在仓库的 examples/python-language/ 目录执行:
1 | |
/usr/bin/python3.12 是本次明确调用的可执行文件;-m tasklab.demo 要求解释器按模块搜索路径找到并运行模块。PYTHONPATH=src 临时加入尚未安装的包所在目录,并非应用交付时推荐的安装方式。从其他目录执行却沿用相对的 src,搜索位置就会不同。官方命令行文档定义了 -m 的模块查找行为;sys.executable说明程序内如何检查解释器路径。两份信息共同定位“究竟运行了什么”,不能靠命令提示符中的 (venv) 猜测。
这里的 demo 暂时是可复现的实验入口,不是正式 CLI。未来多次 CLI 调用要共享任务,必须等 21 引入持久化;现阶段每次启动都是新的进程内存。
固定哪一层版本
以下 Ubuntu 版本和逐章测试数量是 2026-10-07 留存的历史实验。本仓库另在 2026-10-12 用 macOS arm64 的 CPython 3.12.3 复跑当前累计源码、重新构建并安装 wheel;完整版本、二进制哈希、编译参数和 SQLite 版本见 examples/python-language/evidence/completion/acceptance/cpython-environment.stdout。相同补丁号不能把两个平台视作同一个构建;复现命令应以 RUN.md 的可选解释器路径为准,不能要求 macOS 上存在 /usr/bin/python3.12。下文上游 tag 是参考源码定位,不能单凭它推定新二进制的全部下游补丁。
sys.version 的实测值是 3.12.3 (main, Aug 31 2026, 10:18:26) [GCC 13.3.0],解释器路径是 /usr/bin/python3.12,系统包版本为 python3.12-minimal 3.12.3-1ubuntu0.17,平台为 Ubuntu 24.04.4 LTS、x86_64。编译选项包括 --enable-shared 和 --without-ensurepip,链接的 SQLite 为 3.45.1。二进制 SHA-256 和完整配置记录在仓库的 examples/python-language/evidence/00/20261007-baseline/environment.txt。
版本号相同仍不等于二进制完全相同。本机安装的是带发行版补丁的 CPython:上游源码 tag 为 v3.12.3,对应 commit f6650f9ad73359051f3e558c2431a109bc016664;本机包还对应 Ubuntu 3.12.3-1ubuntu0.17 的 .dsc、原始源码包和补丁包。两份源码包已下载,并按 .dsc 内的 SHA-256 校验。只把上游 commit 写作“本机的精确源码”会漏掉发行版补丁。相关网址、校验值和未执行的签名验证均在环境记录中。
资料依据 Python 3.12 文档分支。它提供该版本的接口描述,不代表文档网页的日期就是本机二进制的构建时间,更不代表正文已在 Python 3.14 或其他构建上验证。构建、实际 GIL 状态也要分开说:这台机器是通常的 CPython 3.12 构建,Py_GIL_DISABLED 未启用,探测时当前线程持有 GIL;此版本没有 sys._is_gil_enabled 接口。本记录没有 free-threaded 构建,也没有扩展兼容性实验,不能由它推测 38 的结果。
还有三份不同的依赖事实:pyproject.toml 的直接运行依赖声明是空列表;构建工具、解析锁与静态检查器尚未选择;当前运行使用的是系统已安装的解释器与标准库,不是从 wheel 干净安装得到的环境。声明、解析结果、实际安装三者不能互相替代。
先定义最小可核对的任务
tasklab 此时公开四个动作:submit(store, kind, text) 先校验,再生成 task-0001 并标为 queued;run 同步计算并写入终态;query 返回状态快照;cancel 对未开始任务接受取消,对已经成功的任务拒绝覆盖。结果是按空白分词、转为小写后的计数字典,不是语言学意义上的分词器。
成功命令的原始标准输出是:
1 | |
退出码为 0。提交、执行、查询都发生在同一次进程内调用。状态路径为 queued → running → succeeded;它并不说明长任务一定能即时响应取消。未来并发章节要区分“接受请求”与“实际停止”,已经提交的结果不因迟到的取消请求而变成 cancelled。
反例从同一个合法任务开始,然后尝试提交空的任务类型:
1 | |
标准输出为空,标准错误为 {"error": "only word_count tasks are supported", "task_count": 1},退出码为 2。task_count 仍是此前那个合法任务;失败的提交不推进下一个任务 ID。测试还用 None 文本触发 TypeError,验证旧任务没有改变,再提交合法任务得到 task-0002。这些是当前进程的实际断言,不是“输入坏了,程序大概会失败”的预期描述。
证据的覆盖范围
在 examples/python-language/ 下执行:
1 | |
本轮发现并运行四条测试,退出码 0。用例覆盖成功任务、拒绝输入、取消排队任务及结果查询快照;原始标准输出、标准错误和每个命令的退出码保存在 evidence/00/20261007-baseline/。unittest 的发现规则解释了为何 -s tests 是测试入口而不是 Python 包名。
目前的断言没有跨进程保存任务,没有测量吞吐量,也没有证明进程异常退出后的清理;这些问题必须由持久化、并发和资源章节分别检验。测试使用 PYTHONPATH 让源码包可导入,因而不能拿这四个通过的断言冒充 wheel 安装验收。
预测题
保持工作目录和解释器不变,去掉 PYTHONPATH=src 后再执行 python -m tasklab.demo,会怎样?改变解释器的版本号但保留环境变量,能否推出词频输出一定变化?
答案:在当前未安装 tasklab 的环境中,解释器不能从默认搜索路径找到源码包,模块查找在任务函数之前失败;实际输出还取决于环境中是否另外安装了同名包,所以预测前须检查安装状态。版本号变化也不足以单独推出业务输出变化;要固定输入并重新执行,记录新解释器的路径、依赖和断言结果。这里没有把未执行的跨版本对照算成通过。
变更题
把拒绝场景的任务类型改为合法的 word_count,同时把文本改成 None。需要检查什么,才能断定非法提交没有污染已有任务?
答案:期待 TypeError;对比拒绝前后的旧任务快照及 len(store["tasks"]),再提交一个合法任务检查 ID 是否仍为 task-0002。仅检查退出码不能证明旧状态没变;仅检查错误文本也不能证明编号没被消耗。这组断言已由本章 test_invalid_input_preserves_existing_tasks_and_id 执行。
01 要解释为什么把两个名称指向同一任务输入,不等于创建两份独立的数据。未完成章节不预先建立文章链接;已验收篇目集中在系列导航。






