chore(py): pin ruff rules with select, adopt 0.16 - #1231
Merged
Conversation
The bindings config selected rules with `extend-select`, which extends ruff's defaults rather than replacing them, so the effective rule set moved with upstream. ruff 0.16.0 grew its defaults from 59 rules to 413: measured on this tree, same config, the enabled count went from 265 (0.15.22) to 501 (0.16.1), pulling in 28 families the config never asked for. Among them PLC/PLE/PLR/PLW, which silently overrode the deliberate omission of Pylint's design rules that the comment above the setting states. The same release dropped 18 opinionated E/F rules from the defaults, which a defaults-relative config would have lost just as quietly. State the set absolutely with `select` and raise the ceiling to <0.17. Under `select` the count is 265 on 0.15.22 — unchanged, so nothing was lost by dropping the implicit defaults — and 268 on 0.16.2, the three additions being new RUF rules in an already-selected family. `ruff check` passes on both with no noqa, no suppressions and no source changes; the three diagnostics 0.16 reported (DTZ001, BLE001, PYI044) were all in families never selected. `uv lock` keeps a package at its locked version while that version still satisfies the requirement, so widening the ceiling alone left ruff at 0.15.22. Adopting 0.16.2 took an explicit `uv lock --upgrade-package ruff`; CONTRIBUTING.md now documents that step. The floor moves 0.6 -> 0.13 because it was already fiction. UP038 left ruff's stable set in 0.13, and 0.6.0, 0.9.0, 0.12.0 and 0.12.12 each flag the same five isinstance(x, (A, B)) sites here — identically under the old extend-select config, so this changes nothing beyond saying what was always true. The ruff-pre-commit `rev:` moves to v0.16.2 to match what uv.lock resolves. It had drifted to v0.15.14 against a locked 0.15.22 — inside the old bound, so nothing failed, but the two ran different ruffs. Fixes #1222
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adopts ruff 0.16 by stating the bindings' rule set absolutely instead of
relative to ruff's defaults.
Why
big-code-analysis-py/pyproject.tomlselected rules withextend-select, which extends ruff's defaults rather than replacingthem — so the effective set moved whenever upstream moved. ruff 0.16.0
grew its default rule set from 59 rules to 413, and 28 families arrived
that this config never asked for. Among them
PLC/PLE/PLR/PLW, silently overriding the deliberate Pylint omission that thecomment immediately above the setting states:
The same release dropped 18 opinionated
E/Frules from thedefaults, which a defaults-relative config would have lost just as
quietly. The three diagnostics 0.16 reported were the symptom; the
moving baseline was the bug.
Measurements
Counted from
ruff check --show-settings,linter.rules.enabled:extend-select(before)select(after)The 265→265 cell is the load-bearing one: dropping the implicit
defaults costs this config nothing, because
select = ["E", "F", …]already names those families whole. The +3 on 0.16.x are
RUF036,RUF063andRUF068— new rules inside an already-selected family,which is how the set is supposed to grow. Set difference confirms
nothing was lost.
ruff checkpasses on 0.15.22 and 0.16.2 with nonoqa, nosuppressions and no source changes. Reverting
selectback toextend-selectunder 0.16.2 reproduces exactly the three reporteddiagnostics (
DTZ001,BLE001,PYI044), all in families neverselected; with the fix, zero.
Beyond the issue's task list
make py-relockalone would not have adopted 0.16.uv lockkeeps a package at its locked version while that version still
satisfies the requirement, so raising the ceiling re-resolved to a
still-locked
ruff==0.15.22. Adopting 0.16.2 needed an explicituv lock --upgrade-package ruff. Documented in CONTRIBUTING.md so thenext bound widening does not ship the same no-op.
.pre-commit-config.yamlhad already drifted —rev: v0.15.14against a locked 0.15.22, with the comment quoting the old
<0.16bound. Both sat inside that bound so nothing failed, but
pre-commit run --all-filesran a different ruff than CI. Nowv0.16.2, with the comment pointing atrequirements/dev.txtas theauthority.
>=0.6floor was fiction.UP038(non-pep604-isinstance)left ruff's stable set in 0.13, and this tree has five
isinstance(x, (A, B))sites. Measured: 0.6.0, 0.9.0, 0.12.0 and0.12.12 each report the same five; 0.13.0 / 0.14.0 / 0.14.5 / 0.15.0
report none — identically under the pre-fix config, so this is
pre-existing rather than anything
selectintroduced. Raised to>=0.13with the measurement inline, following the precedent themypy>=2.3entry three lines above already set in the same file.Not addressed here
ruff 0.16 formats Python code blocks inside Markdown, so
make py-fmtnow includes
big-code-analysis-py/README.mdwhere 0.15 skipped it(32 files → 33). Already clean, so nothing churns, but it is
version-dependent behaviour that
selectdoes not govern — it is theformatter, not the linter.
The remaining version-drift channels are filed as #1230: the
honour-system
rev:lockstep, andmake py-lint/py-fmtresolvingrufffromPATH— wheremise.toml("pipx:ruff" = "latest"),Dockerfile:284(uv tool install ruff) and a barepipx install ruffall install unpinned — while CI uses the locked venv copy. These are
robustness rather than breakage now: with
select, a version differencecan no longer silently enable 28 rule families.
Validation
make pre-commit→BCA_GATE: pass (gate=pre-commit), with zeroskipped stages (mypy, pyright, 363 pytest tests + 1 xfail, stubtest,
5166 cargo tests). CI's exact ruff steps —
ruff format --check .andruff check .frombig-code-analysis-py— pass under the locked0.16.2, as do
make py-lint/py-fmt-checkover both the bindingstree and the separate
scripts/smokeinvocation with--config.No coverage impact: the diff is TOML, YAML and Markdown only, with no
.rs,.pyor.pyifile touched, so it contains no coverable lines.Fixes #1222