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
- Explain the task with a real input and output example.
- Say whether you can install dependencies or must stick to the standard library.
- Save the script and tests, and run
pytestbefore using it on real data. - Always try it with
--dry-runfirst.
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.