Standardize your Python

Standardize your Python

A little background: I've been a Python developer for just over 5 years. I started off as a web developer, and have transitioned into an operational data scientist in the past couple of years.

I really enjoy programming in Python. I enjoyed learning it on my own, and teaching some Python-based intro-to-programming classes in my previous life as a High School teacher.

One of the wonderful things about Python is the many different ways that it can be written and used. Just the fact that I was able to transition from web development to data science within the same language is a testament to that fact. Different development styles can be used – just writing scripts meant to be run individually, or a whole suite of modules and classes to be imported and integrated together.

But, one of the frustrating things in working with a team of developers is having to work with other programmer's styles. Here are a few tips for standardizing your team's Python code.

You can follow along in the examples by pulling the code from https://github.com/blackary/standardization

In this post, I want to give a brief introduction to some of the guides and tools which I have found useful for standardizing Python on the teams I've worked on.

PEP 8

The most famous of the PEPs (Python Enhancement Proposals), PEP 8 describes the official style guidelines which should be followed when writing Python code. Even just having everyone on your team read through this document can be a great first step in standardizing your style. If you have disagreements with some parts, and think some pieces will be different, that's OK. Start with PEP 8 and tweak what you want to tweak (or ignore what you want to ignore) as a team, and run with it. Or just use it to start a discussion about how you as a team want to style your code.

flake8

One of the first difficulties in actually applying PEP 8 is that there is a lot to remember. It would be nice to have some automated way to check and see if you're actually following the style rules. Enter flake8.

Flake8 is. a linter, which checks your code for PEP 8 style conventions, and also various common mistakes (like importing a library but not using it), Syntax Errors, etc.

For example, given the following code in bad.py:

a= "way waywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywaywayway too long of a line"

import something_unused

6 = x

y = [1,2, 3,
        4,
5]

Running flake8 bad.py returns the following errors:

$ flake8 bad.py
bad.py:1:2: E225 missing whitespace around operator
bad.py:1:80: E501 line too long (160 > 79 characters)
bad.py:3:1: E402 module level import not at top of file
bad.py:5:1: E999 SyntaxError: cannot assign to literal
bad.py:7:7: E231 missing whitespace after ','
bad.py:8:9: E127 continuation line over-indented for visual indent
bad.py:9:1: E128 continuation line under-indented for visual indent

This can be run manually as above, but is even more useful if it's run automatically. For example, when you commit code to a repository, through a pre-commit hook, or it can be installed as a plug-in on your editor (VS Code, vim, etc.), to add underlines around code which violates styles guides or has a syntax error.

But, what about cases where PEP 8 has multiple valid formatting options? Enter Black.

Black

What should you do when there are disagreements which both fit within the PEP 8 guide? In particular, things like where to place closing brackets or braces, single-vs-double quotes, etc. Black is a Python formatter which is very opinionated, and takes a stand even on cases where PEP 8 leaves several options open.

Here is an example from PEP 8 itself of multiple valid styles:

import some_function_that_takes_arguments


my_list = [
    1, 2, 3,
    4, 5, 6,
    ]
my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    "d", "e", "f",
)

flake8 valid_options.py yields no output, signaling that this is valid code as far as Flake8 is concerned. However, running it through Black shows how it would be reformatted. Just running black {filename} would actually reformat the file in-place, so for demonstration sake, I will use black - which takes code in from standard input and writes to standard output the resulting reformatted result.

$ cat valid_options.py | black -
my_list = [
    1,
    2,
    3,
    4,
    5,
    6,
]
my_list = [
    1,
    2,
    3,
    4,
    5,
    6,
]
result = some_function_that_takes_arguments("a", "b", "c", "d", "e", "f",)
result = some_function_that_takes_arguments("a", "b", "c", "d", "e", "f",)
reformatted -
All done! ✨ 🍰 ✨
1 file reformatted.

Any code that has been formatted by Black should also successfully pass the flake8 test.

Just like flake8, Black can be run as a command-line function, installed as an editor plugin (VS Code, vim, etc.) or through a pre-commit hook.

Conclusion

I hope these tools are helpful for you in creating more consistent code for you and your team.