VVersions.dev

Upgrade to Python 3

Moving to Python 3 (targeting 3.12) ranges from routine to high-risk. The hard parts are the unicode/bytes split, print becoming a function, true division, and removed modules like distutils — not day-to-day logic.

Version upgradeDifficulty: hardEffort: 1 day to several weeks depending on Python 2 usagehigh risk

Last verified · Updated May 22, 2026

Targeting Python 3.12 is straightforward for code already on a recent Python 3, and a substantial project for anything still on Python 2.7. Most breakage concentrates in the unicode/bytes split, print as a function, true division, and removed modules (distutils, imp) — inspect for those first.

Who should upgrade

  • Anything still on Python 2.7 — it is end-of-life and receives no security fixes.
  • Projects on an EOL 3.x (e.g. 3.8) that need supported runtimes and faster CPython.
  • Teams wanting modern syntax: f-strings, the walrus operator, and structural pattern matching.

Who should wait

  • Apps blocked by a C-extension dependency without a wheel for the target interpreter.
  • Services pinned to a framework that has not yet released a Python 3.12-compatible version.

What changed (2 to 3)

  • print is a function: print(x) instead of print x.
  • True division: 5 / 2 == 2.5; use // for integer division.
  • Strict unicode/bytes split — text is str, binary is bytes, no implicit coercion.
  • Exception syntax: except Foo as e instead of except Foo, e.
  • dict.iteritems/iterkeys/itervalues and xrange removed; use items()/range().
  • from future imports, 2to3, futurize, and six bridge 2-and-3 codebases.

⚠ The unicode/bytes split is where data corruption hides

Python 2 silently coerced str and unicode; Python 3 does not. Audit every file, socket, and subprocess boundary and decide encoding explicitly, or non-ASCII input will raise (or worse, corrupt) at runtime.

Automated first pass with futurize

# 1. Create a fresh venv on the target interpreter
python3.12 -m venv .venv && . .venv/bin/activate

# 2. Run an automated pass and review the diff
python -m pip install future
futurize --stage1 -w .   # or: 2to3 -w .

# 3. Reinstall and verify
pip install -e . && pip check && python -m pytest -q

Compatibility matrix

ConcernPython 2.7Python 3.12
printStatementFunction
5 / 22 (integer division)2.5 (true division)
str typebytesunicode text
distutilsAvailableRemoved — use setuptools/pyproject
dict.iteritemsAvailableRemoved — use items()

AI-assisted migration workflow

Upgrade this project to Python 3.12. First inspect for Python 2-only syntax (print statements, except Foo, e:, iteritems, xrange) and removed modules (distutils, imp, collections ABC aliases). Then create a fresh venv, run a futurize/2to3 pass and review the diff, fix unicode/bytes boundaries explicitly, replace removed modules, reinstall dependencies, and resolve pip resolver conflicts. Run pip check and the tests after each step and report before continuing.

Safety: Incremental edits only. Pause for review after the automated pass and after each module replacement.

PR review checklist

  • Project runs on the target interpreter in a clean venv
  • No print statements, iteritems/iterkeys, xrange, or except Foo, e: remain
  • No imports of distutils or imp
  • Text/bytes boundaries encode and decode explicitly
  • pip check passes and no new DeprecationWarnings appear in tests

Rollback strategy

  • Keep the venv recreation, automated pass, and manual fixes in separate commits.
  • Pin the prior interpreter and dependency set so the old environment can be rebuilt.
  • Hold the upgrade behind a branch until the full test suite is green on the target version.

Common errors

  • TypeError: a bytes-like object is required — a bytes/str boundary needs an explicit encode/decode.
  • ModuleNotFoundError: No module named 'distutils' — migrate packaging to setuptools/pyproject.toml.
  • ResolutionImpossible from pip — see the fix-python-dependency-errors workflow.

Official sources

Copy-ready AI prompts

Structured prompts for an AI coding assistant. Inspect first, then execute incrementally, and keep a human in the review loop.

Repo inspection: Repo inspection prompt

You are helping migrate a Python codebase from Python 2 to Python 3.

Do not edit files yet. First inspect the repository and report:
1. The exact Python version requirement in setup.py / pyproject.toml / tox.ini / CI config, and the interpreter the project runs on.
2. Python 2-only syntax: print statements, integer-division reliance, except Foo, e: syntax, dict.iteritems/iterkeys/itervalues, xrange, and unicode/str/bytes confusion.
3. Removed/deprecated module usage: distutils, imp, and deprecated collections ABC aliases (collections.Mapping etc.).
4. The dependency manifest (requirements.txt vs lock vs pyproject), any C-extension builds, and pinned versions.
5. The test runner, virtualenv/venv setup, and the install/build/test commands.

Return: a migration risk summary, the files most likely to break, a suggested migration order, the commands to run before editing, and any questions that need human confirmation.

Safety: Inspection only. The agent must not modify files in this step.

Works with Claude Code, Cursor, GitHub Copilot.

Migration execution: Migration execution prompt

Migrate this codebase from Python 2 to Python 3, one concern at a time.

Work in this order and pause for review after each: (1) create and activate a fresh virtualenv/venv on the target interpreter, (2) run an automated pass with python -m future.utilities futurize (or 2to3) and review the diff, (3) replace removed/deprecated modules (distutils -> setuptools/pyproject, imp -> importlib, collections ABC aliases), (4) fix unicode/bytes boundaries explicitly, (5) reinstall dependencies and resolve pip resolver conflicts.

After each step run pip check and the project's lint and tests, and report results before continuing. Do not refactor unrelated code.

Safety: Apply changes incrementally and keep each step reviewable. Never bundle unrelated refactors.

Works with Claude Code, Cursor, GitHub Copilot.

Test plan

Commands

  • python -m venv .venv && . .venv/bin/activate
  • pip install -e .
  • pip check
  • python -m pytest -q

Manual checks

  • Text/bytes: confirm file and network I/O encode/decode explicitly and round-trips.
  • Division: verify code that relied on Python 2 integer division still produces expected results.
  • C extensions: confirm any compiled dependencies build against the target interpreter.

Regression risks

  • Silent behavior change from true division replacing integer division.
  • Bytes/str mix-ups surfacing only at runtime on non-ASCII input.
  • C-extension dependencies without wheels for the target interpreter.

Acceptance criteria

  • pip check reports no broken requirements and pytest passes on the target version.
  • No imports of distutils, imp, or deprecated collections ABC aliases remain.
  • All dependencies resolve to versions supporting the target Python.

Frequently asked questions

Is the Python 2 to 3 upgrade a hard migration?

For a meaningful 2.7 codebase, yes — the unicode/bytes split and removed APIs make it a real project. 2to3 and futurize automate the mechanical parts, but the text/binary boundaries require human judgement.

Should I target the latest Python or an LTS?

Python has no LTS; target the latest stable supported release (3.12 here) so you get security fixes and the faster CPython, and avoid landing on an already-EOL 3.x like 3.8.