Skip to content

What Python 2 Linting Tools Still Work in 2026?

I tried to run flake8 on our legacy Jython codebase last week. Got this:

ERROR: flake8 7.0.0 requires Python 3.8+

Right. Python 2 died in 2020. But our codebase didn’t get the memo.

The Reality of Python 2 in 2026

I maintain a Jython-based enterprise system. Migration to Python 3 isn’t happening anytime soon - too many dependencies, too much risk. But I still need code quality checks.

The problem? Most linting tools have moved on.

pip install flake8
# Gets version 7.x - no Python 2 support
pip install pylint
# Gets version 3.x - requires Python 3.7+

What Actually Works

After digging through Reddit threads, documentation archives, and trial-and-error, here’s what I found:

Option 1: Pin to Legacy Versions (If You Still Have Python 2)

If your CI/CD or development environment still runs Python 2.7:

Terminal window
pip install "flake8<4.0" # Last compatible: 3.8.4
pip install "pycodestyle<2.8" # Last compatible: 2.7.0
pip install "pylint<3.0" # Last compatible: 2.15.x

I tested this with Python 2.7.18:

Terminal window
$ python --version
Python 2.7.18
$ pip install "flake8==3.8.4"
Successfully installed flake8-3.8.4
$ flake8 --version
3.8.4 (mccabe: 0.6.1, pycodestyle: 2.6.0, pyflakes: 2.2.0) CPython 2.7.18

Works. But you’re stuck on old tool versions forever.

Option 2: Run Modern Tools from Python 3

This is what I actually do now. Most linting tools can parse Python 2 syntax even when running on Python 3:

Terminal window
# On Python 3.11
python3 -m flake8 --python-version=2.7 legacy_code.py
pylint --py-version=2.7 legacy_code.py

This approach gives you:

  • Modern tool versions (mostly)
  • Bug fixes and improvements
  • Better plugin ecosystem

The Configuration That Works

flake8 Setup

# .flake8 or setup.cfg
[flake8]
max-line-length = 120
exclude = .git,__pycache__,venv
ignore = E501,W503
# Critical: specify Python 2 syntax awareness
python-version = 2.7

pylint Setup

.pylintrc
[MASTER]
py-version=2.7
[MESSAGES CONTROL]
# Disable Python 3-only checks that spam Python 2 code
disable=consider-using-f-string,
use-maxsplit-arg,
redundant-u-string-prefix

What About ruff?

Ruff is fast. Really fast. But:

“Ruff does not support Python 2. Ruff may run on pre-Python 3.7 code, although such versions are not officially supported (e.g., Ruff does not respect type comments).”

I tried it anyway:

Terminal window
$ ruff check legacy_code.py
error: SyntaxError: invalid syntax at line 42

The file had valid Python 2 syntax (old-style print statements). ruff choked.

Verdict: Don’t waste your time. Use flake8 or pylint instead.

Common Pitfalls

Pitfall 1: Mixing Plugin Versions

flake8 plugins must match your Python 2 setup:

Terminal window
# WRONG - latest versions require Python 3
pip install flake8-bugbear
pip install flake8-comprehensions
# RIGHT - pin compatible versions
pip install "flake8-bugbear<22.0"
pip install "flake8-comprehensions<3.0"

Pitfall 2: Forgetting CI/CD

Your local setup works, but CI fails. Here’s a working GitHub Actions config:

name: Lint Python 2 Code
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install linting tools
run: |
pip install "flake8==3.8.4"
pip install "pycodestyle==2.7.0"
pip install "pylint==2.15.0"
- name: Run flake8
run: flake8 legacy/ --python-version=2.7
- name: Run pylint
run: pylint legacy/ --py-version=2.7

Tool Compatibility Reference

+----------------+------------------------+--------------------------+
| Tool | Last Python 2 Version | Notes |
+----------------+------------------------+--------------------------+
| flake8 | 3.8.x (3.8.4) | 4.0+ dropped Python 2 |
| pycodestyle | 2.7.x | 2.8+ requires Python 3 |
| pylint | 2.x (2.15.x) | 3.0+ requires Python 3.7 |
| ruff | None | No Python 2 support |
| pyflakes | 2.x | Works with Python 2 |
| prospector2 | 1.x | Maintained fork |
+----------------+------------------------+--------------------------+

Why Bother?

Why lint Python 2 code at all? It’s dead.

But dead code still runs in production. Our Jython system processes millions of transactions daily. Unlinted code means:

  • Hidden bugs that only surface in production
  • Security vulnerabilities that go unnoticed
  • Technical debt that compounds during eventual migration

Every week I find issues through linting that would have caused production incidents. The 30 minutes I spend running these tools saves hours of debugging.

The Bottom Line

For Python 2 linting in 2026:

  1. Best approach: Run flake8 3.8.x or pylint 2.x from Python 3, targeting Python 2 syntax
  2. Alternative: Pin to legacy versions if you must run on Python 2 interpreter
  3. Don’t use: ruff (no Python 2 support)
  4. For comprehensive analysis: Try prospector2

Pin your versions explicitly. Document why. Add a comment in your requirements file:

# Python 2 compatibility - DO NOT UPGRADE
flake8==3.8.4 # Last version with Python 2.7 support
pycodestyle==2.7.0 # Last version with Python 2.7 support
pylint==2.15.10 # Last version with Python 2.7 support

Your future self (or the next maintainer) will thank you.

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