Ruff v0.5.0 is available now! Install it from PyPI, or your package manager of choice:
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.
Migrating to v0.5 #
In most cases, you should be able to upgrade seamlessly to Ruff v0.5 without having to change a thing. Nonetheless, this release includes a few deprecations and breaking changes that may require action when upgrading.
The most notable breaking changes in this release are:
- Several deprecated settings and commands have been removed
- Selecting
ALL
no longer leads to deprecated rules being selected - Several rules have been remapped to new error codes. (This may lead to previously disabled rules being enabled on your code.)
Output format now defaults to full
#
The exact format Ruff uses to report lint violations is configurable using the
--output-format
option.
Ruff v0.2 introduced a new option --output-format=full
:
unlike other formats, this displays the code snippet that triggered the lint violation
as well as the violation's error code and error message.
This can provide important context when you're trying to address a violation reported by Ruff.
We want our diagnostics to be as informative and actionable as possible,
so this release makes --output-format=full
the default behavior for Ruff.
In the future, this change will also enable us to show suggested fixes as part of reporting violations,
further improving the experience for our users.
The new default violation format looks like this:
❯ ruff check
bundled/tool/ruff_server.py:2:8: F401 [*] `pathlib` imported but unused
|
1 | import os
2 | import pathlib
| ^^^^^^^ F401
3 | import shutil
4 | import site
|
= help: Remove unused import: `pathlib`
Found 1 error.
[*] 1 fixable with the `--fix` option.
Use --output-format=concise
or the RUFF_OUTPUT_FORMAT
environment variable
to go back to a more concise output without code snippets:
❯ ruff check --output-format=concise
bundled/tool/ruff_server.py:2:8: F401 [*] `pathlib` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
Selecting ALL
now excludes deprecated rules #
The ALL
selector is an easy way to enable all of Ruff's rules from the command line or via a configuration file.
In previous releases of Ruff, we took this quite literally —
using --select=ALL
on the command line selected all of Ruff's rules, even those which were deprecated!
This could, in turn, lead to Ruff emitting warnings about deprecated rules having been selected.
Selecting ALL
now selects all of Ruff's rules except any rules that have been deprecated.
We expect this is what most of our users using --select=ALL
wanted all along.
Deprecated rules can still be enabled, but you'll now have to explicitly specify them on the command line or in a configuration file:
ruff check my_code.py --select=ALL,DEPRECATED_RULE001
XDG specification now used on macOS #
Previously, Ruff discovered user-level configuration in ~/Library/Application Support/ruff/ruff.toml
on macOS
and followed the XDG specification on other Unix systems
(defaulting to ~/.config/ruff/ruff.toml
). Ruff now follows the XDG specification on all Unix platforms, including macOS.
When upgrading, Ruff will tell you if you're using a user-level configuration file stored in the Application Support/ruff
directory
and will give tips on where to move your configuration file.
Rule remappings #
Many of Ruff's rules are reimplementations of lints from other, older tools. Occasionally this means we need to merge overlapping rules, remove duplicate rules, or rename rules for consistentency with other linters.
This release merges the [flake8-trio
] and flake8-async
categories into a single flake8-async
category,
following the merger of the flake8 plugins from which many of these rules were originally derived.
All the rules in these two categories have been remapped to new codes:
flake8-async
blocking-http-call-in-async-function
:ASYNC100
has been remapped toASYNC210
open-sleep-or-subprocess-in-async-function
:ASYNC101
has been split intoASYNC220
,ASYNC221
,ASYNC230
, andASYNC251
blocking-os-call-in-async-function
:ASYNC102
has been merged intoASYNC220
andASYNC221
flake8-trio
trio-timeout-without-await
:TRIO100
has been remapped toASYNC100
trio-sync-call
:TRIO105
has been remapped toASYNC105
trio-async-function-with-timeout
:TRIO109
has been remapped toASYNC109
trio-unneeded-sleep
:TRIO110
has been remapped toASYNC110
trio-zero-sleep-call
:TRIO115
has been remapped toASYNC115
Attempting to select rules using the deprecated TRIO
codes will cause Ruff to emit a warning;
however, Ruff will automatically map the deprecated code to the updated code.
The old ASYNC100
, ASYNC101
, ASYNC102
or ASYNC1
codes will need to be manually updated to their new ASYNC2XX
codes.
As well as the ASYNC
and TRIO
remappings, repeated-isinstance-calls
has also been remapped from PLR1701
to SIM101
.
Changes to E999 and reporting of syntax errors #
Prior to Ruff v0.4.0, Ruff would immediately stop parsing a Python file if it encountered a single syntax error. This meant that Ruff's linter would only report a single violation regarding invalid syntax, even if there was more than one syntax error in the file. With the release of the new hand-written parser in Ruff v0.4.0, Ruff can recover from syntax errors and continue parsing the file. This means Ruff can now report all syntax errors in a file, not just the first one.
The E999
rule was used to report syntax errors, allowing users to silence
these reports via Ruff's disablement mechanisms. This release deprecates the
E999
rule, which will be removed in a future release. Ruff will now always
report all syntax errors it encounters, meaning users can no longer ignore
them. The motivation for this change is to ensure users are always aware
of syntax errors, as these prevent proper linting. By making syntax error
reporting mandatory, we eliminate the risk of users missing these critical
issues.
To prepare for this change, we recommend that you remove any disablement of the
E999
rule as it will no longer have any effect. You can use
our unused-noqa
rule and autofix to automatically remove any unused noqa: E999
comments from your code.
New server setting: showSyntaxErrors
#
Now that Ruff always reports syntax errors, we understand that some users may want to hide these diagnostics in their editor. This is especially relevant when using multiple Python language servers, Ruff being one of them: multiple servers complaining about the same syntax error can create a chaotic user experience.
To accomodate this use case and to give users more control over their editor
experience, this release introduces a new showSyntaxErrors
server setting.
This setting is true
by default and controls whether syntax error diagnostics
are shown in the editor.
In Ruff's VS Code extension, you can configure this using the ruff.showSyntaxErrors
setting:
{
"ruff.showSyntaxErrors": false
}
In other editors using the new ruff server
or ruff-lsp
, you can configure
this using the showSyntaxErrors
server setting. For example, in Neovim you can
use the following config:
require('lspconfig').ruff.setup({
init_options = {
settings = {
showSyntaxErrors = false
},
},
})
-- Same configuration for `ruff_lsp`
--statistics
now shows rule names #
ruff check --statistics
can be used to get a report summarising all lint violations in your project, grouped by lint rule.
The format for this report has now been altered to show the rule's name alongside the rule's code.
The new format looks like this:
❯ ruff check . --statistics
236 W191 [ ] tab-indentation
85 PLR2004 [ ] magic-value-comparison
84 UP031 [*] printf-string-formatting
81 PLR0913 [ ] too-many-arguments
73 RUF012 [ ] mutable-class-default
65 Q000 [*] bad-quotes-inline-string
Previously, Ruff showed the error message of the first violation instead of the rule's name. However, this could sometimes lead to strange results, as the message can vary between violations of the same rule. For example:
❯ ruff check . --statistics
236 W191 [ ] Indentation contains tabs
85 PLR2004 [ ] Magic value used in comparison, consider replacing `-128` with a constant variable
84 UP031 [*] Use format specifiers instead of percent format
81 PLR0913 [ ] Too many arguments in function definition (10 > 5)
73 RUF012 [ ] Mutable class attributes should be annotated with `typing.ClassVar`
65 Q000 [*] Double quotes found but single quotes preferred
Stabilized rules #
The following rules have been stabilized and are no longer in preview:
mutable-fromkeys-value
(RUF024
)default-factory-kwarg
(RUF026
)django-extra
(S610
)manual-dict-comprehension
(PERF403
)print-empty-string
(FURB105
)readlines-in-for
(FURB129
)if-expr-min-max
(FURB136
)bit-count
(FURB161
)redundant-log-base
(FURB163
)regex-flag-alias
(FURB167
)isinstance-type-none
(FURB168
)type-none-comparison
(FURB169
)implicit-cwd
(FURB177
)hashlib-digest-hex
(FURB181
)list-reverse-copy
(FURB187
)bad-open-mode
(PLW1501
)empty-comment
(PLR2044
)global-at-module-level
(PLW0604
)misplaced-bare-raise
(PLE0744
)non-ascii-import-name
(PLC2403
)non-ascii-name
(PLC2401
)nonlocal-and-global
(PLE0115
)potential-index-error
(PLE0643
)redeclared-assigned-name
(PLW0128
)redefined-argument-from-local
(PLR1704
)repeated-keyword-argument
(PLE1132
)super-without-brackets
(PLW0245
)unnecessary-list-index-lookup
(PLR1736
)useless-exception-statement
(PLW0133
)useless-with-lock
(PLW2101
)
There are many more new rules still in preview. We'd highly recommend checking them out! You can turn on preview and opt-in to rules one-at-a-time.
Stabilized behaviors #
Some of our stable lint rules have had their scope significantly expanded or reduced in this release. When we find that we need to make changes like this, we'll often made them in preview mode only (even for stable rules) for several minor releases, before "promoting" the behavior change to stable mode so that all users can benefit from it.
The following behavior changes have been promoted to stable for Ruff v0.5:
-
is-literal
(F632
) now also warns for identity checks against list, set or dictionary literals. For example, antipatterns such asif x is {}
orif y is ["foo"]
will now be identified by the check. -
needless-bool
(SIM103
) now detectsif
expressions with implicitelse
branches. For example, the following snippet would not have been previously flagged by the rule, but will be in Ruff v0.5.0+:def is_greater_than_ten(number: int) -> bool: if number > 10: return True return False
-
module-import-not-at-top-of-file
(E402
) now allowsos.environ
modifications between import statements. -
type-comparison
(E721
) now allows idioms such astype(x) is int
. -
yoda-conditions
(SIM300
) now flags a wider range of expressions. For example, expressions such as[] == make_me_a_list()
would previously not have triggeredyoda-conditions
, but will in Ruff v0.5.0+.
Removed deprecated features #
This release removes support for the following deprecated features:
CLI #
ruff <path>
: Useruff check <path>
ruff --clean
: Useruff clean
ruff --generate-shell-completion
: Useruff generate-shell-completion
Settings #
output-format=text
andshow-source
:- With
show-source=true
: Useoutput-format=full
- With
show-source=false
: Useoutput-format=concise
- Without
show-source
: We recommend usingoutput-format=full
, butconcise
is also available.
- With
tab-size=<size>
: Useindent-width=<size>
When upgrading, Ruff will tell you if you use any of these commands or settings and advise you on what command or setting to use instead.
View the full changelog on GitHub.
Read more about Astral — the company behind Ruff.
Thanks to Micha Reiser and Dhruv Manilawala, who both contributed to this blog post.