The documentation has stale configuration guidance and a few examples
that do not work as written. Clarify the prose and correct editor
configuration precedence, user-level configuration discovery, rule
descriptions, and the tutorial and configuration examples.
---------
Co-authored-by: Zanie Blue <zanie@openai.com>
Summary
--
This PR updates our default Python version to 3.11, in line with 3.10
reaching [EOL] next month, and
updates our latest Python version to 3.15. I followed last year's PR
(#20725) to find everywhere
that needed to change. Fortunately there were not as many test changes
needed as last year's
deferred annotation issues.
I also updated the language classifier in our pyproject.toml.
[EOL]: https://devguide.python.org/versions/#supported-versions
Test Plan
--
Existing tests with updated snapshots. Most of the updates were to
snapshots for TC001, TC002, and
TC003, which now default to the lazy-import fix.
After updating the snapshots, I tried to make sure we still had coverage
for whatever changed. That
meant adding a few more mdtests for the TC rules, and one small mdtest
for RUF017 to restore
coverage for the `functools.reduce` fix.
## Summary
Removes the "Who's using ruff?" list.
The section was useful when Ruff was young and not well known. Ruff is
now widely used in the ecosystem, so that this list provides diminishing
returns and we also lack a clear guidance on what it takes for a project
to be added.
This PR stabilizes the expanded default rule set currently in preview
and adds a new generated `Default Rules` page to the docs to avoid
needing to include a lengthy selector block in multiple places.
Just to be sure, I ran the example from #23587 again on this branch:
```console
~/astral/ruff on brent/0.15.4 [$] is 📦 v0.15.4 via 🐍 v3.14.2 via 🦀 v1.93.0
❯ echo 'x = id' | uvx ruff@latest --isolated check - --preview --select ANN003,PLR1712
error: Ruff crashed. If you could open an issue at:
https://github.com/astral-sh/ruff/issues/new?title=%5BPanic%5D
...quoting the executed command, along with the relevant file contents and `pyproject.toml` settings, we'd be very appreciative!
thread 'main' (1681253) panicked at crates/ruff_python_semantic/src/definition.rs:285:26:
index out of bounds: the len is 0 but the index is 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
~/astral/ruff on brent/0.15.4 [$] is 📦 v0.15.4 via 🐍 v3.14.2 via 🦀 v1.93.0
❯ echo 'x = id' | just run --isolated check - --preview --select ANN003,PLR1712
cargo run -p ruff -- --isolated check - --preview --select ANN003,PLR1712
Compiling ruff_python_semantic v0.0.0 (/home/brent/astral/ruff/crates/ruff_python_semantic)
Compiling ruff v0.15.4 (/home/brent/astral/ruff/crates/ruff)
Compiling ruff_linter v0.15.4 (/home/brent/astral/ruff/crates/ruff_linter)
Compiling ruff_graph v0.1.0 (/home/brent/astral/ruff/crates/ruff_graph)
Compiling ruff_workspace v0.0.0 (/home/brent/astral/ruff/crates/ruff_workspace)
Compiling ruff_markdown v0.0.0 (/home/brent/astral/ruff/crates/ruff_markdown)
Compiling ruff_server v0.2.2 (/home/brent/astral/ruff/crates/ruff_server)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 23.14s
Running `target/debug/ruff --isolated check - --preview --select ANN003,PLR1712`
warning: Detected debug build without --no-cache.
All checks passed!
```
## Summary
This PR adds the new default rule set in preview. This ended up being
pretty non-invasive because the `DEFAULT_SELECTORS` are only used in one
place where `preview` isn't set to the default value of `false`.
I've currently listed each rule with a separate `RuleSelector`, which I
generated with the script below. I thought about trying to be more
clever by finding the smallest set of prefix selectors that yield the
same rule set, but I figured this would be the easiest way to add and
remove rules in the future anyway.
<details><summary>Script</summary>
<p>
```py
import json
import subprocess
import tomllib
from pathlib import Path
from string import ascii_uppercase
RULES = {
rule["code"]: rule["linter"]
for rule in json.loads(
subprocess.run(
["ruff", "rule", "--all", "--output-format=json"],
check=True,
text=True,
capture_output=True,
).stdout
)
}
for code, linter in RULES.items():
if linter == "Ruff-specific rules":
RULES[code] = "Ruff"
def kebab_to_pascal(s: str) -> str:
return "".join(part.title() for part in s.split("-"))
rules = tomllib.loads(Path("proposal.toml").read_text())["lint"]["select"]
for rule in rules:
linter = kebab_to_pascal(RULES[rule])
suffix = rule.lstrip(ascii_uppercase)
prefix = "_"
match linter:
case "Flake8Comprehensions":
suffix = suffix.removeprefix("4")
case "Pycodestyle":
prefix = ""
suffix = rule
case "Flake8Gettext":
linter = "Flake8GetText"
case "Pep8Naming":
linter = "PEP8Naming"
case "Pylint":
prefix = ""
suffix = rule.removeprefix("PL")
case "Flake8Debugger":
suffix = suffix.removeprefix("10")
print(
" " * 4,
f"RuleSelector::rule(RuleCodePrefix::{linter}("
f"codes::{linter}::{prefix}{suffix})),"
f" // {rule}",
sep="",
)
```
</p>
</details>
## Test Plan
A new CLI test showing the preview default rules. I filtered down the
snapshot to just the `linter.rules.enabled` section, which isn't
strictly necessary but was a lot shorter. I also tested manually in VS
Code to make sure I didn't miss wiring up the preview defaults in the
server:
<img width="681" height="340" alt="image"
src="https://github.com/user-attachments/assets/9f7a0cd4-968e-40d6-abf0-dfbfa496531d"
/>
I also tested in the playground.