Skip to content

Write py2py3 compatible code

Giovanni Barillari edited this page Apr 27, 2015 · 1 revision

#Write py2py3 compatible code on pyDAL

pyDAL aims to be compatible with several python versions, from python 2.6.x to python 3.x

In order to guarantee an easy development for the contributors, pyDAL integrates the _compat module, and the main development team has wrote down these guidelines that should be followed in contributing to the project.

Check the python version

When you need to check the python version, or separate some code blocks from python 2 to python 3, you should use the PY2 boolean variable, as follows:

from ._compat import PY2

if PY2:
    # some python 2 code
else:
    # some python 3 code

keys, values and items

As you may know, with python 3 the dict methods iterkeys, itervalues and iteritems are no longer available, since now keys, values and items return dynamic objects instead of lists.

When you need to use an iterator, you should use the provided functions of _compat:

from ._compat import iterkeys, itervalues, iteritems

d = dict()
for k in iterkeys(d):
    # some code
for v in itervalues(d):
    # some code
for k, v in iteritems(d):
    # some code

Also, when you need to work with copied lists from a dictionary, and want the behavior to be the same both on python 2 and python 3, you should write:

d = dict()

keys = list(d)
values = list(d.values())
items = list(d.items())

long

In python3 there's no type for python 2 long. So, when you need to work with longs in python2 and get the same code working on python 3, you should import integer_types from _compat and define long:

from ._compat import integer_types

long = integer_types[-1]

xrange

In python 3 you don't have range and xrange anymore. You only have range that behaves as the python 2 xrange. So, when you work with xrange in python 2, ensure to import it from _compat:

from ._compat import xrange

class iterator and boolean methods

In python 2 you can implements an iterator and a boolean with next and __nonzero__ methods. In python 3 you have the better named methods __next__ and __bool__.

When you need to implements these methods in classes inside pydal, you should use the python 3 notation and import the implements_iterator and implements_bool decorators from _compat:

from ._compat import implements_iterator, implements_bool

@implements_iterator
@implements_bool
class MyClass(object):
    def __next__(self):
        # iterator code
    def __bool__(self):
        # boolean code

metaclasses

In python 2 you can implement a type on a class using the __metaclass__ attribute. This is no longer an option in python 3. When you need to implement a metaclass in pyDAL, you should use the with_metaclass method of _compat:

from ._compat import with_metaclass

class MyMetaclass(type):
    # code

class MyClass(with_metaclass(MyMetaclass)):
    # code

You can also do sub-classing using with_metaclass:

class A(object):
    # code

class B(object):
    # code

class C(with_metaclass(MyMetaclass, A)):
    # code

class D(with_metaclass(MyMetaclass, A, B)):
    # code

strings and unicode

Since in python 3 everything changed regarding bytes/unicode, pyDAL has some helpers in _compat module to help you writing compatible code on both sides:

  • string_types is a tuple containing the proper string types
  • basestring gives you the same behavior of python 2 in python 3
  • to_unicode(obj) method helps you convert objects to unicode in both language versions

Also, you may need to compute md5 hashes, and you will find the hashlib_md5 method in _compat which converts objects to the right format in both versions.

pickle, StringIO and copy_reg

With python3 these libraries have changed in names, or are available in different packages from python 2. Due to this, when you need them, you can import them from the _compat module and use them as normal:

from ._compat import pickle
from ._compat import StringIO
from ._compat import copyreg