Migration from OMERO 5.5 (Python 2) to OMERO 5.6 (Python 3)

This page serves as a collection of recommendations, developed as the OME team went through the migration to Python 3. This is not a complete guide but may serve as a useful starting point.

For more information, please see a dedicated Python 3 page like http://python-future.org/.

Futurize

Installing future from Python 3 is now required for all OMERO Python components. This library comes with the futurize tool which performs many of the basic transformations needed to migrate Python 2 code to Python 3:

futurize -0 your_file.py

Add -w to update the file in place.

print()

The most common transformation needed is adding parentheses around print statements since print is no longer a keyword.

dict.keys()

The return value from the keys() method of dictionaries is of type dict_keys and no longer has methods like sort(). Wrap with a call to list if you need the previous behavior: list(my_dict.keys()).

Strings

Changes to the handling of strings was our major hurdle in upgrading from Python 2 to Python 3. In Python 2, there is a separation between str and unicode. In Python 3, both of those are like unicode (but called str) and a new type was introduced: bytes. A good starting places to learn the difference is:

http://python-future.org/compatible_idioms.html?highlight=string#strings-and-bytes

The future library which enables support for Python 2 and Python 3 concurrently has its own str class. It is necessary to look at the imports for a module to know what str is being used.

Which str is it??

If nothing special is imported, str is the builtin str which in Python 2 is non-unicode and unicode in Python 3. String literals like “foo” are also of type str.

If unicode_literal is imported, then “foo” is the same as u”foo” and is unicode in Python 2 or just str in Python 3.

If from builtins import str is imported, then str is more like unicode and may fail existing calls to isinstance().

isinstance(x, str)

Since str can change its type, this often will not do what you want. Using past.builtins.basestring is generally a good solution, e.g. isinstance(x, basestring)

str(some_variable)

If you are trying to turn a variable into a string, this may not do what you want since it might be creating a unicode.

This is especially problematic for passing strings to Ice methods, which are implemented in C++ and fail spectacularly if they receive non-string objects (like unicode).

future.utils.native_str maintains the previous semantics producing builtin str objects. Native str semantics are especially important when working with Ice, e.g.

ctx = {'omero.group': native_str(groupId)}
conn.getUpdateService().saveArray(pixels, ctx)

StringIO and open(“file”, “r”)

StringIO and open() may need replacing with BytesIO and open(“file”, “rb”) respectively. This depends on whether or not your code is expecting a stream to be binary.

Regexes

Regexes must start with r”” for raw to prevent escapes from being misinterpreted (e.g. d).

Numerics

long no longer exists. Replace omero.rtypes.wrap(long_value) with omero.rtypes.rlong(long_value).

Division with / now produces a floating point. For example, choice * int(percent) / 100 no longer produces an integer in Python 3. Use //.