How to Build a Subcommand Parser Tree with argparse in Python
Create a CLI with nested subcommands (like git) using argparse subparsers, where each subcommand maps to its own handler function.
Python code
31 linesimport argparse
def cmd_add(args):
print(f"Adding {args.num1} + {args.num2} = {args.num1 + args.num2}")
def cmd_sub(args):
print(f"Subtracting {args.num1} - {args.num2} = {args.num1 - args.num2}")
def main():
parser = argparse.ArgumentParser(prog="calculator")
subparsers = parser.add_subparsers(dest="command", required=True)
parser_add = subparsers.add_parser("add", help="Add two numbers")
parser_add.add_argument("num1", type=int)
parser_add.add_argument("num2", type=int)
parser_add.set_defaults(func=cmd_add)
parser_sub = subparsers.add_parser("sub", help="Subtract two numbers")
parser_sub.add_argument("num1", type=int)
parser_sub.add_argument("num2", type=int)
parser_sub.set_defaults(func=cmd_sub)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
Output
python calculator.py add 10 5
Adding 10 + 5 = 15
python calculator.py sub 10 5
Subtracting 10 - 5 = 5
python calculator.py
usage: calculator [-h] {add,sub} ...
calculator: error: the following arguments are required: command
How it works
add_subparsers creates a dispatch point where the first positional argument selects which parser branch to use. Setting dest="command" stores the chosen subcommand name in args.command. The required=True flag forces a subcommand to be provided. Each subparser is an independent ArgumentParser with its own arguments and help text. set_defaults(func=...) attaches the handler function to the namespace, so after parse_args you can call args.func(args) to dispatch to the correct logic. This pattern keeps each command's argument parsing isolated while sharing a single entry point.
Common mistakes
- Forgetting `required=True` on `add_subparsers`, which lets the program run silently without a command
- Not attaching `set_defaults(func=...)` and trying to look up the handler with an if/elif on `args.command`
- Reusing the same argument names across subparsers, which can cause shadowing or confusion if not scoped properly
Variations
- Add a third subcommand like 'mul' with its own parser and handler, following the same pattern
- Use `parser.set_defaults(func=main_help)` as a fallback for when no subcommand is given, if you want a default behavior
Real-world use cases
- Building CLI tools like 'git add', 'git commit', and 'git push' where each verb is a separate subcommand with different flags.
- Creating DevOps scripts with subcommands like 'deploy --env prod' and 'rollback --version 1.2' that run different deployment logic.
- Implementing database migration tools with commands like 'migrate up' and 'migrate down', each accepting their own options.
Sponsored
More from Functions & basics
- Add Type Hints to Function Parameters and Return in Python easy
- Benchmark list append vs comprehension in Python easy
- Build a Context Manager in Python with contextlib.contextmanager easy
- Build a Progress Callback Function for Loops in Python easy
- Cache expensive function with lru_cache in Python easy
- Calculate Time Difference Across Time Zones in Python easy
Keep learning
Related tutorials and quizzes for this topic.