ruff
is a tool to check and lint Python code. It’s written in Rust and it
really is blazing fast. The first time I ran it on a real codebase I
immediately got an error and, for a split-second, though it was an execution
error. It had actually run successully in a few milliseconds and found an issue
in my code (a silly one, but a valid one).
A flake8
replacement
ruff
implements all the rules from pyflakes
and pycodestyle
, which
essentially implies it serves as a full replacement for flake8
. Additionally,
it implements a lot of rules for flake8
plugins (these need to be enabled
manually).
I’ve transitioned a couple of projects from flake8
to ruff and I’m very happy
with the results. On top of re-implementing the rules in a very fast way,
ruff
also implements “severities”: diagnostic may be Errors, Warnings, Info
or Hint. This helps a lot; when drafting code (e.g.: in a very rough state),
errors (e.g.: missing import, reading from undefined variable, etc) are not the
same as hints (e.g.: missing or extra whitespace, too many empty newlines, etc).
I actually suggested implementing severities for flake8, but this was deemed completely unacceptable; the authors believe that every single user out there should re-implement this mapping themselves, which is an absurd idea that doesn’t scale.
Finally, ruff
can auto-fix a lot of issues that existing tools could only
detect (for example: rewriting loops using comprehensions).
An isort
replacement
There’s nothing truly broken with isort
to be honest. It’s a great tool. And
ruff
implements all its functionality into the same codebase. It kinda makes
sense; both tools need to parse and entire codebase and analyse it. Adding a
new check to a single tool is simpler implementing two tools. Plus, combining
the auto-fixing of flake8
rules with auto-fixing of imports into one action
makes code maintenance a lot simpler.
A pyupgrade
replacement
You can see where this is going, right? ruff
also implement fixes done by
pyupgrade
.
For those not familiar, pyupgrade
allows specifying which version of python a
project is targeting, and will automatically update syntax for newer language
and drop obsolete syntax.
This usually removes a lot of redundant code, and updates code to newer (easier to read) syntax.
Aside from fixing these automatically, ruff
will show these potential fixes
as warnings when checking code, or when running via a LSP…
A dedicated language server: ruff-lsp
ruff-lsp
is a language server built on top of ruff
. It’s very simple and
straightforward to configure in neovim or any of many other code
editors. It provides diagnostics for all of ruff
’s checks, as well as actions
to apply fixes via an IDE’s interface.
My experience so far with ruff-lsp
has been very pleasant. A lot of useful
feedback when editing any Python file. It’s fast, and has very sensible
defaults, so even for projects that don’t have a ruff configuration the
diagnostics are quite valuable (e.g.: unused variable, unused import, missing
definition, all valid checks).
Summarising
You can read more about ruff
in today’s post by the author. If you’re
interesting in switching a project from flake8
+isort
+pyupgrade
, it’s
usually pretty straighforward. Here’s an example of how I switched
todoman
to use ruff
.
For project with many developers (e.g.: those where you work in a team), be sure to align with other devs. They will need to ensure that their workflow is updated.