Skip to content

Which Python Code Formatter Works with Python 2 in 2026?

I tried formatting a Python 2.7 codebase with Black last week and got this error:

Error: cannot format legacy_code.py: Cannot parse: 24:7: print "Hello, World!"

Wait, what? Black can’t handle print statements? I thought Black was the universal Python formatter.

Turns out, I was using Black 24.3.0 - a version that dropped Python 2 support years ago. Let me save you the hours I spent figuring this out.

The Problem

Python 2 reached end-of-life on January 1, 2020. But here we are in 2026, and I’m still maintaining a Jython project. The codebase works, but it’s messy - inconsistent indentation, mixed import styles, and zero formatting standards.

I just wanted to clean up the code. How hard could it be?

+------------------+-------------------------+----------------------+
| Tool | Last Py2 Compatible Ver | Status in 2026 |
+------------------+-------------------------+----------------------+
| Black | 21.12b0 | Removed in 22.1.0 |
| autopep8 | < 1.6 | Gradual deprecation |
| yapf | Current | Still supported |
| isort | Current | Can format Py2 code |
| ruff | Current | ~99% compatible |
+------------------+-------------------------+----------------------+

Trial and Error with Black

First attempt - the obvious choice:

Terminal window
pip install black
black --target-version py27 legacy_code.py

Result: Failed on Python 2 syntax. The --target-version flag exists, but Black’s parser can’t handle Python 2 code anymore.

Second attempt - pin an older version:

Terminal window
pip install "black<22.0"
black --target-version py27 legacy_code.py

This worked! Black 21.12b0 successfully formatted the file.

But here’s the catch - Black 21.x requires Python 3.6+ to run. So you need a Python 3 environment to format Python 2 code. Not ideal for CI/CD on a Python 2-only system.

The yapf Alternative

Since Black had limitations, I tried yapf (Yet Another Python Formatter):

Terminal window
pip install yapf
yapf --in-place legacy_code.py

This worked immediately. No version pinning, no Python 3 requirement for the runtime. yapf explicitly supports Python 2.7 and 3.4+.

# .style.yapf - custom configuration
[style]
based_on_style = pep8
indent_width = 4
column_limit = 79

yapf gave me more control. I could choose PEP 8 style, Google style, or customize everything. For a legacy codebase, this flexibility matters - you can match existing code conventions instead of forcing a new style.

autopep8 for PEP 8 Compliance

Next, I tested autopep8:

Terminal window
pip install "autopep8<1.6"
autopep8 --in-place --aggressive --aggressive legacy_code.py

The --aggressive --aggressive flag enables level 2 fixes. autopep8 uses pycodestyle under the hood and fixes most PEP 8 violations.

One thing I noticed - autopep8 is less opinionated than Black. It follows PEP 8 rules rather than making arbitrary decisions about code style. For legacy code, this is often better since it respects existing conventions.

Import Sorting with isort

My codebase had messy imports:

import os,sys,re
from mymodule import something
import json

isort cleaned this up:

Terminal window
pip install isort
isort --py 27 legacy_code.py

The --py 27 flag is crucial. Without it, isort might add Python 3-only imports like from __future__ import annotations.

Result:

import json
import os
import re
import sys
from mymodule import something

isort runs on Python 3 while formatting Python 2 code - perfect for modern CI/CD pipelines.

The ruff Edge Case

Ruff is the new hot formatter - written in Rust, 30x faster than Black. I had to try it:

Terminal window
pip install ruff
ruff format legacy_code.py

99% of the time, it works perfectly. But I hit this edge case:

# ruff formatted this:
func(
arg1,
arg2,
**kwargs, # Trailing comma
)
# Python 2 syntax error:
SyntaxError: invalid syntax

In Python 2, trailing commas after **kwargs are invalid. Ruff adds them automatically.

The fix is simple - manually remove the trailing comma:

func(
arg1,
arg2,
**kwargs # No trailing comma
)

For large codebases, this 1% edge case means you’ll need manual review after running ruff.

What I Learned

After testing all these tools on my Jython project, here’s what works:

For Black-style formatting:

Terminal window
pip install "black<22.0"
black --target-version py27 legacy_code.py

For maximum compatibility:

Terminal window
pip install yapf
yapf --in-place legacy_code.py

For import sorting:

Terminal window
pip install isort
isort --py 27 legacy_code.py

Complete pre-commit setup for Python 2:

.pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: isort-py2
name: isort (Python 2)
entry: isort --py 27
language: system
types: [python]
- id: yapf-py2
name: yapf
entry: yapf --in-place
language: system
types: [python]

Why This Still Matters in 2026

You might ask - why care about Python 2 formatting in 2026?

Legacy systems don’t disappear overnight. Jython enables Python on the JVM, and many enterprises still run Java-based systems where Jython is embedded. Migration timelines extend years beyond initial estimates.

Formatted code is easier to maintain. Consistent style reduces cognitive load. Automated formatting prevents team debates about spacing and quotes.

The tools exist. You just need the right versions.

My Recommendation

For Python 2 projects in 2026:

  • Use yapf - most reliable, still officially supports Python 2.7
  • Use isort with --py 27 - for import sorting
  • Avoid Black 22+ - Python 2 support was intentionally removed
  • Be careful with ruff - check for trailing commas after **kwargs

The exact versions matter. Pin them in your requirements.txt:

black<22.0
autopep8<1.6
yapf
isort
ruff

And in your pyproject.toml:

[tool.black]
line-length = 88
target-version = ['py27']
required-version = "21.12b0"
[tool.isort]
py_version = 27
profile = "black"
line_length = 88

Now I can finally clean up that legacy codebase without syntax errors.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments