python
42 lines · 6 steps
Building a subcommand CLI with argparse
How argparse subparsers dispatch to per-command handler functions in a Python tool.
Explained by
highlit
1import argparse
2import sys
3from pathlib import Path
4
5
6def cmd_init(args):
7 target = Path(args.directory)
8 target.mkdir(parents=True, exist_ok=True)
9 (target / "config.toml").write_text(f"name = \"{args.name}\"\n")
10 print(f"Initialized project in {target}")
11
12
13def cmd_build(args):
14 src = Path(args.source)
15 if not src.exists():
16 sys.exit(f"error: source {src} does not exist")
17 flags = ["--release"] if args.release else []
18 print(f"Building {src} {' '.join(flags)}".rstrip())
19
20
21def build_parser():
22 parser = argparse.ArgumentParser(prog="proj", description="Project tool")
23 parser.add_argument("-v", "--verbose", action="count", default=0)
24 sub = parser.add_subparsers(dest="command", required=True)
25
26 p_init = sub.add_parser("init", help="create a new project")
27 p_init.add_argument("directory")
28 p_init.add_argument("-n", "--name", default="untitled")
29 p_init.set_defaults(func=cmd_init)
30
31 p_build = sub.add_parser("build", help="build the project")
32 p_build.add_argument("source", nargs="?", default="src")
33 p_build.add_argument("--release", action="store_true")
34 p_build.set_defaults(func=cmd_build)
35
36 return parser
37
38
39def main(argv=None):
40 parser = build_parser()
41 args = parser.parse_args(argv)
42 args.func(args)
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Subparsers let one program expose distinct commands, each with its own arguments and defaults.
- 2Attaching a handler via set_defaults(func=...) turns argument parsing into clean command dispatch.
- 3Keeping parsing in build_parser and logic in cmd_* functions makes the CLI easy to test and extend.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
python
from typing import Any, Sequence, Mapping def render_markdown_table(
Rendering an aligned Markdown table in Python
string formatting
data transformation
closures
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-subcommand-cli-with-argparse-explained-python-2682/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.