argparse is Python's standard library for building CLIs. Here's how to use it effectively.

Basic Usage

import argparse
 
parser = argparse.ArgumentParser(description="Process some files")
parser.add_argument("filename", help="File to process")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
 
args = parser.parse_args()
print(args.filename)
print(args.verbose)
python script.py data.txt -v

Positional Arguments

Required arguments by position:

parser.add_argument("input", help="Input file")
parser.add_argument("output", help="Output file")
python script.py input.txt output.txt

Optional Arguments

Flags with - or --:

# Boolean flag
parser.add_argument("-v", "--verbose", action="store_true")
 
# With value
parser.add_argument("-n", "--count", type=int, default=10)
 
# Required optional
parser.add_argument("-c", "--config", required=True)

Argument Types

# Integer
parser.add_argument("-n", type=int)
 
# Float
parser.add_argument("-r", "--rate", type=float)
 
# File (opens automatically)
parser.add_argument("-f", type=argparse.FileType("r"))
 
# Path validation
from pathlib import Path
parser.add_argument("-d", type=Path)

Choices

Restrict to specific values:

parser.add_argument(
    "--format",
    choices=["json", "csv", "xml"],
    default="json"
)
 
parser.add_argument(
    "--level",
    type=int,
    choices=range(1, 11)  # 1-10
)

Multiple Values

# Fixed number
parser.add_argument("files", nargs=2)
 
# One or more
parser.add_argument("files", nargs="+")
 
# Zero or more
parser.add_argument("files", nargs="*")
 
# Optional positional
parser.add_argument("output", nargs="?", default="out.txt")

Actions

# Store True/False
parser.add_argument("-v", action="store_true")
parser.add_argument("--no-cache", action="store_false", dest="cache")
 
# Count occurrences
parser.add_argument("-v", action="count", default=0)
# -v -> 1, -vv -> 2, -vvv -> 3
 
# Append to list
parser.add_argument("-i", "--include", action="append")
# -i a -i b -> ["a", "b"]
 
# Store constant
parser.add_argument("--debug", action="store_const", const=10)

Default Values

parser.add_argument("-n", type=int, default=100)
parser.add_argument("--name", default="world")
 
# Environment variable fallback
import os
parser.add_argument(
    "--api-key",
    default=os.environ.get("API_KEY")
)

Help Text

parser = argparse.ArgumentParser(
    description="Process data files",
    epilog="Example: %(prog)s data.csv -o output.json"
)
 
parser.add_argument(
    "-o", "--output",
    metavar="FILE",
    help="Output file (default: stdout)"
)

Subcommands

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
 
# 'init' subcommand
init_parser = subparsers.add_parser("init", help="Initialize project")
init_parser.add_argument("name")
 
# 'build' subcommand
build_parser = subparsers.add_parser("build", help="Build project")
build_parser.add_argument("-o", "--output", default="dist")
 
args = parser.parse_args()
 
if args.command == "init":
    print(f"Initializing {args.name}")
elif args.command == "build":
    print(f"Building to {args.output}")
python cli.py init myproject
python cli.py build -o ./build

Mutually Exclusive

group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")

Argument Groups

parser = argparse.ArgumentParser()
 
input_group = parser.add_argument_group("Input options")
input_group.add_argument("-i", "--input")
input_group.add_argument("-f", "--format")
 
output_group = parser.add_argument_group("Output options")
output_group.add_argument("-o", "--output")
output_group.add_argument("--compress", action="store_true")

Config File Pattern

import argparse
import json
 
def load_config(path):
    with open(path) as f:
        return json.load(f)
 
parser = argparse.ArgumentParser()
parser.add_argument("-c", "--config", type=load_config, default={})
parser.add_argument("--name")
 
args = parser.parse_args()
 
# Config file provides defaults, CLI overrides
config = args.config
if args.name:
    config["name"] = args.name

Complete Example

#!/usr/bin/env python3
import argparse
import sys
 
def main():
    parser = argparse.ArgumentParser(
        description="Convert files between formats",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  %(prog)s input.csv -o output.json
  %(prog)s data.xml --format yaml -v
        """
    )
    
    parser.add_argument("input", help="Input file")
    parser.add_argument(
        "-o", "--output",
        help="Output file (default: stdout)"
    )
    parser.add_argument(
        "-f", "--format",
        choices=["json", "yaml", "csv"],
        default="json",
        help="Output format (default: json)"
    )
    parser.add_argument(
        "-v", "--verbose",
        action="count",
        default=0,
        help="Increase verbosity"
    )
    parser.add_argument(
        "--version",
        action="version",
        version="%(prog)s 1.0.0"
    )
    
    args = parser.parse_args()
    
    if args.verbose >= 2:
        print(f"Debug: {args}")
    
    # Process files...
    process(args.input, args.output, args.format)
 
if __name__ == "__main__":
    main()

Quick Reference

import argparse
 
parser = argparse.ArgumentParser(description="...")
 
# Positional
parser.add_argument("name")
 
# Optional
parser.add_argument("-n", "--name", default="value")
 
# Boolean
parser.add_argument("-v", action="store_true")
 
# With type
parser.add_argument("-n", type=int)
 
# Choices
parser.add_argument("--fmt", choices=["a", "b"])
 
# Multiple values
parser.add_argument("files", nargs="+")
 
# Parse
args = parser.parse_args()
print(args.name)

argparse handles the boring parts of CLI building—parsing, validation, help text. Focus on your logic.

React to this post: