真实构造带子命令、互斥参数组、类型转换的argparse工具,验证Namespace的合并方式和各种校验失败时统一的SystemExit(2)错误处理机制。
代码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(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这个字段。
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 = 2g = 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这一层用参数声明清楚,具体的报错格式和退出码完全不需要业务代码自己处理,用户看到的错误提示风格在整个工具里真实是一致的。
每一步都是本文上面用真实的argparse跑出来的原始结果。
argparse