Linting with Ruff
The Problem
When you write code, you may not always follow the best practices. That’s ok! First you should write code that works. Then, before committing, you should clean it up, take a second look, and make sure it’s readable and maintainable.
Examples of common issues:
- You may have a variable named
l
ordf
which is not very descriptive. - You use
print
statements for debugging and forget to remove them. - You have unused imports or variables.
- You have commented out code that should be removed.
- You have hardcoded passwords or API keys.
- You have f-string with missing variables.
- You use
eval
function orassert
statement. - Your class name is not in CamelCase, or your function name is not in snake_case.
And many more.
The Solution
As you can see above, all of those issues are easy to catch and fix one-by-one, but there are so many of them that it’s easy to miss some. That’s why we have linters, and ruff
seems to be the best one in the Python world.
While formatting tools like ruff format
take care of the style, linter like ruff check
check for more complex issues and potential bugs. It operates based on set of rules (like the ones above), so you can customize it to your needs. In tapyr
we propose a set of rules in ruff.toml
that we find useful in Shiny for Python applications.
At first, you may find it annoying that it catches so many issues, but over time you’ll appreciate it. You may wonder what’s wrong with my name l
for a list or df
for a DataFrame?, but the truth is that it’s better to have meaningful names. Often linter will catch issues that you wouldn’t notice otherwise.
Example: When you write
def divide(a, b):
= a / b
c return a
ruff
will catch the following issue
example.py:2:5: F841 Local variable `c` is assigned to but never used
Found 1 error.
Only now you realize that you wanted to return c
instead of a
😱.
Then there are other rules, for example bandit
security checks that looks for hardcoded passwords, insecure use of pickle
, SQL queries (potentially leading to SQL injection), eval
functions and many more.
Using and configuring ruff
As with formatter, for ruff
configuration, please refer to the official linter documentation.
To run ruff
on your project, just run:
ruff check
If you want to fix some issues automatically, you can run:
ruff check --fix
Why not use flake8
/pylint
/pyflakes
?
ruff
is MUCH faster, while offering (almost) the same functionality. Almost all the rules from flake8
/pylint
/pyflakes
are available in ruff
already. On top of that, ruff also offers bandit
security checks and more.