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:
pip install "flake8<4.0" # Last compatible: 3.8.4pip install "pycodestyle<2.8" # Last compatible: 2.7.0pip install "pylint<3.0" # Last compatible: 2.15.xI tested this with Python 2.7.18:
$ python --versionPython 2.7.18
$ pip install "flake8==3.8.4"Successfully installed flake8-3.8.4
$ flake8 --version3.8.4 (mccabe: 0.6.1, pycodestyle: 2.6.0, pyflakes: 2.2.0) CPython 2.7.18Works. 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:
# On Python 3.11python3 -m flake8 --python-version=2.7 legacy_code.pypylint --py-version=2.7 legacy_code.pyThis 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 = 120exclude = .git,__pycache__,venvignore = E501,W503# Critical: specify Python 2 syntax awarenesspython-version = 2.7pylint Setup
[MASTER]py-version=2.7
[MESSAGES CONTROL]# Disable Python 3-only checks that spam Python 2 codedisable=consider-using-f-string, use-maxsplit-arg, redundant-u-string-prefixWhat 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:
$ ruff check legacy_code.pyerror: SyntaxError: invalid syntax at line 42The 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:
# WRONG - latest versions require Python 3pip install flake8-bugbearpip install flake8-comprehensions
# RIGHT - pin compatible versionspip 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.7Tool 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:
- Best approach: Run flake8 3.8.x or pylint 2.x from Python 3, targeting Python 2 syntax
- Alternative: Pin to legacy versions if you must run on Python 2 interpreter
- Don’t use: ruff (no Python 2 support)
- For comprehensive analysis: Try prospector2
Pin your versions explicitly. Document why. Add a comment in your requirements file:
# Python 2 compatibility - DO NOT UPGRADEflake8==3.8.4 # Last version with Python 2.7 supportpycodestyle==2.7.0 # Last version with Python 2.7 supportpylint==2.15.10 # Last version with Python 2.7 supportYour 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:
- 👨💻 flake8 documentation
- 👨💻 pylint documentation
- 👨💻 Python 2.7 sunset
- 👨💻 ruff - Rust-based Python linter
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments