# Subcommands with argparse Multi-command CLI like `git`, `docker`, or `kubectl` using subparsers. ## Template Reference `templates/subparser-pattern.py` ## Overview Create CLIs with multiple commands: - `mycli init` - Initialize project - `mycli deploy production` - Deploy to environment - `mycli status` - Show status Each subcommand has its own arguments and options. ## Quick Start ```bash # View main help python subparser-pattern.py --help # View subcommand help python subparser-pattern.py init --help python subparser-pattern.py deploy --help # Execute subcommands python subparser-pattern.py init --template react python subparser-pattern.py deploy production --force python subparser-pattern.py status --format json ``` ## Key Patterns ### 1. Create Subparsers ```python parser = argparse.ArgumentParser(description='Multi-command CLI') subparsers = parser.add_subparsers( dest='command', # Store command name in args.command help='Available commands', required=True # At least one command required (Python 3.7+) ) ``` **Important:** Set `dest='command'` to access which command was used. ### 2. Add Subcommand ```python init_parser = subparsers.add_parser( 'init', help='Initialize a new project', description='Initialize a new project with specified template' ) init_parser.add_argument('--template', default='basic') init_parser.add_argument('--path', default='.') ``` Each subcommand is a separate parser with its own arguments. ### 3. Set Command Handler ```python def cmd_init(args): """Initialize project.""" print(f"Initializing with {args.template} template...") init_parser.set_defaults(func=cmd_init) ``` **Dispatch pattern:** ```python args = parser.parse_args() return args.func(args) # Call the appropriate handler ``` ### 4. Subcommand with Choices ```python deploy_parser = subparsers.add_parser('deploy') deploy_parser.add_argument( 'environment', choices=['development', 'staging', 'production'], help='Target environment' ) deploy_parser.add_argument( '--mode', choices=['fast', 'safe', 'rollback'], default='safe' ) ``` ## Complete Example ```python #!/usr/bin/env python3 import argparse import sys def cmd_init(args): print(f"Initializing with {args.template} template") return 0 def cmd_deploy(args): print(f"Deploying to {args.environment}") return 0 def main(): parser = argparse.ArgumentParser(description='My CLI Tool') parser.add_argument('--version', action='version', version='1.0.0') subparsers = parser.add_subparsers(dest='command', required=True) # Init command init_parser = subparsers.add_parser('init', help='Initialize project') init_parser.add_argument('--template', default='basic') init_parser.set_defaults(func=cmd_init) # Deploy command deploy_parser = subparsers.add_parser('deploy', help='Deploy app') deploy_parser.add_argument( 'environment', choices=['dev', 'staging', 'prod'] ) deploy_parser.set_defaults(func=cmd_deploy) # Parse and dispatch args = parser.parse_args() return args.func(args) if __name__ == '__main__': sys.exit(main()) ``` ## Help Output ### Main Help ``` usage: mycli [-h] [--version] {init,deploy,status} ... Multi-command CLI tool positional arguments: {init,deploy,status} Available commands init Initialize a new project deploy Deploy application to environment status Show deployment status optional arguments: -h, --help show this help message and exit --version show program's version number and exit ``` ### Subcommand Help ```bash $ python mycli.py deploy --help usage: mycli deploy [-h] [-f] [-m {fast,safe,rollback}] {development,staging,production} positional arguments: {development,staging,production} Target environment optional arguments: -h, --help show this help message and exit -f, --force Force deployment without confirmation -m {fast,safe,rollback}, --mode {fast,safe,rollback} Deployment mode (default: safe) ``` ## Accessing Parsed Values ```python args = parser.parse_args() # Which command was used? print(args.command) # 'init', 'deploy', or 'status' # Command-specific arguments if args.command == 'deploy': print(args.environment) # 'production' print(args.force) # True/False print(args.mode) # 'safe' ``` ## Common Patterns ### Pattern 1: Switch on Command ```python args = parser.parse_args() if args.command == 'init': init_project(args) elif args.command == 'deploy': deploy_app(args) elif args.command == 'status': show_status(args) ``` ### Pattern 2: Function Dispatch (Better) ```python # Set handlers init_parser.set_defaults(func=cmd_init) deploy_parser.set_defaults(func=cmd_deploy) # Dispatch args = parser.parse_args() return args.func(args) ``` ### Pattern 3: Check if Command Provided ```python args = parser.parse_args() if not args.command: parser.print_help() sys.exit(1) ``` **Note:** Use `required=True` in `add_subparsers()` to make this automatic. ## Common Mistakes ### ❌ Wrong: Forgetting dest ```python subparsers = parser.add_subparsers(dest='command') # ✓ Can check args.command ``` ```python subparsers = parser.add_subparsers() # ✗ Can't access which command ``` ### ❌ Wrong: Accessing wrong argument ```python # deploy_parser defines 'environment' # init_parser defines 'template' args = parser.parse_args(['deploy', 'prod']) print(args.environment) # ✓ Correct print(args.template) # ✗ Error - not defined for deploy ``` ### ❌ Wrong: No required=True (Python 3.7+) ```python subparsers = parser.add_subparsers(dest='command', required=True) # ✓ ``` ```python subparsers = parser.add_subparsers(dest='command') # ✗ Command optional # User can run: python mycli.py (no command) ``` ## Nested Subcommands For multi-level commands like `git config get`, see: - `nested-commands.md` - `templates/nested-subparser.py` ## Next Steps - **Nested Commands:** See `nested-commands.md` - **Validation:** See `validation-patterns.md` - **Complex CLIs:** See `advanced-parsing.md`