Linters & formatters

Ruff for Python: Linter and Formatter, Rule Selection and Configuration

Linters & formatters · · Published 2026-10-27

A man and a woman working at the control console and tape units of an IBM 704 computer in a 1950s machine room
Operators at an IBM 704 electronic data processing machine. NASA, public domain.

Ruff is a Python linter and code formatter written in Rust and created by the company Astral as an open-source project under the MIT licence. It reimplements the checks of many existing tools, including pycodestyle, pyflakes, isort and dozens of flake8 plugins, and keeps their rule codes, so most of what you know about those tools carries over.

One tool, many rule sets

Before ruff, a typical Python project combined several tools: pycodestyle for layout, pyflakes for likely errors (both usually through flake8), isort for imports, a handful of flake8 plugins, and Black for formatting. Ruff provides equivalents of most of them in one executable, configured in one place. Its main selling point in its documentation is speed, as it is often fast enough to run on every keystroke in an editor.

Each rule has a code made of a prefix and a number, and the prefix tells you where the rule came from. ruff rule E711 prints its documentation, starting with its origin:

# none-comparison (E711)

Derived from the **pycodestyle** linter.

Fix is always available.

## What it does
Checks for comparisons to `None` which are not using the `is` operator.

Rule documentation also explains why the pattern is a problem, usually quoting PEP 8 or the original tool, and gives an example.

Choosing rules

Ruff enables only a subset of its rules by default, and that default set has changed between releases. The reliable approach is to state your selection in configuration:

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "UP", "B"]
ignore = ["E501"]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["N802"]

A few options do most of the work:

To see the full resolved configuration for a file, including which rules are active, run ruff check --show-settings path/to/file.py. Settings can live in pyproject.toml under [tool.ruff], or in a ruff.toml or .ruff.toml file without the tool.ruff prefix.

Preview rules and layout checks

Some rules are marked as preview: new or unstable, and only active when preview mode is enabled with --preview or preview = true. At the time of writing (ruff 0.16), this includes many of pycodestyle's whitespace and blank-line checks, such as E225 and E302. On a file containing def right after an import line and item.price!=None, ruff check --select E,W reported only E401 and E711; with --preview it also reported E302 and E225. The reasoning is that a project using ruff format or Black never sees those problems. If you rely on ruff alone for layout warnings without a formatter, check which rules your version actually runs.

Fixes: safe and unsafe

Many rules come with an automatic fix, marked [*] in the output. Ruff classifies fixes as safe or unsafe. A safe fix preserves the meaning of the code; an unsafe one might change behaviour in some cases, and is applied only with --unsafe-fixes. The E711 fix is an example: replacing x != None with x is not None is almost always right, but would change the result for a class with an unusual __eq__.

The pyupgrade rules show how fixes help with type hints. With target-version = "py310", a function annotated def f(x: Optional[int]) -> List[int]: produced:

up.py:1:1: UP035 `typing.List` is deprecated, use `list` instead
up.py:4:10: UP045 [*] Use `X | None` for type annotations
up.py:4:28: UP006 [*] Use `list` instead of `List` for type annotation

Running ruff check --fix rewrote the annotations to int | None and list[int], the modern forms introduced by PEP 604 and PEP 585.

The formatter

ruff format formats code in a style designed to match Black's, and its documentation lists the known differences. It reads line-length from the shared configuration and has its own [tool.ruff.format] table, for example quote-style = "single" for projects that prefer single quotes, something Black does not offer. Like Black, it supports --check and --diff for CI, and respects the magic trailing comma. The formatter does not sort imports; that is the linter's I rules, run with ruff check --select I --fix. See our guide to Black for the style itself.

Silencing and migrating

When migrating from flake8, list the plugins you use and find their prefixes in ruff's rules reference; not every plugin has an equivalent, and some rules behave slightly differently. Our comparison of flake8 and pylint describes the tools ruff is most often compared with.

Where it fits

Ruff does not replace a type checker, and it does not implement everything pylint checks. A common modern setup is ruff for linting, import sorting and formatting, plus mypy or another type checker, all run from pre-commit and CI; our guide to checking style locally walks through that setup. For a single snippet, the checker on our home page runs a subset of the same pycodestyle checks in the browser.

Sources