← Recipes

阅读中文版本

Build argparse Subcommands

Structure a Python CLI with subparsers so each command has its own arguments and a shared top-level parser.

~30 minclipythonargparse

Task

Add subcommands to an argparse CLI with isolated argument sets and help.

When to use this

A single flat CLI accumulates conflicting flags and its help becomes hard to read as features grow.

Prerequisites

  • Python 3 with argparse
  • A CLI entry point
  • A set of intended commands

Steps

1. Create subparsers for each command.

import argparse
p = argparse.ArgumentParser(prog='tool')
sub = p.add_subparsers(dest='command', required=True)
sync = sub.add_parser('sync', help='sync data')
sync.add_argument('--since')
listp = sub.add_parser('list', help='list items')
listp.add_argument('--limit', type=int, default=20)
args = p.parse_args()

Expected output: Each command parses its own arguments and the help organises options per command.

2. Dispatch on the selected command.

if args.command == 'sync':
    run_sync(args.since)
elif args.command == 'list':
    run_list(args.limit)

Expected output: The chosen subcommand routes to its handler without flag clashes.

Troubleshooting

Problem

Global options needed everywhere

Solution

Define shared options on the top-level parser and read them after parse.

Problem

Command required but none given

Solution

Set required=True on the subparsers to produce a clear error.

Limitations

  • Nested subcommands get verbose.
  • argparse help formatting is limited.
  • Does not cover shell completion by itself.

What this recipe does

Add subcommands to an argparse CLI with isolated argument sets and help.

A single flat CLI accumulates conflicting flags and its help becomes hard to read as features grow.

Prerequisites

  • Python 3 with argparse
  • A CLI entry point
  • A set of intended commands

Procedure

  1. Create subparsers for each command.
import argparse
p = argparse.ArgumentParser(prog='tool')
sub = p.add_subparsers(dest='command', required=True)
sync = sub.add_parser('sync', help='sync data')
sync.add_argument('--since')
listp = sub.add_parser('list', help='list items')
listp.add_argument('--limit', type=int, default=20)
args = p.parse_args()

Expected result: Each command parses its own arguments and the help organises options per command.

  1. Dispatch on the selected command.
if args.command == 'sync':
    run_sync(args.since)
elif args.command == 'list':
    run_list(args.limit)

Expected result: The chosen subcommand routes to its handler without flag clashes.

How to confirm it worked

Run the commands above and confirm every expected result in order. If a step does not produce the stated output, use the troubleshooting notes before continuing. A passing run shows each subcommand has isolated help and flags, and invoking an unknown command exits with a clear error.

Troubleshooting

Global options needed everywhere — Define shared options on the top-level parser and read them after parse.

Command required but none given — Set required=True on the subparsers to produce a clear error.

Limitations

  • Nested subcommands get verbose.
  • argparse help formatting is limited.
  • Does not cover shell completion by itself.