PSD401 template for containerized Python web apps. FastAPI + uv + Docker, src layout, Python 3.12+.
- FastAPI app factory (
src/app/main.py):create_app()with a/healthzliveness probe and one real example route (POST /api/reading-time) using typed pydantic request/response models withFieldvalidation at the edge. - Environment-driven config (
src/app/config.py): pydantic-settings withAPP_prefix,.envsupport, and a committed.env.example. All environment reads live in one module. - Tests that mean something: pytest + httpx TestClient (via
httpx2— starlette 1.6+ deprecated the httpx 0.x line) — healthz, happy path with exact values, two 422 validation paths, and config loading (defaults, env override, invalid value, factory wiring). PSD CI fails zero-test repos by design. - ruff (lint + format, line length 100,
E W F I UP B SIM RUF) and mypy strict (pydantic plugin) oversrc+tests, both configured inpyproject.toml. - Docker: multi-stage Dockerfile (uv builder →
python:3.12-slimruntime, non-root user, only the venv copied),docker-compose.ymlfor live-reload local dev,.dockerignore. - PSD CI wiring (org reusable workflows), Dependabot (github-actions + uv, weekly, minor/patch grouped), MIT LICENSE, CLAUDE.md.
Everything runs through uv — never bare python or pip:
uv run pytest # not: python -m pytest
uv run some_script.py # not: python some_script.py- Rename:
nameinpyproject.toml, thesrc/app/package directory, the[tool.hatch.build.targets.wheel]packages entry, imports intests/, and theapp.main:appreferences inDockerfileanddocker-compose.yml. Naming: lowercase-kebab repo, snake_case package,psd-prefix for district-specific apps. - Set repo custom properties:
tier(defaultc-experiment),owner,lifecycle: active; add topics (python,fastapi, …). - Configure:
cp .env.example .env, adjust values; add new settings as typed fields onSettingsplus a line in.env.example. - Review CLAUDE.md and prune it to your app.
- Verify green:
uv sync && uv run pytest && uv run ruff check && uv run ruff format --check && uv run mypy. - Verify the container:
docker build -t my-app .thendocker compose up --buildand hit http://localhost:8000/healthz. - Replace the reading-time route and its tests with your real endpoints — never leave the repo with zero tests.
| Task | Command |
|---|---|
| Install/sync | uv sync |
| Test | uv run pytest |
| Lint | uv run ruff check |
| Format | uv run ruff format |
| Format check (CI) | uv run ruff format --check |
| Typecheck | uv run mypy |
| Dev server | uv run uvicorn app.main:app --reload |
| Dev in Docker | docker compose up --build |
| Production image | docker build -t psd-webapp . |
Technology Services, Peninsula School District.