1.CLI application structure
add_subparsers() splits one program into named subcommands (tool migrate, tool status), each with its own arguments and its own function — instead of one parser with a growing pile of flags.
Key features
- parser.add_subparsers(dest="command", required=True) turns "tool X" into a named subcommand X
- sub.add_parser("migrate") returns its own ArgumentParser — arguments added to it apply only to that subcommand
- set_defaults(func=handler) attaches a handler function directly to a subcommand, avoiding an if/elif dispatch chain
- add_argument_group("connection") clusters related flags together in --help output without changing parsing
- A real CLI keeps argument parsing in one function and business logic in another, so the logic is testable without argv
Structuring a multi-command CLI
- sys.argv — dbtool migrate --target 0007
- leads to ArgumentParser
- ArgumentParser — add_subparsers(dest="command")
- leads to migrate subparser (command == "migrate")
- leads to status subparser (command == "status")
- migrate subparser — --target flag, func=cmd_migrate
- leads to args.func(args)
- status subparser — func=cmd_status
- leads to args.func(args)
- args.func(args) — runs the matched handler
Command
parser = argparse.ArgumentParser(prog="tool")
sub = parser.add_subparsers(dest="command", required=True)
migrate_p = sub.add_parser("migrate")
migrate_p.add_argument("--target", default="latest")
migrate_p.set_defaults(func=cmd_migrate)
args = parser.parse_args()
args.func(args) # dispatches without an if/elif chainadd_subparsers() turns one flat argument list into named subcommands, each with its own arguments and its own set_defaults(func=...) handler — no if/elif dispatch needed.