# Ruff configuration — pinned deliberately. # # Before this file existed the repo had no Ruff config, so `ruff check .` ran whatever the # installed Ruff version enables by default (Ruff 0.16 turns on a very broad set: UP/RUF/PLR/ # S/TRY/PERF/FURB/YTT/ASYNC...). The same tree therefore produced different findings on # different machines and between a developer and a runner. Never rely on tool defaults. # # Policy, decided from the measured findings rather than from taste: # # * `select` is explicit. High-signal correctness rules first, then only the mechanical # modernisation rules we are willing to apply without human review. # * BLE001 is selected so the existing `# noqa: BLE001 - ` suppressions stay *used* # and RUF100 can never delete their rationale — a broad `except Exception` is a legitimate # choice in hardware, GL and teardown paths. It is deliberately NOT part of the gate. # * S110 / S112 / E722 ARE part of the gate: a silent handler hides a failure. Silence is # acceptable only with a reasoned suppression, or with a handler that reports/re-raises. # * RUF100 reports unused suppressions but is `unfixable`: a machine must not remove a # suppression whose comment records why it exists. Triage those by hand. # * The mechanical sweep never uses `--unsafe-fixes`. # # The gate (pre-merge / runner) selects the narrow set explicitly, so BLE001 cannot fail a # build, and only ever touches changed files: # # tools/lint_gate.py # the gate: changed files, machine-readable # ruff format --check # never a whole-tree rewrite mid-diff target-version = "py310" line-length = 88 [lint] select = [ "E9", "E722", # syntax/IO errors, and bare `except:` "E402", # module import not at top of file — SELECTED for visibility, NOT gated. # `import` after `os.environ[...]` is deliberate in this codebase (the # environment must be set before Qt/cv2 load), so gating it would be noise; # but it is the only rule that would catch an accidental import moved above # that setup, so it stays reported. An intentional site belongs in a # `# noqa: E402 - ` when it is touched. "F", # pyflakes: F401 unused import, F811 redefinition, F821/F822 undefined name "S110", "S112", # try-except-pass / try-except-continue — an invisible failure "B023", # function definition does not bind loop variable "PLW1510", # subprocess.run without an explicit check "RUF100", # unused noqa — reported for triage, never auto-removed "UP", # pyupgrade (Optional[X] -> X | None, deprecated imports) "I", # import sorting "RUF022", "RUF023", # __all__ / __slots__ ordering "PIE", # flake8-pie "RET501", "PLR1711", # useless return / return None "SIM114", # if-with-same-arms "BLE001", # selected for suppression hygiene only — excluded from the gate ] unfixable = ["RUF100"] [format] quote-style = "double" indent-style = "space"