Ruff v0.8.0 is available now! Install it from PyPI, or your package manager of choice:
uv pip install --upgrade ruff
As a reminder: Ruff is an extremely fast Python linter and formatter, written in Rust. Ruff can be used to replace Black, Flake8 (plus dozens of plugins), isort, pydocstyle, pyupgrade, and more, all while executing tens or hundreds of times faster than any individual tool.
Ruff v0.8 is a significant release containing many features and improvements. Highlights include:
- Ruff's linter and formatter both now assume Python 3.9 by default. (This is configurable; see below for more details.)
- 13 rules have been stabilized that were previously only available in preview mode. Several behaviors have also been stabilized for pre-existing rules.
- All the usual bugfixes, behavior improvements and documentation updates that you'd expect from any Ruff release.
Migrating to v0.8 #
While upgrading to Ruff v0.8 should be a smooth experience for most users, this release contains a number of breaking changes that may impact the way your code is linted or formatted. We'll tackle these one by one.
Target Python version now defaults to 3.9 #
This release makes some changes to the "target Python version" that Ruff infers by default if it has no other information to go by.
Ruff needs to figure out the minimum version of Python your code supports for several reasons:
-
Many rules in the Ruff linter vary their behavior depending on the inferred target version. For example, the linter has import-sorting rules that categorize your imports depending on whether a module comes from a third-party dependency or the standard library — but the answer to this question depends on the Python version you're using.
zoneinfo
, for example, was only added to the standard library in Python 3.9, soimport zoneinfo
must be categorized as a standard-library import if you assume Python 3.9. However, by process of deduction, it must be considered a third-party import on lower versions of Python, since there is no standard-libraryzoneinfo
module on those Python versions. -
The formatter also sometimes varies its behavior according to the inferred target version. For example, the formatter prefers to parenthesize
with
statements where possible, but this is only valid syntax on Python >=3.9. If Ruff understands that it should assume an older Python version is being used, a slightly different formatting style is applied.
Ruff uses a three-stage process to determine which Python version it should assume:
- If you specify a value for the
target-version
setting in your Ruff configuration, Ruff will assume that Python version when linting and formatting your code. - If
target-version
is not specified but you have apyproject.toml
file in your repository, Ruff will next look to see ifproject.requires-python
is specified there, and, if so, will use that. - If Ruff still can't figure out which version to assume after stages (1) and (2), it will fall back to a default target version.
On previous versions of Ruff, the default target version used in stage (3) of the algorithm above was Python 3.8. Ruff v0.8 bumps this default to 3.9, as Python 3.8 is now End Of Life.
If you haven't specified a value for
target-version
or
requires-python
,
you might see formatting changes that look like this when using the Ruff formatter:
- with open(LONG_FILE_PATH_1) as file_1, open(
- os.path.join("foo", "bar", "baz", "spam", "eggs", "ham", "bacon)
- ) as file_2:
+ with (
+ open(LONG_FILE_PATH_1) as file_1,
+ os.path.join("foo", "bar", "baz", "spam", "eggs", "ham", "bacon) as file_2,
+ ):
do_something()
You might see changes like this when using Ruff's
isort
rules to fix your import
sorting:
import collections
import difflib
+ import zoneinfo
import requests
import sqlalchemy
- import zoneinfo
And Ruff's UP006
rule
might start complaining about code that makes use of deprecated typing
-module
aliases such as typing.List
or typing.Dict
.
Many more of Ruff's rules take account of the inferred target version in some
way; this is not an exhaustive list of rules that might be affected by this
change. To revert to Ruff's previous behavior in all cases, consider
specifying project.requires-python = ">= 3.8"
in your pyproject.toml
file,
or specifying "py38"
for the
target-version
setting in your Ruff configuration.
New error codes for flake8-type-checking
rules #
Many of Ruff's rules are reimplementations of lints from other, pre-existing tools. Occasionally this means we need to merge overlapping rules, remove duplicate rules, or rename rules for consistency with other linters.
This release changes the error codes for the rules in our
flake8-type-checking
category
from TCH
to TC
. This matches changes that were made in the original
flake8-type-checking
plugin from which these rules were originally adapted.
Selecting a rule using a deprecated TCH
code will still work, as Ruff will
automatically map the deprecated code to the updated code. However, Ruff will
now emit a warning if a deprecated error code is used. Users are encouraged to
update their configuration files to use the new error codes where possible.
Removal of six deprecated rules #
Six rules have been removed in this release. We realized that the changes they were asking users to make to their code were either unnecessary or outright undesirable:
-
missing-type-self
(ANN101
) andmissing-type-cls
(ANN102
) have both been removed from ourflake8-annotations
category. Both have been deprecated since Ruff 0.2.0. -
pytest-missing-fixture-name-underscore
(PT004
) andpytest-incorrect-fixture-name-underscore
(PT005
) have both been removed from ourflake8-pytest-style
category. Both have been deprecated since Ruff 0.6.0. -
unpacked-list-comprehension
(UP027
) has been removed from ourpyupgrade
category. It's been deprecated since Ruff 0.6.0 -
E999
, deprecated in Ruff 0.5.0, has been removed as an error code. Syntax errors are now always reported and cannot be suppressed.
Changes to calculations of Unicode widths #
In some cases, users with code that contains Unicode characters may see their
code reformatted, or may see changes in whether
E501
is reported on their
code. This is because Ruff v0.8 uses a new version of the
unicode-width Rust crate to
calculate the width of Unicode characters and strings.
Few users are likely to be affected by this change.
XDG (i.e. ~/.local/bin
) now used by standalone installer #
Previously, Ruff's standalone installer used $CARGO_HOME
or ~/.cargo/bin
for its target install directory. But this made little sense, as Ruff has no
relationship to Cargo.
Ruff will now be installed into one of $XDG_BIN_HOME
,
$XDG_DATA_HOME/../bin
, or ~/.local/bin
(tried in that order). This change
is only relevant to users of the standalone Ruff installer (using the shell or
PowerShell script). If you install Ruff using uv, pip, or any other
installer, you should be unaffected.
If you are using the standalone installer in CI, you may need to update your
workflow to add ~/.local/bin
to the PATH
.
If you upgrade with ruff self update
, the previous $CARGO_HOME
location will
continue to be used.
If you want to migrate from the old install location to the new one, simply
remove the Ruff binary from the old location (rm ~/.cargo/bin/ruff
on Unix),
and then install the latest version.
Changed location of pydoclint
diagnostics #
If a diagnostic is emitted for a rule in our
pydoclint
category, the
line number of the diagnostic will now always point to the first line of the
problematic docstring. Previously this was not the case.
If you've opted into these preview
rules but have them suppressed using
noqa
comments in
some places, this change may mean that you need to move the noqa
suppression
comments. Most users should be unaffected by this change.
Rule stabilizations #
The following 13 rules have been stabilized and are no longer in preview:
builtin-import-shadowing
(A004
)mutable-contextvar-default
(B039
)fast-api-redundant-response-model
(FAST001
)fast-api-non-annotated-dependency
(FAST002
)dict-index-missing-items
(PLC0206
)pep484-style-positional-only-parameter
(PYI063
)redundant-final-literal
(PYI064
)bad-version-info-order
(PYI066
)parenthesize-chained-operators
(RUF021
)unsorted-dunder-all
(RUF022
)unsorted-dunder-slots
(RUF023
)assert-with-print-message
(RUF030
)unnecessary-default-type-args
(UP043
)
Other behavior changes #
This release also improves the behavior of several other rules.
Firstly, invalid-pyproject-toml
(RUF200
)
now understands pyproject.toml
files that use features from the latest version of
PEP 639. The PEP has been provisionally
accepted, and is already enjoying widespread adoption among parts of the
community.
Finally, Ruff v0.8 stabilizes several behaviors that were previously only available to users who opted into preview mode:
-
ambiguous-variable-name
(E741
) now ignores violations in stub files (Python source code files with.pyi
file extensions). Authors of stubs typically don't control the names used for variables in their stubs. Rather, the priority is generally for a stub to exactly emulate the interface of the corresponding Python module exposed at runtime. -
printf-string-formatting
(UP031
) now reports allprintf
-like usages in your code. Previously, violations were only reported if an autofix was available. -
The autofix for
zip-instead-of-pairwise
(RUF007
) has been promoted to stable (though it is still categorized as unsafe).
View the full changelog on GitHub.
Read more about Astral — the company behind Ruff.