Code Style Checks in Python

Code Style Checks in Python

After starting your first Python project, you might realize that it is actually not that obvious to be consistent with the way you write Python code. If you collaborate with other developers, your code style might differ, and the code can become somehow unreadable.

I hate coding style discussions as much as every engineer. Who has not seen hours of nitpicking on code reviews, a heated debate around the coffee machine or nerf guns battles to decide where the semicolon should be?

When I start a new project, the first thing I do is set up an automated style check. With that in place, there's no time wasted during code reviews about manually checking what's a program's good at: coding style consistency. Since coding style is a touchy subject, it's a good reason to tackle it at the beginning of the project.

Python has an amazing quality that few other languages have: it uses indentation to define blocks. While it offers a solution to the age-old question of "where should I put my curly braces?", it introduces a new question in the process: "how should I indent?".

I imagine that it was one of the first question that was raised in the community, so the Python folks, in their vast wisdom, came up with the PEP 8: Style Guide for Python Code.

This document defines the standard style for writing Python code. The list of guidelines boils down to:

  • Use 4 spaces per indentation level.
  • Limit all lines to a maximum of 79 characters.
  • Separate top-level function and class definitions with two blank lines.
  • Encode files using ASCII or UTF-8.
  • One module import per import statement and per line, at the top of the file, after comments and docstrings, grouped first by standard, then third-party, and finally local library imports.
  • No extraneous whitespaces between parentheses, brackets, or braces, or before commas.
  • Name classes in CamelCase; suffix exceptions with Error (if applicable); name functions in lowercase with words separated_by_underscores; and use a leading underscore for _private attributes or methods.

These guidelines really aren't hard to follow and they make a lot of sense. Most Python programmers have no trouble sticking to them as they write code.

However, errare humanum est, and it's still a pain to look through your code to make sure it fits the PEP 8 guidelines. That's what the pycodestyle tool (formerly called pep8) is there for: it can automatically check any Python file you send its way.

$ pycodestyle E302 expected 2 blank lines, found 1
$ echo $?

pycodestyle indicates which lines and columns do not conform to PEP 8 and reports each issue with a code. Violations of MUST statements in the specification are reported as errors — their error codes start with an E. Minor issues are reported as warnings — their error codes start with a W. The three-digit code following the first letter indicates the exact kind of error or warning.

You can tell the general category of an error code at a glance by looking at the hundreds digit: for example, errors starting with E2 indicate issues with whitespace; errors starting with E3 indicate issues with blank lines; and warnings starting with W6 indicate deprecated features being used.

I advise you to consider it and run a PEP 8 validation tool against your source code on a regular basis. An easy way to do this is to integrate it into your continuous integration system: it's a good way to ensure that you continue to respect the PEP 8 guidelines in the long term.

Most open source project enforce PEP 8 conformance through automatic checks. Doing so since the beginning of the project might frustrate newcomers, but it also ensures that the codebase always looks the same in every part of the project. This is very important for a project of any size where there are multiple developers with differing opinions on whitespace ordering. You know what I mean.

It's also possible to ignore certain kinds of errors and warnings by using the --ignore option:

$ pycodestyle --ignore=E3
$ echo $?

This allows you to effectively ignore parts of the PEP 8 specification that you don't want to follow. If you're running pycodestyle on a existing code base, it also allows you to ignore certain kinds of problems so you can focus on fixing issues one category at a time.

If you write C code for Python (e.g. modules), the PEP 7 standard describes the coding style that you should follow.

Other tools also exist that check for actual coding errors rather than style errors. Some notable examples include:

  • pyflakes, which is also extendable via plugins.
  • pylint, which also checks PEP 8 conformance while performing more checks by default. It also can be extended via plugins.

These tools all make use of static analysis — that is, they parse the code and analyze it rather than running it outright.

If you choose to use pyflakes — which I recommend — note that it doesn't check PEP 8 conformance on its own — you would still pycodestyle to do that. That means you need 2 different tools to have a proper coverage.

In order to simplify things, a project named flake8 exists and combines pyflakes and pycodestyle into a single command. It also adds some new fancy features: for example, it can skip checks on lines containing # noqa and is extensible via plugins.

There are a large number of plugins available for flake8 that you can just use. For example, installing flake8-import-order (with pip install flake8-import-order) will extend flake8 so it also checks that your import statements are sorted alphabetically in your source code.

flake8 is now heavily used in most open source projects for code style verification. Some large open source projects even wrote their own plugins, adding checks checks for errors such as odd usage of except, Python 2/3 portability issues, import style, dangerous string formatting, possible localization issues, etc.

If you're starting a new project, I strongly recommend you use one of these tools and rely on it for automatic checking of your code quality and style. If you already have a codebase, a good approach is to run them with most of the warnings disabled and fix issues one category at a time.

While none of these tools may be a perfect fit for your project or your preferences, using flake8 together is a good way to improve the quality of your code and make it more durable. If nothing else, it's a good start toward that goal.

Many text editors, including the famous GNU Emacs and vim, have plugins available (such as Flycheck) that can run tools such as pep8 or flake8 directly in your code buffer, interactively highlighting any part of your code that isn't PEP 8-compliant. This is a handy way to fix most style errors as you write your code.