When you want to force someone to write the code the way you want: you need to create a rule for that.
There are multiple options of how this can be done. This guide will walk through all possible cases and cover every decision path.
Deciding what exactly to write¶
The most important thing is the question: what kind of rule do you want to create?
Depending on the answer you can end up with either:
Creating a new
Just a new violation and checking logic to find problems with your code
What does it depend on internally?
Writing new plugin¶
First of all, you have to decide:
Are you writing a separate plugin and adding it as a dependency?
Are you writing a built-in extension to this styleguide?
How to make a decision?
Will this plugin be useful to other developers without this styleguide?¶
If so, it would be wise to create a separate
Then you can add newly created plugin as a dependency.
Our rules do not make any sense without each other.
It is also useful when you try to wrap an existing tool into
Real world examples of tools that are useful by them self:
Can this plugin be used with the existing checker?¶
flake8 has a very strict API about plugins.
Here are some problems that you may encounter:
Some plugins are called once per file, some are called once per line
Plugins should define clear
It is impossible to use the same letter violation codes for several checkers
So, if you want a plugin to work with each logical line - you have to create a custom plugin.
Real world examples of plugins unsuitable for this checker:
Is this rule out off scope?¶
There are awesome tools that cannot be added
because they are just simply out of scope.
This means that they cover very specific case or technology
and not just good-old
Real world examples of plugins that are out of scope:
All these plugins should be installed individually to the end-user dependencies. And only when user really want it. So, it is up to the user to decide.
And these plugins while being awesome won’t be added to our project at all.
Writing new visitor¶
First of all, you have to decide what base class do you want to use?
There are several possibilities:
When to choose what base class? Imagine that you have several ideas in mind:
I want to lint module names not to contain numbers
I want to lint code not to contain number
I want to lint code to disallow multiplication of exactly two number
Each of these tasks will require different approaches.
Will require to subclass a filename-based visitor
Will require to subclass a
Will require to subclass a
How to differ these cases by yourself?
You need to read though the docs of
You can have a look at the existing visitors
But, you might not want to write a new visitor. You can reuse existing ones and write only a violation and checking logic.
Technical documentation about the Visitors API is available.
Writing new violation¶
The only thing you should care about is to select the correct base class for new violation.
It only depends on already selected visitor type, so you won’t have to make this decision twice.
Technical documentation about the Violations API is available.
Writing business logic¶
We do this inside the
but we create protected methods and place logic there.
Consider this example:
class WrongComprehensionVisitor(BaseNodeVisitor): _max_ifs = 1 def _check_ifs(self, node: ast.comprehension) -> None: if len(node.ifs) > self._max_ifs: # This will restrict to have more than 1 `if` # in your comprehensions: self.add_violation(MultipleIfsInComprehensionViolation(node)) def visit_comprehension(self, node: ast.comprehension) -> None: self._check_ifs(node) self.generic_visit(node)
You may also end up using the same logic over and over again.
In this case we can decouple it and move to
Then it would be easy to reuse something.
Writing end-to-end tests¶
In end-to-end tests we check that our visitor, violation and business logic work correctly together all the way from flake8 config file to its output.
To check all supported violations, we have two modules containing code which
noqa_controlled.py. The first is for all
possible violations while the second is only for those which may be tweaked
i_control_code option. If violation may be ignored (or instead,
i_control_code, the appropriate piece of code should be
added to both modules.
The next thing is test itself which should reside in
tests/test_checker/test_noqa.py module. The main test functions are
written already, so probably the only thing to do is to put the violation
code into either
container, or into both. For example, if the violation is raised with
i_control_code=True, it must be placed into
1 and into
SHOULD_BE_RAISED_NO_CONTROL with value
By doing this we check that the violation is raised in one situation and
is not raised in opposite one. If the violation is ignored when
1 it containers. If the
violation cannot be tweaked with
i_control_code it should only be
SHOULD_BE_RAISED container with appropriate value.