Painting your python code Black

Recently, thanks to my colleague, I have learned about the Black formatter. After watching the PyCon 2019 talk I decided I want to use it in my projects. The greatest thing about Black is that it doesn’t matter how bad your code formatting is, Black will fix it for you (and in this blog post I will also show how to enforce it on your teammates). Let’s get started!

Example of what Black can do

Before Black:

x = {  'a':37,'b':42,

'c':927}
if very_long_variable_name is not None and \
 very_long_variable_name.field > 0 or \
 very_long_variable_name.is_debug:
 z = 'hello '+'world'
else:
 world = 'world'
 a = 'hello {}'.format(world)
 f = rf'hello {world}'
if (this
and that): y = 'hello ''world'#FIXME: https://github.com/python/black/issues/26

After Black:

x = {"a": 37, "b": 42, "c": 927}
if (
    very_long_variable_name is not None
    and very_long_variable_name.field > 0
    or very_long_variable_name.is_debug
):
    z = "hello " + "world"
else:
    world = "world"
    a = "hello {}".format(world)
    f = rf"hello {world}"
if this and that:
    y = "hello " "world"  # FIXME: https://github.com/python/black/issues/26

This sadly doesn’t work with Jupyter Notebooks at the time of the post. When I tried running Black on an .ipynb file, it just broke. Tough luck.

Installation and basic usage

Installing black is pretty straightforward:

pip install black

One thing that is important to know about Black, is that it is uncompromising. That’s why we shouldn’t do any configurations, unless we really need to do it (and those would be edge cases or tool crossfire). So to make our Black formatter running we just need to run one of the following commands:

# Format a specific file
black your_python_file.py

# Format a specific directory
black your_python_directory/

# Go crazy and format everything!
black .

Black can also be used as a Python package, if that’s what you preffer:

python -m black {source_file_or_directory}

To run Black with more options, check out the command line documentation.

Github Actions and CI

So when we have our precious project running and we decide on using Black, we need to keep an eye that EVERYONE is embracing it to the fullest. We will use Github Actions to FAIL everyone who forgets to format their code.

An example Github Actions workspace could look like the one below. It is roughly taken from my Django project iot-home that you can preview here. The project uses docker-compose for various build and test tasks. Black package is a part of the testing requirements and is run together with pytest, coverage and other useful things. The important command here that checks whether you complied to Black standards is:

black . --check

And here used together with docker-compose:

name: Lint

on: [push]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Check Black code quality
        run: docker-compose -f iothome/docker-compose.test.yml run test black . --check

So when someone forgets to run Black they can get:

Oh no! 💥 💔 💥
15 files would be reformatted, 13 files would be left unchanged.
##[error]Process completed with exit code 1.

And when you actually embrace Black you will get:

All done! ✨ 🍰 ✨
28 files would be left unchanged.

Tell me what you think!