All Is Turned to Black
If the title worried you about my mental state, you can relax. It’s about the Python code formatter Black!
Yesterday I finished converting the open source Python projects I maintain to use it.
I’ve followed the project from a distance for a while, and figured now is a my time to try it out. It’s not out of beta yet, but it’s stable and gaining traction:
- My most recent client ev.energy decided to use it on their codebase.
- On Django we recently agreed on Django Enhancement Proposal (DEP) 8, which states Django will convert to Black when it leaves beta.
- It’s an unofficially official Python project, living on the Python GitHub Organization. The original author is Łukasz Langa, the Python 3.8 release manager, whom I saw speak last weekend at PyLondinium.
Is the name some kind of “Black humo(u)r”?
The name confuses some people. It’s a reference to Henry Ford, who quipped about the single colo(u)r option on his Model T:
Any customer can have a car painted any color that he wants so long as it is black.
Hence the project’s logo bears similarity to the Ford motors one:
Black similarly tries to be “one style fits all” with the only configuration option being line length - and you shouldn’t change it! I think this is its success as a code formatter, which should be a tool to prevent bike-shedding.
The Black Code Style
The Black documentation has an extensive guide on the code style it implements. In general, it tries to appease pycodestyle, the checker for PEP 8 compliance. This is great for me, I’ve been running pycodestyle as part of Flake8 on my projects for years.
len(line)
?
Black’s default line length is 88. Normally in Python I’ve found myself working with either a line limit of 80 or 120. The standard library has a limit of 79, based on the 80 plus space for diff markers.
The standard limit of 80 has apparently survived since punch cards and caused flamewars ever since.
The idea behind Black’s 80 characters is to allow up to 10% above the limit, since it’s no longer a hard limit and can be treated “more like a speed limit”. The documentation recommends using the flake8-bugbear extension for Flake8 for line limit checking, so I’ve set that up.
'"' or "'"
?
Black also defaults to double quotes over single quotes. This was a slightly contentious decision.
Personally, I’ve mostly defaulted to typing single quotes over the years, even using flake8-quotes on one project to enforce this. But I’m not bothered - it’s a small thing really, and I’m happy to give up I’ve mostly been using single quotes, which have been
Checklist
My projects are all fairly homogeneous so converting them was a matter of going through the same checklist on each. For an example PR, see my small-ish project pytest-randomly.
The steps were:
- Install Black. I do my linting on Python 3.7 only, so I added it to my requirements file with an environment marker:
black ; python_version == '3.7.*'
. My requirements are set up with pip-tools to compile to multiple Python versions. - Add installation of flake8-bugbear for softer warnings on line limit
- Remove flake8-commas which I was using to ensure trailing commas are added. Black inserts them itself in 90% of cases, but those it doesn’t need fixing, such as issue #274.
- Add a
pyproject.toml
file with black configuration to target Python 3.5. All my projects support Python 3.5+ - I dropped Python 2 support earlier this year, then Python 3.4 as it reached its end of life. - Reconfigure isort and flake8 as per the Black documentation.
- Reformat the code with Black
- Manually fix really long lines that Black can’t reformat. These were basically really long strings that trigger flake8-bugbear’s B950 warning, and were mostly solved by splitting them over multiple lines.
- Add the shiny README badge:
Fin
I consider Black a success for Python. I’ve spent many hours re-styling code to make it more comprehensible, and giving style feedback in code review. I’m looking forward to doing 99% less of that.
Hope this little review helps you learn about Black,
—Adam
Newly updated: my book Boost Your Django DX now covers Django 5.0 and Python 3.12.
One summary email a week, no spam, I pinky promise.