Any tool with more than one job (init, run, deploy) is easier to use with subcommands.
Tools like git commit and pip install use subcommands: the first positional chooses a command, and each command has its own arguments.
sub = parser.add_subparsers(dest="command", required=True)
run = sub.add_parser("run", help="run the app")
run.add_argument("--port", type=int, default=8000)add_subparsers() returns a special action; each add_parser(name) returns an ordinary ArgumentParser for that command. dest="command" stores which command was chosen, and required=True makes omitting it an error (subcommands are optional by default).
The neat dispatch idiom is set_defaults: attach the handler function to each subparser and call it after parsing.
run.set_defaults(func=cmd_run)
args = parser.parse_args()
args.func(args)Useful extras:
add_parser("install", aliases=["i"])for short names,- options defined on the main parser go before the subcommand (
tool -v run), while subparser options go after it, - shared options can be added via parent parsers (an expert lesson),
- each subparser gets its own
--help:tool run --help.
Example
import argparse
def cmd_init(args):
print(f"init in {args.path}")
def cmd_run(args):
print(f"run on port {args.port}, verbose={args.verbose}")
p = argparse.ArgumentParser(prog="app")
p.add_argument("-v", "--verbose", action="store_true")
sub = p.add_subparsers(dest="command", required=True)
init = sub.add_parser("init", help="create a project")
init.add_argument("path", nargs="?", default=".")
init.set_defaults(func=cmd_init)
run = sub.add_parser("run", aliases=["r"], help="start the server")
run.add_argument("--port", type=int, default=8000)
run.set_defaults(func=cmd_run)
for argv in (["init"], ["-v", "r", "--port", "9000"]):
args = p.parse_args(argv)
args.func(args)
# init in .
# run on port 9000, verbose=TrueRemember
- add_subparsers() + add_parser(name) gives each command its own parser
- Use dest= and required=True to record and require the command
- set_defaults(func=handler) makes dispatch a one-liner
Watch out for
- Putting a main-parser option after the subcommand name, where the subparser doesn't recognise it.