Robust Python CLI script with tests

Write a production-ready Python command-line script: argparse, logging, error handling, exit codes and pytest tests. Designed for ChatGPT, Claude and Microsoft Copilot. It includes 6 customizable variables and you can copy it for free in one click.

Intermediate · Code · Pending editorial review

Prompt

Act as a senior Python developer who writes maintainable internal tools.

I need a command-line script called `{{script_name}}` that does the following: {{task}}.
Inputs: {{inputs}}.
Expected outputs: {{outputs}}.
Python version: {{python_version}}. Allowed dependencies: {{dependencies}}.

Deliver, in this order:
1. Interface design: subcommands (if they make sense), positional arguments, options with defaults, and a sample `--help` output.
2. The complete code in a single file, with:
   - `argparse`, `main(argv=None) -> int` and an `if __name__ == "__main__"` guard.
   - Configurable `logging` via `--verbose` and `--quiet`, messages to stderr and results to stdout.
   - Full type hints, `pathlib` for paths, and small pure functions kept separate from I/O.
   - A `--dry-run` flag if the script modifies files or external systems.
   - Documented exit codes (0 success, 1 usage error, 2 data error, etc.).
3. Error handling: which exceptions are caught, clear user-facing messages, and no tracebacks unless in verbose mode.
4. `pytest` tests (using `tmp_path` and `capsys`) for the happy path, invalid inputs and edge cases.
5. Usage instructions and how to package it with `pyproject.toml` as an installable command.

Constraints:
- Follow PEP 8 and do not use dependencies outside the allowed list.
- Do not assume data formats I have not described: ask me or use [FILL IN].

How to use it

  1. Explain the task with a real input and output example.
  2. Say whether you can install dependencies or must stick to the standard library.
  3. Save the script and tests, and run pytest before using it on real data.
  4. Always try it with --dry-run first.

Usage example

Script: rename-invoices · Task: rename invoice PDFs using the date and vendor from a CSV · Standard library only.

Sample output

2. Code (excerpt from rename_invoices.py)

from __future__ import annotations

import argparse
import csv
import logging
import sys
from pathlib import Path

log = logging.getLogger("rename-invoices")


def new_name(date: str, vendor: str) -> str:
    """Pure function: '2025-03-14', 'Acme Paper' -> '2025-03-14_acme-paper.pdf'."""
    return f"{date}_{'-'.join(vendor.lower().split())}.pdf"


def read_csv(path: Path) -> dict[str, str]:
    with path.open(newline="", encoding="utf-8") as f:
        return {row["file"]: new_name(row["date"], row["vendor"]) for row in csv.DictReader(f)}


def main(argv: list[str] | None = None) -> int:
    p = argparse.ArgumentParser(prog="rename-invoices", description="Rename invoice PDFs from a CSV.")
    p.add_argument("folder", type=Path)
    p.add_argument("csv", type=Path)
    p.add_argument("--dry-run", action="store_true", help="show changes without applying them")
    level = p.add_mutually_exclusive_group()
    level.add_argument("-v", "--verbose", action="store_true")
    level.add_argument("-q", "--quiet", action="store_true")
    args = p.parse_args(argv)
    logging.basicConfig(level=logging.DEBUG if args.verbose else logging.ERROR if args.quiet else logging.INFO)

    try:
        changes = read_csv(args.csv)
    except (OSError, KeyError) as e:
        log.error("Cannot read CSV: %s", e)
        return 2
    for old, new in changes.items():
        src, dst = args.folder / old, args.folder / new
        if not src.exists() or dst.exists():
            log.warning("Skipped: %s", old)
            continue
        print(f"{old} -> {new}")
        if not args.dry_run:
            src.rename(dst)
    return 0


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

Exit codes: 0 success, 1 usage error (argparse uses 2 by default: [FILL IN] if you want them unified), 2 data error. The CSV date format hasn't been described: [FILL IN]…

Key facts

Category
Programming & development · Python
Output type
Code
Level
Intermediate
Designed for
ChatGPT, Claude and Microsoft Copilot
Verified in
Pending editorial verification
Variables
Script name, Task, Inputs, Outputs, Python version and Allowed dependencies
Language
English
Price
Free, no sign-up
Updated
Nov 7, 2026

Frequently asked questions

Why argparse instead of Click or Typer?

Argparse ships with Python and adds no dependencies, which helps on locked-down servers. If you can install packages, list Typer or Click as allowed dependencies and the prompt will adapt.

What do I gain by separating logic from I/O?

You can test the logic with fast tests that never touch real files, and reuse it from other scripts or an API.

What is this prompt for?

Write a production-ready Python command-line script: argparse, logging, error handling, exit codes and pytest tests.

Which AI tools does it work with?

It is designed for ChatGPT, Claude and Microsoft Copilot. We have not verified it in these tools yet, so results may vary between them.

How do I customize the prompt?

Click “Customize” and fill in the variables (Script name, Task, Inputs, Outputs, Python version and Allowed dependencies). The text updates instantly and you can then copy it in one click. Values are stored only in your browser.

Do I need experience to use it?

It is intermediate: it helps to know the topic so you can provide good context and review the result critically.

Is this prompt free?

Yes. Every HomeCasa Prompts prompt is free, with no sign-up or subscription. You can also use them in professional projects.

Can I trust the AI output?

Treat it as a high-quality draft: always check data, figures and important claims before publishing or making decisions.

Related prompts