Write a Python 2 to 3 Migration Prompt That Separates Syntax From Semantics
Build a Python 2 to 3 migration prompt that splits mechanical syntax from semantic changes, flags what 2to3 can't fix, and locks a per-file contract.
The reason Python 2 to 3 migrations drag on for years isn't the print statements. Those convert in seconds. It's the str/bytes split, the integer division change, the lazy iterators, the encoding assumptions baked into code that nobody documented. A Python 2 to 3 migration prompt is worth building precisely because it separates the mechanical changes a tool can handle from the semantic ones that quietly corrupt data if you get them wrong.
Search results for this are either the 2to3 tool docs or a single generic "convert this script" prompt. Neither distinguishes safe from dangerous. They mechanically rewrite syntax and leave the semantic landmines for you to step on at runtime.
Why 2to3 finishes 80% and leaves the scary 20%
2to3 and futurize are good at what they do. Print statements, exception syntax, renamed modules. They rewrite the surface and the code parses under Python 3. Then it runs, and a function that concatenated a string and a byte string in Python 2 either throws or, worse, writes garbage to a file.
The stance: the mechanical 80% was never the risk. The risk is the 20% that doesn't error, it just behaves differently. 5 / 2 was 2 in Python 2 and is 2.5 in Python 3. A migration prompt that rewrites syntax and stays silent on semantics is doing the safe part and skipping the part that actually breaks production.
What a Python 2 to 3 migration prompt does
A Python 2 to 3 migration prompt is a triage converter that reads Python 2 source, applies the mechanical changes, and separates them from semantic changes it flags for human review against a locked contract. It tells you what's safe and what isn't.
Run it to:
- Apply mechanical syntax fixes (print, exceptions, renamed imports)
- Flag every
str/bytessite that needs an encoding decision - Mark integer-division changes where behavior shifts
- Identify lazy iterators (
map,filter,dict.keys) that code consumed twice - Note
six/futureshims where dual-version support is wanted - Lock a per-file contract that tags each edit safe or review-needed
That safe-versus-review tag is what turns a risky rewrite into a reviewable one.
Anatomy of the prompt
Variables
{{python2_source}} → the Python 2 file(s) to convert
{{encoding_context}} → how strings/bytes are used (files, network)
{{dual_version}} → whether 2/3 compatibility is needed (six/future)
Prompt
Role: you are a Python 2-to-3 migration engineer.
Task: convert {{python2_source}} to Python 3.
Rules:
- Apply mechanical fixes; tag them safe.
- Flag str/bytes, division, and lazy-iterator changes as semantic.
- Never resolve an encoding ambiguity; route it to review.
Output contract
Return: converted file + per-edit safe/review tag + semantic flags.
1. Give it the encoding context
The str/bytes decision depends on what the code does with the data. Paste the file or network usage into {{encoding_context}} so the model can flag, not guess, the encoding boundary.
2. Decide on dual-version support
Set {{dual_version}} if the code has to run on both Python 2 and 3 for a transition period. That changes whether the model reaches for six shims or clean Python 3.
3. Work the semantic-flag list
The mechanical edits you can mostly trust. The semantic flags, division, encoding, lazy iteration, are your review queue. Each one is a place the behavior could shift.
The trap in a Python 2 to 3 migration is treating every change as equal. Converting print x to print(x) is safe and boring. Converting code that assumed str was bytes is a data-corruption risk that won't error. A migration prompt has to tag the difference, so your review time goes to the encoding decisions and the division changes, not to re-reading print statements.
Prompt-craft patterns for version migration
Tag every edit safe or review-needed. The output contract's most useful field isn't the code, it's the tag. A mechanical fix you can skim; a semantic flag you have to read. Force the model to classify each change.
Never resolve an encoding ambiguity. When the model can't tell whether something should be str or bytes, the right move is to flag it, not to pick. A silent choice here corrupts data. Make the contract forbid silent resolution.
Surface the lazy-iterator gotcha. map and filter return iterators in Python 3, consumable once. Code that iterated a map result twice worked in Python 2 and breaks in Python 3. The prompt should flag double-consumption sites.
Where Claude and GPT-4o diverge on semantics
The models differ most on ambiguity. Claude tends to flag a str/bytes site and state both interpretations rather than committing to one, which is the safe behavior for a data-integrity change. GPT-4o converts faster and will silently pick an interpretation unless the "flag semantic changes, don't resolve them" rule is the last instruction it reads.
Both models reliably handle the mechanical syntax, so the value is entirely in how they treat the dangerous 20%. Integer division is a quiet one: neither model will warn you that / changed meaning unless asked, so the contract has to name it. Restate the semantic-flag rule for GPT-4o on the final line; pin the model version, because a converter that began resolving encoding ambiguity on its own after an update is exactly the kind of change that corrupts a file before anyone notices.
How this fits broader migration work
A Python version upgrade is one transformation among many. The triage discipline, separate what's safe to automate from what needs judgment, applies to any port. For language-to-language work, the cross-language refactor prompt handles idiom preservation; for a staged rollout across services, the framework migration prompt covers reversible steps.
Variables you'll set
| Variable | Required | What it is |
|---|---|---|
{{python2_source}} | Yes | The Python 2 file or files to convert |
{{encoding_context}} | Recommended | How strings and bytes are used |
{{dual_version}} | Optional | Whether 2/3 compatibility is needed |
Getting started
- Copy the structure into ChatGPT, Claude, or Gemini.
- Paste a module into
{{python2_source}}. - Describe the string and byte usage in
{{encoding_context}}. - Run it and read the semantic flags before accepting any edit.
- Resolve each encoding and division flag by hand.
- Run the file under Python 3 and check the lazy-iterator sites.
For a migration spanning many modules with the triage and staging built in, the Cross-Language Refactor Harness runs the contract-locked pass across the codebase.
Browse the migration prompt packs →The Cross-Language Refactor Harness does this end-to-end: it preserves idioms, flags non-translatable constructs, and locks an output contract per file, so a Python 2-to-3 pass separates the mechanical edits from the str/bytes and division changes that need a human instead of resolving them silently. It's part of The Complete AI Prompts Bundle, a one-time lifetime license to the whole catalog plus future packs, worth it if you run more than one migration a year.
If your Python upgrade also touches the database layer, the schema side needs its own safety net. The Database Migration Safety Harness flags destructive operations and stages a reversible rollout. New to buying packs versus rolling your own? Start with how to choose a reusable AI prompt pack.
See the Database Migration Safety Harness →Common questions
Doesn't the 2to3 tool already do this?
What are the dangerous Python 2 to 3 changes?
Which model handles the str/bytes split better?
Get the prompt packs this guide is built on
Ready-to-paste prompts with documented variables and usage guides for ChatGPT, Claude, and Gemini. One-time payment, own it forever.
More prompt guides

A Discovery Call Synthesis Prompt That Grades the Evidence, Not the Vibes
Sales and product teams run dozens of discovery calls a quarter, then synthesize them from memory the night before the roadmap meeting. The loud interview wins. A vivid quote from one account becomes…

A Sales Meeting Follow-Up Prompt That Recaps, Updates the CRM, and Drafts the Email
Ask any rep what happens after a good sales call, and the honest answer is: four tools and twenty minutes. Notes get retyped into a recap. The recap gets mined for action items. Someone updates the Sa…

Agent Prompt vs System Prompt: What Each One Controls
The terms get used interchangeably, and that's where agents go wrong. Agent prompt vs system prompt isn't a style preference. It's the difference between the rules that hold on every single call and t…