While at the time of writing (May 2019) the death of Python 2 is just around the corner since I’m working in an enterprise environment I’m sure I will be dealing with Python 2.x code for some time to come. To that end here are some gotchas to keep in mind.

Missing Features

In the 10+ years since Python 3’s release it’s not surprising that it has accumlated a large list of features that simply don’t exist in Python 2.x

No enum module

The enum module was added in Python 3.4. In order to make use of this module in versions of Python older than this we can install the enum34 package from PyPi.

$ pip install enum34

Code that uses the standard enum module should now work as is.

Note: It would seem that this package is not actively maintained with the most recent change being in 2016 at the time of writing.

No pathlib module

The pathlib module was added in Python 3.4. In order to make use of this module in older versions of Python we can install the pathlib2 package on PyPi

$ pip install pathlib2

Then code written for the standard pathlib module should work as expected.

No FileNotFoundErrors

Not sure when these were added. If you simply want to be able to throw one it’s easy enough to backport one yourself

class FileNotFoundError(OSError):
    pass

Moved / Changed Features

There are a number of features that did exist in Python 2.x but were renamed/moved or tweaked in someway in Python 3.x so that they are not directly compatible with older interpreters anymore

configparser is ConfigParser

Before Python 3 the configparser module was called ConfigParser, I’m struggling to see if there are any functional differences between the two versions but there is a actively maintained configparser package available which has apparently backported a number of changes to older versions of Python

$ pip install configparser

In theory code using the standard configparser module will “just work”.

Best Practice

Here is a miscellaneous collection things to do when writing code that will run under Python 2.x that should hopefully minimise the amount of surprises!

Use new style classes

In Python 2.x new style classes are an explicit opt-in

class MyClass(object):
    ...

Define both __eq__ and __ne__

When creating a class that you wish to define equality for you need to ensure that you define both the __eq__ and __ne__ special methods for your class to have the behavior you would expect it to.

In Python 2.x the default implementation of __ne__ is something like the following.

def __ne__(self, other):
    return not (self is other)

For your class to behave as expected you will need to define it as follows

class MyClass(object):
    ...

    def __eq__(self, other):
        ...

    def __ne__(self, other):
        return not self.__eq__(other)

str != str

In Python 3 the string related datatypes were overhauled to include built-in support for unicode character encodings. This means that the str type in Python 2.x is not equivalent to the str type in Python 3.x

So if you want to check that some value is an instance of the type str the best way is to make use the six compatibility package

$ pip install six

And to use the following code

import six

if isinstance(value, six.string_types):
    ...