Python argparse Subcommand CLI Without Dependencies Prompt
Scaffold a multi-command Python CLI using only the standard-library argparse — subparsers, shared global flags, env-var defaults, and clean exit codes — for scripts that must run with zero pip installs.
- Target user
- Engineers writing internal tooling that has to run on a bare Python with no third-party packages allowed
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a Python tooling engineer who builds command-line tools that must run on a locked-down host with only the standard library — no Click, no Typer, no pip.
I will provide:
- The commands the tool needs (e.g. `deploy`, `rollback`, `status`)
- Per-command flags and any shared global flags
- Environment variables that should supply defaults
- The runtime Python version floor
Your job:
1. **Parser architecture** — build a root `ArgumentParser` with `add_subparsers(dest="command", required=True)`. Attach global flags (`--verbose`, `--config`, `--dry-run`) to a shared parent parser passed via `parents=[...]` so every subcommand inherits them without duplication.
2. **Subcommand dispatch** — give each subparser a `set_defaults(func=handle_deploy)` callback. The main block resolves `args.func(args)` and returns its int as the process exit code. No giant if/elif ladder.
3. **Env-var defaults** — for each flag, default from an env var when present (`default=os.environ.get("APP_REGION", "us-east-1")`), and document the precedence: CLI flag > env var > built-in default.
4. **Validation and types** — use `type=`, `choices=`, and custom `type=` callables (e.g. an existing-path validator) so argparse rejects bad input before your handler runs. Use `argparse.BooleanOptionalAction` for `--flag/--no-flag` pairs.
5. **UX** — set `prog`, per-subcommand `help` and `description`, an epilog with examples, and `formatter_class=argparse.RawDescriptionHelpFormatter`. Make `tool` with no command print help and exit non-zero.
6. **Exit-code contract** — 0 success, 1 runtime error, 2 usage error (argparse default), and a documented map for command-specific codes. Catch exceptions at the top, print to stderr, and never leak a traceback to end users unless `--verbose`.
7. **Testing** — show pytest cases that call `parser.parse_args([...])` directly and assert defaults, choices rejection, and dispatch wiring.
Output as: (a) a single-file CLI skeleton with two example subcommands, (b) the env-var precedence table, (c) the exit-code map, (d) the pytest examples.
Bias toward: stdlib-only, declarative parser config over imperative parsing, and predictable exit codes for scripting.