How to write Flake8 plugins 😍
Really important parts of code reviews are almost impossible to automate: Architectural decisions and logical bugs. They are too customized to your codebase; too specific for the pull request.
However, many comments in code reviews are not like that. They are about simply style decisions, common minor mistakes, and misconceptions. They are valuable as well, but they distract the reviewer from the harder parts. The lining system Flake8 allows you to write plugins that automatically capture those simple things. You can execute them in your CI pipeline and thus never need to think about them again.
In this article, you will learn how to create a Flake8 plugin. As an example, we will create a plugin which recognizes the pattern
not (a == b)
and thus can suggest using the following instead:
a != b
Flake8 is a linter which only checks rules. It does not change the code. Every rule has a message and a code. The built-in rule codes begin either with E (Error) or with W (Warning). After that, a 3-digit number follows:
- E101: indentation contains mixed spaces and tabs
- E111: indentation is not a multiple of four
- E112: expected an indented block
One can select the rules which one wants to check on a prefix-basis:
# Check all rules beginning with "E1" and nothing else
flake8 --select E1 .
Alternatively, one can blacklist rules:
# Check all rules except the ones beginning with E1 or W512
flake8 --ignore E1,W512 .
Plugins need a 3-character prefix. For my plugin flake8-simplify, I chose SIM as a prefix.
The 3-character prefix should not start with E or W as people might want to completely block Flake8 W-rules.
The Flake8 Plugin Skeleton
Cookiecutter is a command-line utility that allows you to create a project from scratch by using a template. Install it via
pip install cookiecutter
To create your Flake8 plugin template, use:
The Flake8 Plugin class
You need to create a plugin class like this:
You need to point to that class in the
AWE by the 3-character code you want to have for your rules. We are, of course, not there jet. The visitor class still needs to be implemented. But to understand the visitor, we need to understand Pythons AST.
Understanding Pythons AST
An Abstract Syntax Tree (AST) is another representation of the code.
Install astpretty to understand how the AST looks like for a piece of code you’re interested in:
pip install astpretty
Here is how you use it:
$ astpretty --no-show-offsets /dev/stdin <<< "not a == b"Module(
Next, we need to recognize that pattern.
A Flake8 visitor class
The logic of the linter is in the visitor class. The visitor gets called for every node in the AST. It has various methods which are based on the operations you see within astpretty, e.g.
visit_UnaryOp . This method receives an
ast.UnaryOp with which you can do whatever you want. More often than not, this will not be of the pattern you need. In this example, you need to look for the
op == Not() and
This is how it’s done:
I like astor.to_source to get a string like it was in the source code back from the AST node.
Testing Flake8 plugins
A big shoutout to Anthony Sottile. Before his video (linked below), I had no clue how to test Flake8 plugins. Thank you ❤️
It’s trivial once you know it: You create a string that contains a line or multiple lines of code. You pass it to
ast.parse to get the abstract syntax tree. That one can be passed to your plugin, which should generate some output. That output can then be checked:
A non-trivial test looks like this:
This is part of flake8-simplify 0.2.0. I’ve pinned the version so that you can see the code in the simple form it has right now.
Please make me proud and automate your reviews — make the following comics true for Flake8 plugins as well :-)