Introduction
LLMs can generate Python code that actually runs — if you prompt them precisely. The 8 prompts below are the exact templates I use with GPT-4, Claude, and Copilot. They move from a simple CLI script to a FastAPI microservice with async SQLAlchemy. Each prompt specifies the stack, the constraints, and the expected output. This is what separates a toy demo from code you can ship. The recommendations are based on official sources: Python docs (docs.python.org), FastAPI docs (fastapi.tiangolo.com), pytest docs (docs.pytest.org), and Docker best practices (docs.docker.com).
Before You Begin
A strong prompt contains: the input format, the output format, the list of libraries, and your acceptance criteria. Include them all, and you'll get code that fits your project. Now let's look at the prompts.
The Prompts at a Glance
| # | Prompt | Best for |
|---|---|---|
| 1 | CLI Script | Command-line tools |
| 2 | Pandas Analysis | Data cleaning and reports |
| 3 | Async HTTP Client | Calling REST APIs concurrently |
| 4 | FastAPI CRUD | REST endpoints in minutes |
| 5 | FastAPI + SQLAlchemy | Production database access |
| 6 | Pytest Suite | Regression tests for your API |
| 7 | Dockerfile | Containerizing your app |
| 8 | GitHub Actions CI | Automating lint, test, build |
Prompt 1: CLI Script Boilerplate
Use: Generate a command-line tool with proper argument parsing and error handling.
Prompt:
Write a Python 3 script using argparse, Pathlib, type hints, and a main() function. Scan a directory for .txt files, count words, print top N words. Handle missing files with exit code 1.
Sample output:
import argparse
from pathlib import Path
from collections import Counter
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("dir", type=Path)
parser.add_argument("--top", type=int, default=10)
args = parser.parse_args()
if not args.dir.exists():
raise SystemExit(1)
counter = Counter()
for path in args.dir.glob("*.txt"):
counter.update(path.read_text().split())
for word, count in counter.most_common(args.top):
print(f"{word}: {count}")
if __name__ == "__main__":
main()
Why it works: It names argparse, Pathlib, and the exact failure behavior. This follows the official argparse tutorial.
Prompt 2: Pandas Data Analysis
Use: For cleaning and aggregating CSV data.
Prompt:
Write a pandas script with argparse. Read a CSV, drop rows with empty 'email', convert 'signup_date' to datetime, group by 'country', average 'revenue', print a table and save to CSV.
Sample output:
import argparse
import pandas as pd
def main():
parser = argparse.ArgumentParser()
parser.add_argument("input")
args = parser.parse_args()
df = pd.read_csv(args.input)
df = df.dropna(subset=["email"])
df["signup_date"] = pd.to_datetime(df["signup_date"])
result = df.groupby("country")["revenue"].mean().round(2).reset_index()
print(result.to_string(index=False))
result.to_csv("result.csv", index=False)
if __name__ == "__main__":
main()
Why it works: The prompt names the methods (dropna, pd.to_datetime, groupby) from pandas user guide.
Prompt 3: Async HTTP Client
Use: Fetch data from several APIs concurrently.
Prompt:
Write an async httpx client that fetches star counts from the GitHub API for psf/requests and pallets/flask using asyncio.gather. Add retry with exponential backoff and a 10-second timeout.
Sample output:
import asyncio, httpx
async def get_stars(repo):
for attempt in range(3):
try:
async with httpx.AsyncClient(timeout=10) as c:
r = await c.get(f"https://api.github.com/repos/{repo}")
r.raise_for_status()
return repo, r.json()["stargazers_count"]
except httpx.HTTPStatusError:
if attempt == 2: raise
await asyncio.sleep(2 ** attempt)
async def main():
repos = ["psf/requests", "pallets/flask"]
results = await asyncio.gather(*(get_stars(r) for r in repos))
for r, s in results: print(r, s)
asyncio.run(main())
Why it works: The prompt references real endpoints and the exact concurrency primitive. Based on httpx docs.
Prompt 4: FastAPI CRUD
Use: Build a validated REST API with in-memory storage.
Prompt:
Create a FastAPI app with Pydantic models. Endpoints: GET /books, POST /books, GET /books/{id}, PUT /books/{id}, DELETE /books/{id}. Validate: title non-empty, year between 1900 and 2026. Return 404 if book not found. Use response_model.
Sample output (snippet):
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI()
books = {}
counter = 1
class Book(BaseModel):
title: str = Field(min_length=1)
author: str
year: int = Field(ge=1900, le=2026)
@app.get("/books", response_model=list[Book])
def list_books():
return list(books.values())
@app.post("/books", status_code=201, response_model=Book)
def create_book(book: Book):
global counter
books[counter] = book
counter += 1
return book
@app.get("/books/{id}", response_model=Book)
def get_book(id: int):
if id not in books: raise HTTPException(404, "Book not found")
return books[id]
Why it works: This mirrors FastAPI's official tutorial. The Field constraint is a Pydantic pattern.
Prompt 5: FastAPI + Async SQLAlchemy
Use: For database-backed apps with async sessions.
Prompt:
Write a FastAPI app using SQLAlchemy 2.0 async with PostgreSQL. Define a User model (id, name, email unique). Endpoints POST /users and GET /users/{id}. Use async_sessionmaker as a dependency. DATABASE_URL env var with fallback.
Sample output (key lines):
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from fastapi import Depends, FastAPI
import os
engine = create_async_engine(os.getenv("DATABASE_URL", "postgresql+asyncpg://u:p@localhost/db"))
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)
async def get_session():
async with SessionLocal() as s:
yield s
@app.post("/users", status_code=201)
async def create_user(name: str, email: str, session=Depends(get_session)):
user = User(name=name, email=email)
session.add(user)
await session.commit()
return {"id": user.id, "name": name, "email": email}
Why it works: For SQLAlchemy 2.0 you must specify async_sessionmaker. See official async docs.
Prompt 6: Pytest Test Suite
Use: Write tests for the FastAPI app.
Prompt:
Create pytest tests using fastapi.testclient.TestClient. Cover GET /books → empty list, POST /books → 201, GET /books/{id} → 404 when missing, DELETE → 204. Use a fixture to reset books before each test.
Sample output:
import pytest
from fastapi.testclient import TestClient
from main import app, books
@pytest.fixture
def client():
books.clear()
with TestClient(app) as c:
yield c
def test_empty(client):
assert client.get("/books").json() == []
def test_create(client):
resp = client.post("/books", json={"title":"Dune","author":"Herbert","year":1965})
assert resp.status_code == 201
def test_404(client):
assert client.get("/books/999").status_code == 404
Why it works: Fixtures are the recommended pytest isolation mechanism.
Prompt 7: Multi-stage Dockerfile
Use: Build a slim, secure image.
Prompt:
Create a multi-stage Dockerfile for a FastAPI app. Stage 1: python:3.12-slim, pip wheel. Stage 2: copy wheels, install, add non-root user, expose 8000, run uvicorn with 4 workers.
Sample output:
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-index --find-links=/wheels -r requirements.txt
RUN adduser --disabled-password --gecos "" appuser
COPY app /app
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--workers", "4"]
Why it works: Matches Docker best practices: multi-stage, non-root, pinned base image.
Prompt 8: GitHub Actions CI
Use: Automate lint, test, and build.
Prompt:
Write a GitHub Actions workflow for a Python project on push and pull_request to main. Jobs: ruff lint (--line-length=100), mypy check, pytest, and Docker build to GHCR. Use checkout@v4, setup-python@v5, and pip cache.
Sample output:
name: CI
on:
push: {branches: [main]}
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: {python-version: "3.12", cache: pip}
- run: pip install ruff && ruff check . --line-length=100
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: {python-version: "3.12", cache: pip}
- run: pip install -r requirements.txt pytest && pytest
Why it works: Action versions matter; the prompt pins them.
Conclusion
These eight prompts are a daily tool in my workflow. Using them, you'll cut boilerplate time and get code that follows community best practices. But never commit generated code without a review. Run a linter, run the tests, and compare against the official documentation. Start with prompt #1 and work up to #8 — by the end you'll have a complete Python service with CI and Docker. If you want more advanced prompts, follow this blog for future parts.
Further Reading
- Python: https://docs.python.org/3/
- FastAPI: https://fastapi.tiangolo.com/
- SQLAlchemy: https://docs.sqlalchemy.org/
- pytest: https://docs.pytest.org/
- Docker: https://docs.docker.com/develop/dev-best-practices/
- GitHub Actions: https://docs.github.com/actions
Comments