Migrate every remaining Python 2 service to Python 3.11.
For each repository:
1. Apply the mechanical Python 2→3 fixers across the source tree:
- `print` statements → `print()` calls
- `xrange` → `range`, `iteritems`/`itervalues`/`iterkeys` → `items`/`values`/`keys`
- `unicode` literals and `basestring` removal
- `from __future__ import` imports cleaned up
- integer division (`/`) audited; use `//` where the code relied on truncation
2. Replace EOL Python 2-only dependencies with Python 3 equivalents:
- `urllib2` → `urllib.request`
- `ConfigParser` → `configparser`
- `cPickle` → `pickle`
- `mock` (standalone) → `unittest.mock`
3. Update build config:
- `setup.py` / `setup.cfg` / `pyproject.toml`: set `python_requires=">=3.11"`
- `tox.ini`: `envlist = py311`
- Dockerfiles: `FROM python:3.11-slim`
- `.github/workflows/*.yml`: `python-version: '3.11'`
4. Convert string handling, `str`/`bytes` mixing is the most common runtime break.
Audit every file I/O, network I/O, and subprocess call site for explicit encoding. The Problem
Python 2 reached EOL in January 2020, but most organizations with a Python footprint still have a long tail of services that never got migrated, typically the oldest, least-loved, business-critical ones nobody wants to touch. They run on an unsupported runtime, can't accept current versions of any popular library, and accumulate a security-vulnerability backlog every quarter the migration is deferred.
The mechanical fixes (`2to3`, `six` removal, `print` statements) are the easy part. The real risk is the `str`/`bytes` boundary: every file read, network call, and subprocess invocation that worked under Python 2's implicit ASCII coercion can break at runtime under Python 3's strict separation. Tests rarely cover the encoding edge cases, and the failures surface in production, usually months later, when the migrated service finally hits non-ASCII input. Coordinating this across dozens of services, with each one needing its own dependency audit and test pass, is the kind of work that stalls for years.
What Tidra Does
- Applies the standard Python 2→3 mechanical fixers across the source tree,
printstatements,xrange, dict iterator methods,unicode/basestringremoval,__future__cleanup, division operator audits - Replaces Python 2-only stdlib usage with Python 3 equivalents (
urllib2→urllib.request,ConfigParser→configparser,cPickle→pickle) - Audits I/O boundaries for
str/bytesmixing and adds explicitencoding=arguments toopen(), subprocess, and network calls - Updates
setup.py/pyproject.toml/tox.ini/ Dockerfiles / CI workflows to declare and use the target Python version (3.11 by default) - Replaces EOL Python 2-only third-party packages with maintained Python 3 alternatives where the mapping is clear
- Opens a per-repo PR with a triage section listing places that need human judgement (e.g., subprocess pipes with binary data, custom string-encoding helpers)
Before & After
Customization Tips
- Target Python version: 3.11 is a reasonable default for active LTS-ish use; pick 3.12 if you have no C-extension constraints, or 3.10 if a critical dependency lags.
- Six removal: Decide whether to strip
sixentirely (cleaner) or leave it as a transitional shim (faster). Leaving it in place keeps diffs smaller but defers cleanup. - Subprocess and binary I/O: Code that pipes binary data through subprocess or sockets is the highest-risk surface. Flag these files for manual review rather than auto-patching.
- Test coverage gate: Run the existing test suite under Python 3 before merging; aim for parity with the Python 2 pass rate. Failures here usually surface real encoding bugs.