Supporting Python 2.4 through Python 3.X

This page details my experiences in getting 2 of my scripts/programs to work with all versions of Python from 2.4 through the latest version of Python 3.

There are two different ways to accomplish this — write code that runs natively under all desired Python versions and use two separate code bases—-one for Python 2 and the other for Python 3. There are advantages and disadvantages to both ways, but I'll briefly comment on the second option here.

Maintaining two code bases is far from ideal. It increases the number of places for bugs to emerge and increases the workload for the package maintainer. This is fine if you have relegated the Py2-compatible version to bugfixes-only (or dropped support completely), but it is too inefficient to be of practical use if you want to continue developing both code bases. This is where the use of 2to3 (the automated conversion tool for the most common differences between Py2 and Py3) comes in handy. If you can write code that works under both Py2 and Py3 after conversion, you have only one code base to maintain and you have access to a wider range of language features (although you have to continue writing Python 2 code). To ease the burden on authors of Python software, the distutils python package used to build and distribute Python source bundles the functionality to convert modules and scripts using 2to3 at build time.

One Code to Support Them All

  • Cannot use idiomatic exception catching:
try:
    do_something()
except IndexError as e:
    do_something_with(e)

vs.

try:
    do_something()
except IndexError, e:
    do_something_with(e)

The first is only valid for Py2.6+ and Py3, the second is only valid for Py2. You need to do instead:

import sys
try:
    do_something()
except IndexError:
    e = sys.exc_info()[1]
    do_something_with(e)
  • A lot of things that were strings in Py2 are bytes in Py3, so you need to add decode('ascii') in the relevant locations.

Using 2to3 in distutils

  • Convert all instances of `range` to `xrange` where the generator will be most efficient
  • Items that declare __eq__ no longer declare __hash__ and so cannot be added to sets
  • Fixers do not always apply their fix "correctly" (ex. has_key fix) — you can tell 2to3 to explicitly ignore some fixers.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License