Static code analyzers

Apart from the code formatters, there are also linters and static code analyzers.

Flake8

One of the most popular linters is called Flake8. Unlike black that formats your file and doesn’t really give you any feedback when you are coding, Flake8 works the other way around. It won’t change your code, but it will provide you with real-time warnings in your code editor.

Flake8 combines three different projects:

  • pyflakes - gives you warnings about unused modules, undefined variables, etc.
  • pycodestyle - gives you warnings about PEP8 violations.
  • McCabe - (disabled by default) gives you warnings about too high cyclomatic complexity of your functions (“are your functions are too complicated?”).

Flake8 extensions

One of the reasons for Flake8’s popularity is its massive catalog of plugins that can help you further customize it. Some extensions will modify the default behavior of Flake8, while others will give you additional options that you can use when running flake8.

You can find a list of the most popular ones here: awesome-flake8-extension

Some of the available extensions:

  • flake8-builtins - makes sure you don’t use Python builtins as variables or parameters
  • flake8-bugbear - an additional set of checks (some are opinionated) that will complain when you, for example:
    • Use except: instead of except Exception:
    • Use ++n
    • Use .strip() on a multiline string
    • Use a length-one tuple
  • flake8-comprehensions - helps you write better list/set/dictionary comprehensions
  • flake8-docstrings - enforces PEP257
  • flake8-eradicate - reports commented out code (“dead code”)
  • flake8-isort - check if your import statements are sorted according to the isort recommendations
  • flake8-broken-line - complains you use backslash for line breaks (try using parenthesis or tripple quotes instead)
  • flake8-quotes - if you prefer single quotes over double quotes (or the other way around), this plugin can help you enforce them
  • flake8-rst-docstrings - looks for problems in your documentation (usefull if you are using Sphinx that we will talk about later)
  • darglint - checks if your docstrings are matching functions definitions (for example, if you remembered to describe all the parameters and the return value)
  • flake8-mutable - checks that you don’t use mutable default arguments

If you are using a lot of Flake8 plugins between multiple projects, check out FlakeHell or tox. They both let you install and configure all flake8 plugins by modifying one configuration file (that you can then share between projects).

How to install and use Flake8?

You can install flake8 and its extensions with pip or pipx:

$ pip install flake8
# Or even better
$ pipx install flake8

# Install extensions
$ pip install flake8-docstrings flake8-isort
# Or 
$ pipx inject flake8 flake8-docstrings flake8-isort

And then run it on files or directories that you want to check:

$ flake8 my_project
my_project/setup.py:40:80: E501 line too long (103 > 79 characters)
my_project/tests/test_my_project.py:9:1: F401 'my_project.some_function' imported but unused
my_project/my_project/some_file.py:1:7: F821 undefined name 'my_var'

Of course, the best way to use Flake8 is to enable is directly in your code editor. VS Code will offer you this option when you open a Python file, but if it doesn’t, run the Python: Select Linter command.

Other tools

Flake8 and black are the most popular tools that can make your life easier. They complement each other nicely. But there are some other static analysis tools that you might be interested in:

Pylint

Pylint is another static analysis tool that ” looks for programming errors, helps enforcing a coding standard, sniffs for code smells, and offers simple refactoring suggestions”. It has some overlap with Flake8, but you can safely use both at the same time.

Pylint is much more strict than Flake8. It will complain if you have “too few public methods”, “too many local variables” or “classes with no __init__ method”. It’s much easier to write code that will make Flake8 happy than it is to write Pylint-compatible code. Especially if you want to use some more “unusual” code constructs. Sometimes you might know what you are doing, but Pylint will still try to protect “you” from “yourself”.

Try running Pylint on your existing project and see if you like it. Its motto is: “it’s not just a linter that annoys you!” but I personally don’t use it - it annoys me ;)

If you are working with frameworks like Django, make sure you install additional plugins for that framework. For example, pylint-django will suppress warning when you use “class Meta” that only contains attributes, that would normally be reported by pylint.

Bandit

Bandit is a tool designed to find common security issues in Python code. Running it out of the box on your project will give you plenty of false positives. For example, it will complain about assert statements in your pytest files. But once you spend a moment to tweak the settings (ignore some folders or some of the warnings), it will print some possibly useful information about:

  • Insecure usage of some modules
  • Possibility of SQL injection
  • Silently ignoring Exceptions
  • And more

If you are a beginner, bandit can be a good tool that will help “review” your code.

If you are already using flake8, there is a flake8-bandit extension that you can use, so you don’t have to install bandit separately.

pydocstyle

If you want to make sure that your documentation is written according to the PEP257, you can install pydocstyle.

Just keep in mind that it will complain about missing documentation of every function or module that you didn’t bother to documents.

This one is also available as Flake8 plugin under the name of flake8-docstrings

Vulture

Can help you find unused code: unused functions, variables, or modules.

wemake-python-style guide

It’s another strict linter.

It combines:

  • Flake8
  • almost 20 Flake8 extensions
  • and some custom rules on top of that

It might be too much for your project, but maybe you will like it.

Prospector

It’s like Flake8, but instead of pyflake, it’s using pylint (it combines pylint, pycodestyle, and maccabe).

You can automate most of those plugins by using pre-commit. pre-commit lets you plug linters, formatters, and static code analyzers as git hooks - they will be automatically called when you create a commit.
Here is a good tutorial on how to set black and Flake8 pre-commit hooks.