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:
you should import the specific class / function / variable conditionally like this:
In the following you will find snippets for commonly used packages / modules.
queue
urllib
urllib2
xmlrpc
pickle
For pickle you usually use the c implementation in Python 2. In Python 3 there is not differentiation necessary anymore:
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.
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):