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 `//`.