How to make your Python code compatible with version 2.7 and 3.3 (and higher)
While the current ROS distributions are using Python 2 we are striving to support Python 2.7 as well as Python 3.3 (and higher). Especially since some platforms other than Ubuntu are already using Python 3 only.
On this page you will find several tips and common code snippets how to make your Python code compatible with version 2.7 as well as 3.3.
Contents
Common rules
Print is now a function
Whenever calling print the arguments must be enclosed in parenthesis, e.g.:
Whenever you pass multiple arguments to print you must use the future import at the very beginning of your file (in order to not change the behavior):
Also whenever you print to a specific file-like object you must use the future import at the very beginning of your file and pass the file-like object as a keyword argument to the print function:
Strings and encodings / unicode
In Python 2 bytes and strings are not treated differently and can be used with all APIs. It is completely up to the developer to take care to *decode* (from bytes to strings) and *encode* (from strings to binary) them correctly.
In Python 3 these kind of strings have different types: bytes and str. Most of the API is type specific, e.g. a file opened in binary mode will return bytes on read().
E.g. in Python 3 the subprocess module will return the output of executed programs as binary which usually needs to be decoded explicitly.
For more information on unicode please see http://docs.python.org/3.3/howto/unicode.html.
Relative imports
Relative imports must start with a . (dot), e.g.:
1 from .my_sibling_module import MyClass
In general absolute imports are recommended (see https://www.python.org/dev/peps/pep-0008/#imports).
Integer division
While integer division resulted in an int before in Python 3 the result is actually a float. So if the rounding was intentional you usually want to cast it explicitly, e.g.:
You can also use the following approach:
Octal numbers
Octal numbers must contain an o between the leading zero and the rest of the number, e.g.:
1 my_number = 0o1234
Various imports
Commonly the following snippets will try to import the new Python 3 style packages / modules / classes and fall back to the Python 2 version.
Whenever you use a class / function / variable like this:
1 import package
2
3 package.my_name(...)
you should import the specific class / function / variable conditionally like this:
In the following you will find snippets for commonly used packages / modules.
queue
1 try:
2 from queue import Queue
3 except ImportError:
4 from Queue import Queue
urllib
1 try:
2 from urllib.parse import urlparse
3 except ImportError:
4 from urlparse import urlparse
1 try:
2 from urllib.request import urlretrieve
3 except ImportError:
4 from urllib import urlretrieve
urllib2
1 try:
2 from urllib.request import urlopen
3 except ImportError:
4 from urllib2 import urlopen
xmlrpc
1 try:
2 from xmlrpc.client import ServerProxy
3 except ImportError:
4 from xmlrpclib import ServerProxy
pickle
For pickle you usually use the c implementation in Python 2. In Python 3 there is not differentiation necessary anymore:
1 try:
2 import cPickle as pickle
3 except ImportError:
4 import pickle
StringIO
For StringIO the conditional import should first check for the c implementation in Python 2 and then fall back to the Python 3 module. This is recommended since Python 2 does have an io module but its implementation of StringIO is subtle different.
1 try:
2 from cStringIO import StringIO
3 except ImportError:
4 from io import StringIO
Functions
execfile()
unichr()
xrange()
zip()
zip() does not return a list but an iterable in Python 3. Therefore you can not use index based access on this anymore. If you need index based access you should wrap the invocation in list(..).
The same applies to others functions like map, range etc.
Methods
dict.iter*()
Since keys() and values() does not return a plain list but a iterable from which you can not access specific indices directly you must explicitly convert the result to a list:
dict.has_key()
iter.next()
Types
string
To test if a variable is a normal string (rather than a unicode string):