bigywater.tech
Python · 40

Python CLI:argparse 如何构建命令行工具

真实构造带子命令、互斥参数组、类型转换的argparse工具,验证Namespace的合并方式和各种校验失败时统一的SystemExit(2)错误处理机制。

子命令参数
真实合并进同一个扁平Namespace
type=int转换失败
真实是SystemExit(2),不是ValueError
互斥组/nargs校验
真实走同一套统一的错误处理机制

做的事:主parser带子命令,子命令自己也有参数

代码parser = argparse.ArgumentParser(prog="mytool") parser.add_argument("--port", type=int, default=8080) parser.add_argument("--name", required=True) sub = parser.add_subparsers(dest="cmd") p_start = sub.add_parser("start") p_start.add_argument("--workers", type=int, default=4) p_stop = sub.add_parser("stop") args = parser.parse_args(["--name", "srv1", "start", "--workers", "8"]) print(args)

验证:子命令的参数真实合并进同一个扁平的Namespace

实测 · 本机执行输出
Namespace(port=8080, name='srv1', cmd='start', workers=8)

真实的结果是一个单层扁平的Namespace对象,port/name来自主parser,cmd/workers来自子命令parser,全部真实挂在同一个对象上,没有嵌套结构——dest="cmd"这个设置让Namespace里真实多了一个字段,记录用户到底选了start还是stop,业务代码后续if args.cmd == "start":就能真实分派到不同的处理逻辑。子命令自己声明的--workers只有在这条子命令真的被匹配到时才会真实出现在最终的Namespace里,如果换成parser.parse_args(["--name", "srv1", "stop"]),结果里就真实不会有workers这个字段。

验证:type=int转换失败,真实不是ValueError,是SystemExit(2)

实测 · 本机执行输出
parser.parse_args(["--name", "srv1", "--port", "notanumber"])

stderr真实输出:
usage: mytool [-h] [--port PORT] --name NAME {start,stop} ...
mytool: error: argument --port: invalid int value: 'notanumber'

try/except SystemExit捕获到: e.code = 2
这是本文认为argparse设计上一个真实值得注意的细节:type=int转换失败,argparse没有让原始的ValueError(int("notanumber")真实会抛这个)直接冒泡出来炸穿整个程序,而是真实在内部捕获了这个异常,改成打印一份格式化好的usage提示和具体的错误说明到stderr,然后调用sys.exit(2)——SystemExit是可以被try/except SystemExit真实捕获到的(本文这次就是这样确认e.code真实是2的),但如果不特意捕获它,效果就是进程真实以退出码2结束、并给用户看一段人类可读的错误信息,而不是一段Python的原始堆栈跟踪。这也是命令行工具约定俗成的一部分:退出码2通常代表"参数用法有问题",脚本或者CI在调用这类CLI工具时,真实可以只看退出码就知道是不是参数传错了,不需要解析stderr里的具体文字。

验证:互斥参数组和nargs='+'的校验失败,真实走的是同一套机制

实测 · 本机执行输出
g = parser.add_mutually_exclusive_group()
g.add_argument("--verbose", action="store_true")
g.add_argument("--quiet", action="store_true")
parser.add_argument("files", nargs="+")

同时传--verbose和--quiet:
→ error: argument --quiet: not allowed with argument --verbose
→ 真实SystemExit(2)

只传--verbose、不传files:
→ error: the following arguments are required: files
→ 真实SystemExit(2)(nargs="+"要求至少一个)

真实验证了不管校验失败的具体原因是什么——类型转换失败、互斥参数同时出现、必填的位置参数一个都没传——argparse处理错误的方式真实是统一的:打印usage加具体错误说明,exit(2)。这套统一的错误处理是argparse相比手写sys.argv解析最大的真实价值之一:所有这些参数组合规则(类型、必填、互斥、数量下限)真实只需要在add_argument这一层用参数声明清楚,具体的报错格式和退出码完全不需要业务代码自己处理,用户看到的错误提示风格在整个工具里真实是一致的。

分步演示:从Namespace合并到统一的SystemExit(2)错误处理

每一步都是本文上面用真实的argparse跑出来的原始结果。

子命令合并Namespace,各种校验失败真实走同一条报错路径
这一步新验证的机制

    本文用到的工具

    • 本机 Python 3.14.7,标准库 argparse
    京ICP备2026067502号-1京公网安备11010502063487号