Talking About Flake8 Plugin Mechanics

  • Post author:
  • Post last modified:2020-11-25
  • Post category:Code Review
  • Reading time:6 mins read

This article is about Flake8, which we also use in SideCI. There may be duplicate parts from the article we wrote just about one year ago, but I will mainly talk about the plugins of Flake8.

In this article, I will cover the following contents, based on the newest version of Flake8 at the time this article was written(version 3.2.1).

  • About Flake8
  • Plugins of Flake8
  • How to make Flake8 plugins

About Flake8

Flake8 is implemented as a wrapper for the following tools:

Here we see a tool that didn’t appear in the article one year ago, pycodestyle. This tool was renamed from pep8. In using Flake8, you don’t need to be conscious about pep8 being renamed to pycodestyle, but it’s worth keeping in mind.

The indications from Flake8 are classified as follows, if you don’t have plugins installed:

  • Exxx, Wxxx: Outputs from pycodestyle
    Warnings of codes not following PEP8
  • Fxxx: Outputs from PyFlakes
    Warnings of codes that will cause an error when executing
  • C901: McCabe
    Warnings of codes with high cyclomatic complexity

Plugins of flake8

The standard Flake8 wraps the 3 tools listed above, but only McCabe is used as a pure plugin of Flake8. (McCabe can be used alone by itself, but McCabe is also implemented so that it can be used via Flake8)

So, what exactly are the “plugins” for Flake8? What does it refer to?

Flake8 utilizes the Entry Points mechanism of setuptools to load third-party plugins. setuptools is a Python library that provides a mechanism for packaging, and is a de facto in the Python world.

The important point is that the plugins of Flake8 just use the mechanism from setuptools, and do not have their own plugin mechanism.

By this, it makes it possible for Flake8 plugin creators to easily make plugins by packaging with the standard mechanism. Also, it is beneficial for Flake8 users because they can use the plugins by just installing them to their own environment.

Flake8 scans the Entry Points of the following namespaces:

  • flake8.extension
    A namespace for Entry Points for code checks
  • flake8.listen
    A namespace for Entry Points for Auto Correct
  • flake8.report
    A namespace for Entry Points for customizing the outputs of warnings

Major Flake8 plugins

  • hacking
  • flake8-docstrings
  • flake8-import-order

How to Make Flake8 Plugins

Like this, Flake8 has a mechanism that makes making plugins easy. Let’s try making a plugin of our own.

How to make Flake8 plugins is explained in Flake8’s documentation. There are other articles I have used as reference. Check the links at the bottom of this article.

Preparing the environment

This time, we’ll use Python 3.5.2. First, create an independent environment for working and install Flake8.

$ python -m venv venv
$ source venv/bin/activate
$ pip install flake8

Defining Entry Points

So, let’s start writing our code. First, create a setup.py file with the contents below. What’s important is the contents of the entrt_points passed to setup(). This will be the Entry Points as said above.

A dict is passed to the entry_points. Here, we want to Lint, so we pass this with flake8.extension as the key. The corresponding value for flake8.extension is a list type, in which we write a str like error_code = module:callable. This time, we’ll define a error code X801. (The error code here doesn’t have to be an error code in the current implementation, but we’ll do so to make it easier to understand)

# -*- coding: utf-8 -*-
from setuptools import find_packages, setup
setup(
name=’flake8-english-please’,
packages=find_packages(),
entry_points={
‘flake8.extension’: [
‘X801 = flake8_english_please.checker:english_checker’,
],
},
)

Implementation of Lint

Next, let’s write the code we’ll actually use for Lint. This time, we’ll implement a Lint named english_checker, which will detect letters other than English (or rather ASCII) in comments. (I know, it’s not very practical…)

First, you need to create a directory structure for the Python package.

$ mkdir flake8_english_please
$ touch flake8_english_please/__init__.py

Next, we’ll create a flake8_english_please/checker.py file with the following contents.

This lint will run checks per physical line.

A Lint which issues a warning when a non-ASCII character is detected in a comment

import tokenize
X801 = ‘X801 This is a country where we speak English’
def english_checker(physical_line, tokens):
for token_type, text, start, _, _ in tokens:
if token_type != tokenize.COMMENT:
continue
for index, x in enumerate(text):
if not ord(x) < 128:
return (start[1] + index, X801)
english_checker.name = ‘english_checker’
english_checker.version = ‘0.1.0’

The Lint implementation must specify one of the following as the first argument:

  • tree
    Used when recieving a Python AST and running a Lint per file.
    Implementing a class with a run method is also OK.
    In that case, implement so that it receives the tree in the constructor(__init__)
  • physical_line
    Used when running a Lint per physical line. (The present implementation)
    Can only define functions
  • logical_line
    Used when running a Lint per logical line.
    Can only define generators.

Additionally, you can request information you want from Flake8 as a second (or more) argument. For further information, refer to here.

By the way, in this example, we borrowed the warning message from TrumpScript.

Use the implemented plugin

Okay, let’s install our plugin.

$ python setup.py install

Now we’re ready to use our plugin.

To test, create a file like below (example.py) and execute flake8.

# この関数は何かをします(This function does something)
#
# Hi! はい
def some():
print(‘Some’)
$ flake8 example.py

If you can see the following output and the status code after executing the command is 1, our implementation is complete and running without a problem.

example.py:1:3: X801 This is a country where we speak English
example.py:3:7: X801 This is a country where we speak English

Summary

We implemented a very simple non-practical plugin this time, but I hope I showed how easy it is to implement a Flake8 plugin. If you have any good ideas, give it a go!

More articles about Python(pep8, pyflakes, flake8, haking, Pylint)


For more information about Sider, please go to our website.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.