Part of my python FAQ , which is doomed to never befinished.
Maybe you have a Python 2 codebase. Maybe you’d like to make it work with Python 3. Maybe you really wish someone would write a comically long article on how to make thathappen.
I have good news! You’re already readingone.
(And if you’re not sure why you’d want to use Python 3 in the first place, perhaps you’d be interested in the companion article which delves into exactly that question ?)
Making some toughdecisionsWe say “porting from 2 to 3”, but what we usually mean is “porting code from 2 to both 2 and 3”. That ends up being more difficult (and ugly), since rather than writing either 2 or 3, you have to write the common subset of 2 and 3. As nifty as some of the features in 3 are, you can’t actually use any of them if you have to remain compatible with Python2.
The first thing you need to do, then, is decide exactly which versions of Python you’re targeting. For 2, your optionsare:
Python 2.5+is possible, but very difficult, and this post doesn’t really discuss it. Even something as simple as exception handling becomes painful, because the only syntax that works in Python 3 was first introduced in Python 2.6. I wouldn’t recommend doingthis.
Python 2.6+used to be fairly common, and is well-tread ground. However, Python 2.6 reached end-of-life in 2013, and some common libraries have been dropping support for it. If you want to preserve Python 2.6 compatibility for the sake of making a library more widely-available, well, I’d urge you to reconsider. If you want to preserve Python 2.6 compatibility because you’re running a proprietary app on it, you should stop reading this right now and go upgrade to 2.7already.
Python 2.7is the last release of the Python 2 series, but is guaranteed to be supported until at least 2020. The major focus of the release was backporting a lot of minor Python 3 features, making it the best possible target for code that’s meant to run on both 2 and3.
There is, of course, also the choice of dropping Python 2 support , in which case this process will be much easier. Python 2 is still very widely-used, though, so library authors probably won’t want to do this. App authors do have the option, but unless your app is trivial, it’s much easier to maintain Python 2 support during the port ― that way you can port iteratively, and the app will still function on Python 2 in the interim, rather than being a 2/3 hybrid that can’t run oneither.
Most of this post assumes you’re targeting Python 2.7, though there are mentions of 2.6 aswell.
You also have to decide which version of Python 3 totarget.
Python 3.0 and 3.1are forgettable. Python 3 was still stabilizing for its first couple minor versions, and from what I hear, compatibility with both 2.7 and 3.0 is a huge pain. Both versions are also pastend-of-life.
Python 3.2 and 3.3are a common minimum version to target. Python 3.3 reinstated support for u'...' literals (redundant in Python 3, where normal strings are already Unicode), which makes supporting both 2 and 3 much easier. I bundle it with Python 3.2 because the latest version that stable PyPy supports is 3.2, but it also supports u'...' literals. You’ll support the biggest surface area by targeting that, a sort of 3.2. (There’s an alpha PyPy supporting 3.3 , but as of this writing it’s not released as stableyet.)
Python 3.4 and 3.5add shiny new features, but you can only really use them if you’re dropping support for Python 2. Again, I’d suggest targeting Python 2.7 + Python 3.2 first, then dropping the Python 2 support and adding whatever later Python 3 trinkets youwant.
Another consideration is what attitude you want your final code to take. Do you want Python 2 code with enough band-aids that it also works on Python 3, or Python 3 code that’s carefully written so it still works on Python 2? The differences are subtle! Consider code like x = map(a, b) . map returns a list in Python 2, but a lazy iterable in Python 3. Which way do you want to port thiscode?
# Python 2 style: force eager evaluation, even on Python 3 x = list(map(a, b)) # Python 3 style: use lazy evaluation, even on Python 2 try: from future_builtins import map except ImportError: pass x = map(a, b)The answer may depend on which Python you primarily use for development, your target audience, or even case-by-case based on how x isused.
Personally, I’d err on the side of preserving Python 3 semantics and porting them to Python 2 when possible. I’m pretty used to Python 3, though, and you or your team might be thrown for a loop by changing Python 2’sbehavior.
At the very least, prefer if PY2 to if not PY3 . The former stresses that Python 2 is the special case, which is increasingly true going forward. Eventually there’ll be a Python 4, and perhaps even a Python 5, and those future versions will want the “Python 3”behavior.
The good news is that you don’t have to do all of thismanually.
2to3 is a standard library module (since 2.6) that automatically modifies Python 2 source code to change some common Python 2 constructs to the Python 3 equivalent. (It also doubles as a framework for making arbitrary changes to Pythoncode.)
Unfortunately, it ports 2 to 3, not 2 to 2+3. For libraries, it’s possible to rig 2to3 to run automatically on your code just before it’s installed on Python 3, so you can keep writing Python 2 code ― but 2to3 isn’t perfect, and this makes it impossible to develop with your library on Python 3, so Python 3 ends up as a second-class citizen. I wouldn’t recommendit.
The more common approach is to use something like six , a library that wraps many of the runtime differences between 2 and 3, so you can run the same codebase on both 2 and3.
Of course, that still leaves you making the changes yourself. A more recent innovation is the python-future project, which combines both of the above. It has a future library of ren