Go 14:有限输入和 JSON 输出的资源边界
13 之前的 CLI 用 io.ReadAll 读取文件:一个不受控制的大文件会先进入内存,再做任务统计。引入 stdin 与 JSON 后,读取量、坏 JSON 和 stdout 部分写出还会把原来的「输入有效才写结果」契约拆成几个环节。哪一层负责关闭文件?Writer 只接受了部分字节,程序还能声称调用没有任何输出吗? 同一累计工程本章新增 --stdin、--input-json 与 --json。--file/--stdin 的单份输入最多 65536 字节;--input-json 只允许搭配这两种来源,解码形如 {"text":"Go 写 Go"} 的 JSON。--json 对统计结果输出 JSON 数组,--list-ids 则输出字符串数组。已有 --text、--batch、--lookup 和顺序任务记录不改写成另一个工程。本章完整包与SHA256SUMS可在网站独立下载并复跑。 独立复跑步骤列出本章正反例、源码校验及运行命令。 超限必须多读一个字节 readBounded(input) 在 io.LimitReade...
Python 26:安装通过为何不等于发布通过
第25 篇生成的 wheel在 CPython 3.12.3 的干净 venv 能运行 tasklab 命令。但用 pip 的目标版本选项要求按 Python 3.11 解析同一 wheel,得到的是 requires a different Python: 3.11.0 not in '>=3.12',退出 1。这条反例不需要假装机器装了 3.11:它证明打包元数据会在解析阶段排除 3.11,却不能证明一个真实 3.11 解释器的运行表现。版本声明、解析结果和已经安装的环境应分开记录。 本篇只检查本地制品及发布前材料,不推送 Git、不上传 PyPI、不部署博客。所谓发布成功至少要有索引上传和可下载的外部制品记录;本地安装正常不能替代这些证据。 给同一制品三种身份 pyproject.toml 声明项目版本 0.1.0、支持 >=3.12、运行依赖为空;这些是作者写在构建输入中的约束。tools/audit_release.py 读取实际 wheel 的 METADATA 和 WHEEL,又对照 sdist 的 PKG-INFO:两边名称和版本一致,Require...
Go 13:泛型约束能替结果集合省下什么
12 之后,顺序任务服务的每条任务记录仍保存在 []TaskRecord 中。默认批量输出要把这些记录变成统计摘要;早先结果还可以直接持有 []TaskResult。是否值得为这种变换加一个泛型函数?先检查调用方希望保留哪种输入切片,再检查函数体真正要用什么操作,而不是把整套任务模型都改成泛型。 同一累计工程本章新增 Summaries[T HasSummary],主 CLI 的默认输出直接调用 Summaries(records)。查询、文件输入、错误分类、panic 注入测试和临时任务 ID 都仍在原工程;本篇完整累计归档包含正反命令与历史 Go 文本证据,SHA256SUMS提供下载校验。 独立复跑步骤列出本章正反例、源码校验及运行命令。 约束只承诺一个方法 1234567891011type HasSummary interface { Summary() string}func Summaries[T HasSummary](records []T) []string { lines := make([]string, len(...
Go 12:panic 时清理和 recover 的边界
11 已把空文本、查不到任务与文件读取失败变成可判断的错误。统计函数真的发生 panic 时,前面通过 defer 注册的清理还会执行吗?另一个常见误用是在普通代码中调用 recover(),以为它能给任何后续 panic 提供兜底。这两件事有不同条件,不能凭一次 go test 无失败就宣称资源安全。 本章继续沿用同一累计工程的 analyzeTask、readAndClose 与 CLI。analyzeTask 通过 analyzeTaskUsing(text, max, analyze) 将分析函数作为参数交给一个窄的测试接缝,生产入口仍传 stats.Analyze。panic 由测试替身注入;正式 CLI 没有安装吞掉未知 panic 的全局 recover。完整工程、正反原始证据及归档校验值随本篇素材包提供。 独立复跑步骤列出本章正反例、源码校验及运行命令。 先确认注入真的抵达统计函数 TestPanicsAndCleanup 传给 analyzeTaskUsing 一个会执行 panic("injected statistics panic") ...
Python 25:pyproject 如何生成可安装制品
在24 的隔离环境里,PYTHONPATH=src 能运行任务,但这并不测试安装。现在从项目 pyproject.toml 构建 wheel 与 sdist,再把 wheel 装进另一个没有 pip、也不把源码目录加入 PYTHONPATH 的 venv;从仓库外运行生成的 tasklab 命令,先提交,再通过另一个进程执行和查询同一 SQLite 文件,最后验证重复执行被拒绝。安装后 tasklab.__file__ 位于新环境的 site-packages,不是工作树的 src。 这次新增的是分发边界,不是新任务引擎。21 篇的数据库合同和取消逻辑仍由原模块执行。发布到 PyPI、跨平台兼容性和非信任网络输入不在本章验收范围。 分清三个文件和三种角色 pyproject.toml 的 [build-system] 选择 setuptools.build_meta,将后端 setuptools==80.9.0、构建 wheel 所需 wheel==0.45.1 固定;[project] 保留 name=tasklab、version=0.1.0、requires-python...
Go 11:错误包装后怎样保持可判断
10 确定了查询成功必须返回真正的 nil 接口;缺失任务会返回非 nil 错误。接下来调用方还需要区分空文本、查不到 ID 与文件打不开:如果只根据 err.Error() 的字符串做分支,包装上一层 lookup "task[missing]" 就会改变调用方判断,OS 路径也可能进入 CLI 输出。错误类别应能跨包装层识别,诊断细节则留在内部错误链里。 沿用同一累计工程,本章为当前 CLI 的内部调用方声明 ErrInvalidInput、ErrTaskNotFound、ErrExecutionFailed 三种可用 errors.Is 判断的类别。这里的 package main 不是可供其他模块导入的公共错误库;CLI 对用户显示的消息另行决定。本篇完整累计工程与证据在只包含网站源码的环境中也能下载,附有SHA256SUMS。 独立复跑步骤列出本章正反例、源码校验及运行命令。 哪一层知道错误类别 validateConfig(Config{}) 对非正的 MaxWords 包装非法输入类别;analyzeTask 对空白文本和超过字段上限的文本也返回...
Go 10:接口里的 nil 指针为何不是 nil
09 的 TaskLookup 用 Lookup(id) 返回记录和是否存在的布尔值。查询方往往还要把「无此任务」沿调用栈返回给 CLI:如果函数声明返回 error,在成功路径先留一个 *missingTaskError 类型的 nil 指针,直接把它作为 error 返回,会不会等价于 return nil?不会。此时 err != nil 为真,即使内部那个指针为 nil。 同一累计工程在查询路径加入 lookupRecord(source, id) (TaskRecord, error)。查到任务时返回记录和字面 nil;未查到时返回非 nil 的 *missingTaskError,describeTask 把错误交给 CLI。旧的 --lookup 'task[missing]' 仍输出 task not found: task[missing] 到 stderr 并退出 2,成功查询与其他入口输出不变。真实反例、测试和完整工程见本章素材包与SHA256SUMS。 独立复跑步骤列出本章正反例、源码校验及运行命令。 比较的是接口值,不只是指针 Go 规范允许接口值同时携...
Python 24:一个环境为什么能运行却不能重建
在本机直接运行 /usr/bin/python3.12 -m venv /tmp/... 失败:发行版解释器没有安装 ensurepip,提示虚拟环境无法完成创建。改用 venv --without-pip 能隔离解释器,但新的环境仍没有安装器;它能通过 PYTHONPATH=src 从源码导入 tasklab,却不是“已安装了 tasklab”。这三种状态分别是:解释器能启动、源码包能被找到、发行包已安装。只看最后一次 import 成功,会错过前两处依赖条件。 第19 篇的导入路径和21 篇的 SQLite 仓储为本篇提供了现成的全套回归。这里只操作仓库外的临时虚拟环境,不改项目根配置或依赖锁;wheel/sdist 的干净安装留到 25,不能把设置 PYTHONPATH 当成安装成功。 隔离的是解释器,还是依赖清单 sys.prefix 指向新建虚拟环境,sys.base_prefix 仍指向原始系统解释器。初次默认创建失败后,用 /usr/bin/python3.12 -m venv --without-pip 建立环境。为了能在该环境记录安装器与依赖结果,本次取得 P...
Go 09:查询接口由谁需要的方法决定
08 把任务的 ID、结果和状态放进 TaskRecord。查询当前批次时,调用方实际需要几种方法?只要凭 ID 取到一份记录并辨认是否存在;创建批次、列出所有 ID 和更新状态不必进入同一个接口。先把接口定义为「存储系统的全部能力」,测试替身反而需要伪造一套写入协议。 同一累计工程在查询方附近声明 TaskLookup,只有 Lookup(id string) (TaskRecord, bool)。原 CLI 的 --lookup 'task[0]' 现在通过该接口调用;当前批次实体 BatchTasks 仍用 07 的 map 作为索引,不变成新工程、后台任务或持久化数据库。本章完整工程与 00–09 证据可下载,SHA256SUMS验证其内容。 独立复跑步骤列出本章正反例、源码校验及运行命令。 从一次查找收紧接口 describeTask(source TaskLookup, id string) 只问一件事:调用 source.Lookup(id),不存在就返回 "", false,存在则返回 "task[0] words=..."...
Go 08:任务记录的复制与指针更新
07 用临时 map 把 task[0] 映射到结果 slice 的第 0 项。计数、ID 和状态要放在同一个任务记录里时,先复制记录留作快照,再更新原记录,快照会变吗?答案取决于记录里的字段怎样保存数据,也取决于更新方法的接收者。把 Go 的嵌入写成「类继承」会掩盖这个问题。 同一累计工程引入 TaskRecord:包含 ID、TaskResult 和 Status,后者当前只使用 QUEUED 和 SUCCEEDED。原 CLI 的文本、文件、批次与本次批次的查询仍保持原输出。新增的类型只组织顺序计算的结果,尚未引入 worker 或对外可查询的持久任务;本篇完整归档和SHA256SUMS用于离线复跑。 独立复跑步骤列出本章正反例、源码校验及运行命令。 更新原件,快照保持什么 newTaskRecord("task[0]") 返回值的 ID 与初始 QUEUED 状态。record := newTaskRecord(...) 得到可寻址的局部变量;snapshot := record 复制此时整个结构体值;然后 record.MarkSucceeded(c...













