3to2 by hand – back porting python 3 to python 2

As mentioned in an earlier post, I’m currently writing a music streaming server in python. As I wanted to go with the newest thing available, I wrote it in python 3. Unfortunately the application server we rely on, cherrypy, is only packaged for python 2 in most distributions! Even worse, even if the packages were installed for python 3, it would not run, since I relied on python 3.3 features.

Since this keeps my program from being used in the world, I decided to backport it to python 2. For me it was very important, that the code would not get any uglier by doing so, so I started writing a replacement module.

Here’s a collection of useful code snippets to help you making your software python 2 / 3 compatible.

Let’s jump in the code. You can check which version is running like so:

checking the version

As you see, I’m running python 3.3 on my laptop. Fortunately you can compare the version_info object with tuples, so you can easily check if a version is below or above:

But watch out, you can not check for equality in this way.

Well, let’s go to the nice, ugly or hackish gems I have prepared.

setting unicode strings as standard

works in python 2.7+

This is the most important change between python 2 and 3. In python 2 stings are just byte arrays. Python 2 relied on those strings being ASCII encoded. It supplied a special type unicode that could contain characters outside the ASCII range. This option enables, that all strings are understood as unicode objects.

Do you see the u in front of the string? That is how unicode strings are represented in python 2. This means, that all your static string variables are now in unicode!

type() checking strings

Since all strings in python 3 are unicode strings, there is no need anymore for the unicode object. But here comes the tricky part: In python 2, all our strings are now unicode objects, but there is no such object in python 3. So how can we now check for type equality in both versions?

Simple! In python 2:

In python 3:

You might say, »that is neat, but why should I care«? And I’m telling you, because you will encounter 8bit strings in your python 2 version. And this is how you can handle them:

Converting strings to unicode

Adding this empty unicode string to your string, will cast the other stirng automatically to a u’string’! The great thing about it is, that this will not change your strings in python 3, since all strings are of the class str and will therefore just be appended with the empty string. So if you’re now sure about the encoding of the string, and don’t know if it’s unicode or not, you can simply add the empty string.

You can also put this inside a function that will convert all your strings:

If you’re program doesn’t handle many strings, or performance is not your thing, then you can call this method each time you use a string.

print

You can import the python 3 printing function into python 2 as well. This works for python 2.6+

This will give you all the same function arguments as in python 3, e.g. the end argument, which can be very handy. Since python 2 also natively supports the print(‘foo’) syntax (with the strings in parenthesis), there should be nothing else to change for backwards compability.

input and raw_input

The input statement has changed in python 3; input used to be something like the exec statement, and has changed to just receive user input as strings. You can just override the input method with the raw_input statement.

I don’t know if there are any other side effects by doing so, but it worked well enough for me.

argparse and optparse

Python 3.3 introduced a new very handy module called argparse. The main difference between optparse and argparse is, that argparse supports also switches with multiple inputs. So be aware that the code below might break your command line interface. It should however work for the most part.

Enjoy this code with caution.

configparser:

The config parse module didn’t change to much, so all you need to do is to change the letter casing:

Code encoding:

Since you might have unicode literals in your code, make sure, that you set the encoding in your source files!

 Some more info I found very helpful:

Supporting Python 2 and 3 without 2to3 conversion

Update:

Just found this piece of code inside cherrypy, pure gold.

_cpcompat.py in cherrypy

Update 2

This will handle all your calculations as you expect them to (at least from a python 3 point of view)

 

 

 

Happy porting!

 

Leave a Reply

Your email address will not be published. Required fields are marked *