Python 17:命令行输出怎样供人和程序使用
把 "One two one" 交给命令行,成功时标准输出只有一行 JSON,result 是 {"one": 2, "two": 1};把输入换成不存在的文件,标准输出为空,错误写入标准错误,退出码是 2。这两个观察分别回答程序调用方和操作人的问题:哪里取可解析的结果,哪里找失败原因。
本章沿用16 的资源作用域和原来的 submit → run → query,只在外面加一层入口。tasklab 还没有 SQLite;每次调用创建的是独立的进程内任务,返回 task-0001 不意味着下次启动还能查询它。跨进程查询留到 21,不用“CLI 已经能打印 JSON”冒充持久化。
参数何时变成任务
入口 examples/python-language/src/tasklab/cli.py 用 ArgumentParser 把 --text 和 --file 放入 required=True 的互斥组。没有输入和同时传两种输入都会在解析阶段被拒绝。--kind 目前只有 word_count 能进入已有的提交函数;未知值是反例,不悄悄改成默认任务。
输入文件是 pathlib.Path,但 type=Path 只负责构造路径对象,不会证明文件存在、可读或内容有效。入口在 with args.file.open("rb") 中最多读 MAX_INPUT_BYTES + 1(当前是 1025)字节;多读的一字节用于分辨刚好到上限与越界,随后由原有 submit_bytes 检查长度并做严格 UTF-8 解码。文件关闭由 with 保证;在失败时尚未创建任务。文本参数直接进入 submit,由相同词频规则检查任务类型及 UTF-8 编码后的长度:1024 个 ASCII 字符和 1024 个多字节字符不是相同大小的输入。
接口不直接序列化任务对象。query 返回当前快照,入口把 ID 与快照组装成 JSON;json.dumps(..., ensure_ascii=False, sort_keys=True) 固定了这一层的输出方式。这里可解析的是单次命令的结果,既不是对象的持久化格式,也不是未来 JSON 行服务的协议。argparse 解析得到的字符串仍要经过 submit 的运行时校验,类型提示也不能取代它。
在 examples/python-language/ 下实际运行:
1 | |
第一条退出 0,输出中的 id 为 task-0001、state 为 succeeded,标准错误为空。第二条退出 2,标准输出为空,标准错误有 error: only word_count tasks are supported;第三条帮助信息走标准输出,退出 0。experiments/17_cli.py 还验证了文件与文本结果一致、路径不存在、非法字节、文件过大和两种输入同时出现;累计 54 项测试通过。真实命令、原始输出及退出码在 examples/python-language/evidence/17/20261007-cli/,不能只看屏幕上的报错就假设另一条流也为空。
用法错误与执行失败不可混淆
argparse 对用法错误打印用法与错误并以状态码 2 退出;合法参数进入后,打开文件、解码、长度和任务种类错误也经 parser.error 归到入口错误。此时提交函数还没通过校验、ID 尚未分配。这里的“失败”是请求未获接受,不是执行中失败任务的状态。执行中的错误仍由应用层记录并抛出,不能误记成参数错误。
若把报错混入成功时的标准输出,json.loads(stdout) 就不能解析。反过来,调用方只检查是否有输出、不检查退出码,也会把“没有执行”误判成空结果。标准错误保留文件路径和原因供本地诊断;文件路径可能包含敏感信息,未来网络入口不能原样向不可信客户端返回该字符串。
预测题
传入 --text one --file missing.txt,会先尝试打开文件吗?预测两个标准流和退出码。
答案:不会。互斥组在 parse_args 时发现冲突,尚未到 Path.open。退出码 2,标准输出为空,标准错误是用法与冲突错误,不是文件不存在错误。experiments/17_cli.py 的 exclusive 场景覆盖了此顺序。
变更题
把文件读取改成 Path.read_text(),然后只按 len(text) <= 1024 判断长度。为什么旧限制不再成立?
答案:一次性读完整文件失去有界读取,字符数也不等于 UTF-8 字节数。保留按字节读取至上限加一并复用 submit_bytes;准备恰好 1024、1025 字节、含多字节字符的临界输入和非法 UTF-8,检查退出码、标准流和任务是否曾被提交。本章实测 1025 字节与非法 UTF-8;“恰好上限”文件变式未作为独立证据,不说成已测。
18 用类型提示检查开发阶段的接口使用;19 再解释导入和 python -m 入口。当前命令行仅在源码目录通过 PYTHONPATH=src 启动,不能声称已安装 wheel。系列导航只链接完成并验收的文章。




