Python CLI (argparse)

argparse 기반 Python CLI 도구 템플릿. 서브커맨드, 버전 출력, 오류 처리 패턴 포함.

Gist
#!/usr/bin/env python3
import argparse
import sys
from pathlib import Path
from typing import Optional

__version__ = "1.0.0"


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="mytool",
        description="Brief description of your CLI tool.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
examples:
  %(prog)s process input.txt -o output.txt
  %(prog)s process input.txt --verbose --dry-run
  %(prog)s validate config.yaml
        """,
    )
    parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")

    sub = parser.add_subparsers(dest="command", metavar="COMMAND", required=True)

    p = sub.add_parser("process", help="Process a file")
    p.add_argument("input", type=Path, help="Input file path")
    p.add_argument("-o", "--output", type=Path, help="Output file path")
    p.add_argument("-v", "--verbose", action="store_true")
    p.add_argument("-n", "--dry-run", action="store_true")

    v = sub.add_parser("validate", help="Validate a config file")
    v.add_argument("config", type=Path, help="Config file path")
    v.add_argument("--strict", action="store_true", help="Fail on warnings")

    return parser


def cmd_process(args: argparse.Namespace) -> int:
    if not args.input.exists():
        print(f"error: {args.input} not found", file=sys.stderr)
        return 1
    if args.verbose:
        print(f"processing {args.input} ...")
    if args.dry_run:
        print(f"[dry-run] would write to {args.output or args.input.with_suffix('.out')}")
        return 0
    dest = args.output or args.input.with_suffix(".out")
    dest.write_bytes(args.input.read_bytes())
    print(f"written → {dest}")
    return 0


def cmd_validate(args: argparse.Namespace) -> int:
    if not args.config.exists():
        print(f"error: {args.config} not found", file=sys.stderr)
        return 1
    print(f"✓ {args.config} looks valid")
    return 0


def main(argv: Optional[list] = None) -> int:
    args = build_parser().parse_args(argv)
    dispatch = {"process": cmd_process, "validate": cmd_validate}
    return dispatch[args.command](args)


if __name__ == "__main__":
    sys.exit(main())