Tooling a Python Project: Part 2, Optional Tools

In Part 1 we took a look at the basic tools necessary for any Python project.  Now we’ll look at a couple of tools that add additional structure and can help make your development experience as smooth as possible.

Black

The proper way to format code is an old argument among programmers (see the infamous tabs vs spaces argument for the canonical example).  Auto-formatters (such as the go fmt command) resolve this by automatically enforcing a set of style standards.  At Creative Mines, our favorite auto-formatter for Python is Black.

  • The default styling looks great
  • There are editor integrations for every popular editor
  • It isn’t configurable

That last bullet point may surprise some, but in my opinion this is the killer feature of Black.  Since there are only a few configuration options, not only do you short circuit developer arguments about code formatting, you also short circuit arguments about how to tune the code formatter.  Notably, if Black produces something really ugly you can always just exclude that file with --exclude.

For bonus points integrate Black into your CLI by running black --check on your codebase on every checkin.  This will prevent anyone from checking in improperly formatted code.  Black also works well as a precommit hook.

NOTE: If you are also using Flake8 with Black, make sure to ignore W503 and E501.  Black will occasionally violate these rules with its default formatting.

flake8 --ignore E501,W503

mypy

Mypy is an optional type checker for Python.  Since Python doesn’t have static typing, you can often end up with obscure runtime errors resulting from passing in the wrong type of data or object.  Mypy eliminates these errors and lets you lean on the type system the same way you would in compiled languages like Java, Go, etc.  Here’s an example from mypy’s homepage of what the static typing annotations look like:

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a+b

A good rule of thumb I've heard for using mypy is to start using it "when you would get value out of unit testing".  Small scripts in a single file will benefit far less than large projects being built out by a team of programmers.

You can get started with mypy by installing it with pip install mypy.  Mypy can be used on each individual file, but if you're running mypy in CI, you'll probably want to run it on the entire package with mypy -p package_name.

Comprehensive information about mypy can be found in the documentation.