Skip to content

Ruff config enforces almost nothing, inviting out-of-scope lint sweeps #161

Closed

Description

@ColtonPayne

Summary

The Ruff setup enforces almost nothing, and the gap is wide enough that contributors reasonably assume the opposite. This produces large "lint cleanup" diffs that CI neither requires nor rejects.

Current state

.github/workflows/python-package-version-test.yml runs:

- name: Lint with ruff
  run: |
    python -m ruff check pyreason/scripts

and pyproject.toml configures only:

[tool.ruff.lint]
# Ignore ambiguous variable name errors (E741) in interpretation files
[tool.ruff.lint.per-file-ignores]
"pyreason/scripts/interpretation/*.py" = ["E741"]

There is no select, so Ruff falls back to its default rule set: E4, E7, E9, F — a narrow slice of pycodestyle plus pyflakes. Everything else is off, including the rules most people expect a Ruff gate to cover:

  • I001 import sorting
  • UP pyupgrade
  • SIM flake8-simplify
  • RUF012 mutable class attributes needing ClassVar
  • B / BLE flake8-bugbear and blind-except

Two smaller scope gaps in the same command:

  • Only pyreason/scripts is linted. pyreason/__init__.py and pyreason/pyreason.py are not, nor are tests/ or examples/.
  • Note the empty [tool.ruff.lint] table above [tool.ruff.lint.per-file-ignores] — it currently holds nothing but the comment.

Why it matters

A contributor who runs ruff check --fix locally with a stock editor config gets a very different result from CI, and produces a sweeping diff believing it was required to get the build green.

This happened in #160: an import-sorting, SIM, UP, RUF012 and BLE001 sweep touched ~45 files alongside a ~12-line performance change. None of it was needed — CI was already passing — and it was reverted so the actual optimization could be reviewed. Without a config change the same thing will recur, and any convention applied by hand will drift straight back since nothing holds it in place.

Suggested resolution

Decide explicitly, in either direction — both are defensible, but the current state is neither:

Option A — enforce a real rule set. Add select to [tool.ruff.lint], land the one-time sweep as its own standalone PR (never mixed with behavioral changes), and consider adding ruff format --check. Worth starting conservative, e.g. ["E4", "E7", "E9", "F", "I", "UP"], and growing it, since SIM and B are noisier on numba-heavy code.

Option B — document the narrow gate. Keep the default rules and add a CONTRIBUTING.md note saying only E4/E7/E9/F are enforced and broader autofixes should not be submitted. Cheaper, and honest about what the gate does.

Either way, worth deciding whether the lint command should cover pyreason/ rather than just pyreason/scripts.

One constraint on Option A: interpretation.py and interpretation_parallel.py are asserted byte-identical except the parallel= decorator by tests/unit/dont_disable_jit/test_parallel_consistency.py. Any autofix must land on both files identically or that test fails.

Activity

  1. ColtonPayne commented on Aug 14, 2026

    @ColtonPayne
    CollaboratorAuthor

    Closing — resolved by pinning ruff, and correcting the diagnosis above.

    The issue body is wrong on its central claim. It states that with no select, Ruff falls back to E4/E7/E9/F. That was true until Ruff 0.16.0 (2026-07-23), which expanded the default rule set from 59 rules to 413 — the first change to that default since v0.1.0. This repo sets no select and the workflow installed ruff unpinned, so it inherited all 413 and reports ~289 findings.

    So the premise was inverted: the lint sweep in #160 was not unnecessary churn, it was a correct and required response to a breaking upstream change. #160's run history shows exactly that — Lint with ruff failing on 2026-07-27 and 07-28, four days after 0.16.0 shipped, then green once the sweep landed.

    This was never scoped to one PR. main has not run CI since 2026-05-13 and fails identically, so every PR opened after 0.16.0 was broken on arrival regardless of contents.

    Resolution: pin to ruff==0.15.22, the last release with the old default, so lint results depend on the code rather than on when the job ran. One side effect worth recording: 0.16 also dropped 18 rules from the defaults including E741, so the existing per-file-ignores entry is dead config under 0.16 but live again under this pin — keep it.

    Adopting the broader 413-rule set is still worth doing deliberately: add an explicit select, land the cleanup as its own reviewed PR, and unpin. Worth a fresh issue if someone wants to take it, rather than reopening this one under a false premise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions