Ruff

Move Over Flake8, It’s Time to Start Using Ruff

Linters are an important tool for detecting errors, code-smells, poor syntax and other problems. Ruff is a newer one that should be on your radar for a multitude of reasons. Let’s explore them.

What is Ruff?

In a nutshell, Ruff is a super, mega-ultra fast linter for Python that can be used to replace tools such as Black and Flake8. It’s good practice to check your homework and a good standard to use as a base for writing code is PEP8.

OK, so what is PEP8?

Line 7 of The Zen of Python (Python’s guiding principles) states unambiguously: “Readability counts” and whist Python is deliberately ultra-flexible, just because you can write Python is an infinite number of ways doesn’t mean that you should. For the most part, the code we write will, at some point, be read, maintained or otherwise amended by others. Developers worked here before us and will after we leave, working to a baseline standard that is accepted across the Python world means that anyone else can easily read, and therefore maintain, our projects.

One more apt line from the Zen of Python: “Special cases aren’t special enough to break the rules.”

We already use Flake8, so why would we bother with a new toy?

One of the big selling points of Ruff is it’s speed. Ruff claims to be up to 100 times faster than Flake8, which in itself can be a reason. That in itself is not a reason to change anything, unless old and slow is a real problem. In many cases, Flake8 will be found in CI pipelines, where we may have to pay per-second. Those seconds add up, quickly. This means that there is the potential for a real cost saving for what is little more than changing a single word in a pipeline, from “flake8” to “ruff”.

Furthermore, Ruff can be used as a replacement for other commonly used tools and linters, such as black, isort, pydocstyle and pyupgrade.

On type checking, whilst Ruff can catch some typing issues, it is not a formal type checker. I would continue to use something like mypy.

Ruff respects pyproject.toml, which is great if you’re using something like Poetry. Flake8 on the other hand doesn’t because until Python 3.11, Python didn’t have a native TOML parser in the core, something that even now requires an external library, flake8-pyproject. If you want to keep your Ruff and package TOML files separate, you can use a ruff.toml file instead.

Setup

pip install ruff and it’ll work OOTB. You don’t need to create a distinct configuration unless you want to, the defaults are well thought out.

Demo

Let’s write some poorly constructed Python. Unused imports, super-long lines, bad indentation and more.

import datetime

def get_a_silly_long_string():
return "foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar"


def multiple_spaces():
return "bar" + "baz"


def poor_indent():
return "Foo"


if __name__ == "__main__":
pass

Whilst Ruff does work out-of-the-box, we can specify the rules we’d like to focus on.

Now with can either create a ruff.toml with the following:

select = [
"E111",
"E117",
"E222",
"E",
"F",
]

Or, we can add this to our pyproject.toml:

[tool.ruff]
select = [
"E111",
"E117",
"E222",
"E",
"F",
]

Now we run the linter with ruff check .

main.py:1:8: F401 [*] `datetime` imported but unused
main.py:4:89: E501 Line too long (140 > 88 characters)
main.py:8:1: E117 Over-indented
main.py:8:25: E222 Multiple spaces after operator
main.py:12:1: E111 Indentation is not a multiple of 4
Found 5 errors.
[*] 1 potentially fixable with the --fix option.

Let’s see what the auto-fix does:

$ ruff check . --fix

main.py:3:89: E501 Line too long (140 > 88 characters)
main.py:7:1: E117 Over-indented
main.py:7:25: E222 Multiple spaces after operator
main.py:11:1: E111 Indentation is not a multiple of 4
Found 5 errors (1 fixed, 4 remaining).

Whilst Ruff can be used instead of black, it’s not really intended as such and they do differ.

IDEs

You can add Ruff to any IDE that supports LSP (a protocol that strandardises the language used for defining IDE plugins, enabling cross-application plugin support, like intellisense). I’m not going to go through all IDEs, but…

Pycharm

Settings > Plugins > Marketplace and search for Ruff, install. Given a minute or two to index your project, it will then highlight far in excess of what Pycharm uses as a baseline minimum.

VS Code

Bring up the extensions panel with CRTL + shift + x, then search for “ruff”.

Finally

Ruff is an outstanding addition to any Python developer’s toolbox. It’s blisteringly fast, accurate and can be used as a single drop in replacement for many libraries, which often come with maintaining multiple different config files. It’s designed to be trivial to use and can be run adequately with no config whatsoever. Give it a go.