# Python Tooling — Derived Application > Applies Atelier's domain principles to Python tooling specifically. > Derives from `domains/` docs; introduces no new P-rules (D-063). > See `languages/python.md` for the language first-principles stub. ## ruff for Lint and Format (DevOps P2 Automation, C2 Clarity) - **`ruff` replaces flake8 + black + isort + pyupgrade:** one tool, one config, one order of magnitude faster. Format is not debated in review (Clarity C2). - **Rule selection is principled, not "everything":** `select = ["E", "F", "I", "UP", "B", "SIM"]` — each rule group has a one-line `# reason:` in `pyproject.toml`. Rules without a rationale are noise (Documentation P1 — docs are code). - **`ruff format` is the formatter, `ruff check` is the linter:** run both in CI; the formatter is deterministic, the linter surfaces smells. - **Applies `devops/P2`:** the format/lint gate runs on every push; a developer never waits for a reviewer to comment on style. ```toml # pyproject.toml [tool.ruff] target-version = "py311" line-length = 100 [tool.ruff.lint] select = ["E", "F", "I", "UP", "B", "SIM", "RUF"] # reason: E/F = pyflakes+pycodestyle; I = isort; UP = pyupgrade; B = bugbear; SIM = simplification [tool.ruff.format] quote-style = "double" ``` ## mypy and Type-Check Gate (DevOps P2 Automation, Data P7 Type Fidelity) - **`mypy --strict` in CI, not in the editor:** strict flags (`disallow_untyped_defs`, `no_implicit_optional`, `warn_return_any`) are the floor. The editor runs a relaxed mypy for speed; CI runs strict as the gate. - **`pyright` for stricter/async-aware checking:** pyright understands `async` better and reports faster; mypy is the standard. Pick one as the gate, run the other as informational. - **Per-module overrides only with a tracked reason:** `[[tool.mypy.overrides]] module = "legacy.*" ignore_errors = true` — each override block links to a ticket. Untracked overrides accumulate into a permanently untyped core. - **`py.typed` marker for libraries:** ships the type info to consumers. Without it, downstream mypy treats the library as `Any`. ```bash # CI gate mypy --strict src/ pyright src/ || true # informational ``` ## Dependency Management: poetry and uv (DevOps P1 Reproducibility) - **`poetry` or `uv` for lockfile discipline:** both produce a deterministic lock (`poetry.lock` / `uv.lock`). `pip install` alone does not — it resolves at install time, producing different trees across machines. - **`uv` for speed (Rust-based, 10–100x faster):** newer tool, same lockfile semantics. Either is acceptable; do not mix within a repo. - **Lockfile committed for applications:** for libraries, commit the lock for CI reproducibility even though consumers resolve their own tree. - **`--frozen` install in CI:** `poetry install --no-dev --frozen` fails if the lock is out of sync. Prevents a "works on my machine" drift. ```bash # CI install — deterministic uv sync --frozen --no-dev # or poetry install --no-dev --frozen ``` ## Virtualenv Discipline (DevOps P1 Reproducibility, C3 Simplicity) - **One virtualenv per project, never the system Python:** `uv venv` or `python -m venv .venv`. System Python drift breaks reproducibility. - **`uv` creates and pins the Python version:** `uv venv --python 3.12` ensures the same interpreter across machines. A pinned Python is part of the reproducibility contract, not just the lockfile. - **No `pip install` into the system Python in CI:** use `uv`/`poetry`'s venv. A CI step that mutates system Python makes the next job non-hermetic. ```bash uv venv --python 3.12 source .venv/bin/activate uv pip install -r requirements.txt ``` ## Documentation in the Pipeline (Documentation P1 Documentation is Code, DevOps P9 Documentation in the Pipeline) - **`mkdocs` + `mkdocstrings` from docstrings:** API docs are generated from `google`- or `numpy`-style docstrings; the build fails on missing docstrings for public symbols (Documentation P1). - **`doctest` blocks in docstrings are run by pytest:** a `>>>` example is a tested artifact; a stale example fails the build (Documentation P1, Testing P1). - **`pyproject.toml` is the single source of tool config:** ruff, mypy, pytest, poetry all read from it. Do not scatter `.flake8`, `setup.cfg`, `mypy.ini`. One config file is one place to look (Clarity C2). ```python def get_user(id: UUID) -> User: """Fetch a user by id. Args: id: the user's UUID. Returns: The User. Raises: NotFoundError: if the user does not exist. Example: >>> get_user(UUID('intentional-example-uuid')) User(...) """ ... ``` ## Cross-References - `domains/devops/ci-cd.md` — the pipeline gates that host ruff/mypy/poetry. - `domains/devops/first-principles.md` — DevOps P1 Reproducibility, P2 Automation. - `domains/documentation/first-principles.md` — Documentation P1 Documentation is Code. - `languages/py-types.md` — the type rules mypy enforces reference this doc. - `languages/py-testing.md` — the pytest config (`pyproject.toml [tool.pytest]`) detailed here.