Cursor Rules — Modern Python (uv, ruff, pydantic)
Project rules for modern Python: uv-managed, ruff-clean, fully typed, pydantic at the boundaries, pytest conventions — and the anti-patterns spelled out as hard bans.
Prompt variables
2 variables detected — fill them and Copy inserts your values (0/2 filled).
# Project rules — Python
## Toolchain (non-negotiable)
- Python {{python_version}}+. Dependencies via `uv` only: `uv add <pkg>`, never pip, never edits to the lockfile by hand. Run everything through `uv run`.
- `ruff check --fix .` and `ruff format .` must pass with zero violations before any task is complete. Do not disable rules inline without a `# noqa: <rule> — <reason>` comment; bare `# noqa` is banned.
- Full type hints on every function signature, including tests and private helpers. `mypy` (strict) must pass. `Any` requires a comment justifying it; prefer `object` + narrowing or a `Protocol`.
## Style & idioms
- `pathlib.Path` everywhere — `os.path` is banned in new code. f-strings for formatting. Comprehensions over `map`/`filter` when they stay one level deep.
- No mutable default arguments, ever: default to `None`, create inside. Ruff flags it; fix it properly, not with a lint suppression.
- Data shapes: `@dataclass(frozen=True, slots=True)` for internal values; pydantic `BaseModel` at system boundaries (API payloads, config, file formats) where validation earns its cost. Do not use pydantic for hot-path internal objects.
- Enums over string literals for closed sets; `Literal` types acceptable at narrow boundaries.
- Module layout: `src/{{package}}/` layout, absolute imports only. No `from x import *`, no circular-import "fixes" via local imports without a comment explaining the cycle.
## Errors & logging
- Never `except Exception: pass`. Catch the narrowest exception that the code in `try` can actually raise; let programming errors crash.
- Raise domain exceptions (defined in `exceptions.py`) with context: `raise OrderNotFoundError(order_id=order_id) from err` — always chain with `from` when translating.
- Logging via module-level `logger = logging.getLogger(__name__)`; structured key-value messages; no `print()` outside `__main__` blocks and CLIs.
## Testing (pytest)
- Plain `assert` with pytest, no unittest classes for new tests. Parametrize instead of copy-pasting near-identical tests.
- Fixtures for setup; no test may depend on another test's side effects or run order.
- Mock only at process boundaries (network, clock, filesystem) — mocking our own modules to make a test pass means the design or the test is wrong.
- Every bug fix ships with a test that fails on the pre-fix code.
## When editing
- Match existing patterns in the module you are touching, even where these rules are silent.
- New dependencies require asking first — include what stdlib/existing-dep alternative you rejected and why.
Comments (0)