0.93.2 shipped a week's worth of confusion about which Node this repo
runs on; the Python side had the identical shape and nobody had looked.
Prod (Dockerfile), the test image (Dockerfile.test) and CI (check.yml)
each spelled 3.12 as an independent literal, there was no
`.python-version`, no `requires-python`, and ruff had no target-version
so it judged our syntax against some other interpreter. The dev machine
had no 3.12 installed at all — only 3.13 and 3.14 — so every ad-hoc host
script ran two majors ahead of production.
`.python-version` is now the single source of truth, and the contract is
held by tests rather than by prose. It is split in two because no single
runner can see every link: the pytest suite runs inside the test image,
which mounts backend/ alone and cannot read the repo-root Dockerfile or
the workflow.
test_python_engine_contract.py : running interpreter == pyrightconfig
== ruff.toml
pythonEngine.contract.test.ts : .python-version == pyrightconfig ==
ruff.toml == Dockerfile ==
Dockerfile.test == check.yml
Together they close the loop, and the backend half carries the assertion
that pays — that the interpreter running the suite is the pinned one. A
green suite on another minor does not mean the shipped image is green;
it means they have drifted and nobody has noticed yet.
34 lines
2.4 KiB
TOML
34 lines
2.4 KiB
TOML
# Ruff config for the Siftlode backend. Keeps ruff's default rule set (E4/E7/E9 + F = the
|
|
# high-signal pyflakes/pycodestyle checks that catch real dead code + bugs), but ignores E402:
|
|
# a handful of modules (e.g. main.py) deliberately run setup — logging config, sys.path — before
|
|
# importing route modules, so "module import not at top of file" is intentional there, not cruft.
|
|
#
|
|
# line-length 100 (ruff's default is 88): matches the frontend prettier printWidth so both halves of
|
|
# the repo wrap at the same width, and it's the width `ruff format` enforces (gated via
|
|
# `ruff format --check` alongside `ruff check`).
|
|
line-length = 100
|
|
# The interpreter this code actually runs on, so ruff judges syntax and upgrade hints against the
|
|
# right one. Left unset it falls back to a default that has nothing to do with us. `/.python-version`
|
|
# is the single source of truth; tests/test_python_engine_contract.py holds this to it.
|
|
target-version = "py312"
|
|
|
|
[lint]
|
|
# Beyond the default set, the rules that hold a LOG LINE to what E22.5 makes of it:
|
|
# LOG — stdlib logging misuse (`.warn()`, a logger built by hand, `exc_info` on `.exception`)
|
|
# G — the call's shape: %-style args, never an f-string the level filter cannot skip, and
|
|
# never `str(exc)` where `exc_info` would carry the traceback
|
|
# T20 — `print` instead of a log line. Two deliberate ones remain, each with its `# noqa`
|
|
# and a why: `downloads/runner.py`'s usage line is for whoever typed it, and
|
|
# `logsink.py` reports the SINK's own failure, which cannot be logged into the sink.
|
|
# TRY400 — `.error()` in an `except` where `.exception()` would keep the traceback
|
|
# TRY401 — the exception spelled into the message AND passed as `exc_info` (it is in both)
|
|
# S112 — `except: continue`, the silent swallow inside a loop
|
|
# S110 — `except: pass`, the same thing outside one
|
|
# BLE001 — a blind `except Exception`. Every one that remains carries `# noqa: BLE001 — <why>`,
|
|
# which is the point: catching everything is sometimes right (a worker thread with
|
|
# nobody to raise to, a best-effort backfill inside a sync) and always worth a sentence.
|
|
# The wider rule these two enforce lives in tests/test_swallowed_exceptions.py: a handler that
|
|
# neither logs nor re-raises says `# silent-ok: <why>`. Ruff sees only the two narrow shapes.
|
|
extend-select = ["LOG", "G", "T20", "TRY400", "TRY401", "S110", "S112", "BLE001"]
|
|
ignore = ["E402"]
|