Supporting both 2.x and 3.x in one code base [was Re: Python #ifdef]
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Tue May 28 20:57:10 EDT 2013
On Tue, 28 May 2013 18:25:59 -0400, Joel Goldstick wrote:
> I wonder
> if it is a good idea in general to try to write code like this. --
> combined 2.x/3.x codebase can be a bear to maintain.
Not so much a bear as a tiny little kitten.
> I wouldn't do it
> unless there was some imposing reason that I must. Its not just print()
> -- that isn't bad, but changes in module names (urllib), arithmetic, and
> unicode especially make this idea in general, very tricky. Pity the
> next developer who needs to try to maintain it.
It's not that hard really. Well, like all things, it depends on the
circumstances. If you're reliant on external modules, you *may* have a
bad time if those modules are 2.x only or 3.x only, but the standard
library and built-ins don't provide too much of a challenge.
There's no doubt that supporting 2.x and 3.x in one code base is more
difficult that just supporting one or the other, but the difficulty is
much less than often supposed. Python 2.7, and to a lesser extent, 2.6,
are designed to be as easy to port to 3.x as possible, which has the
happy side-effect that they are also relatively easy to write code for
them that will also run under 3.x.
Many of the differences can be eliminated with a few __future__ imports:
from __future__ import division, print_function
Differences in behaviour of the built-ins can be eliminated:
from future_builtins import *
Built-ins such as reduce and cmp that have been moved, or eliminated, can
easily be restored:
if sys.version >= '3':
from functools import reduce
Or if you prefer a "Better To Ask Forgiveness Than Permission" approach:
try:
reduce
except NameError:
from functools import reduce
Name changes of modules are easy to deal with:
try:
import configparser
except ImportError:
import ConfigParser as configparser
The most difficult difference is the difference between strings in 2.x
and 3.x, but if you drop support for Python 3.1 and 3.2, you can write
code that works in both 2.7 and 3.3 by using the u"" syntax. Or just use
ASCII literals, which work perfectly in both.
There are really only a very few things that cannot be shared between 2.x
and 3.x: syntactical features that are only supported by 3.x. So if
you're planning on writing code that runs in both 2.x and 3.x, you need
to eschew the 3-only features like keyword-only function arguments and
function annotations.
But that's no different than writing code to support *any* two versions
that don't have identical syntax. E.g. 2.4 and 2.5: 2.5 supports ternary
if, `a if condition else b`, while 2.4 does not, so if you need to
support both, you can't use ternary if. Nearly all the code I write is
for 2.4 or better, and I can assure you that the hardest part is
supporting 2.4. Adding 3.x doesn't make it much harder.
(My hat goes off to those supporting 2.3 through 3.3 in one code base.
That is, frankly, astonishing.)
Nobody *likes* to have to support really old versions missing the cool
syntax that you want to use, but nobody says that you should even try.
3.x doesn't change that.
--
Steven
More information about the Python-list
mailing list