= Python 2/3 Compatibility = [[TOC]] This guideline documents coding conventions to achieve hybrid Python-2.7/Python-3.5 compatibility in Sahana. It's a working document that will be added to as we move towards full Python-3 compatibility. == Syntax == === No print statements === Don't use the print statement anywhere: {{{#!python print "example" # deprecated }}} You shouldn't use the print-function either, because it can clash with uWSGI: {{{#!python print("example") # not good }}} ...but it can be tolerated in CLI scripts which don't run in the WSGI environment. Best option for debug output is to use sys.stderr.write: {{{#!python import sys sys.stderr.write("example\n") # better }}} ...or the logger (as it can be configured globally to write to a log file instead of the system console): {{{#!python current.log.debug("example") # even better }}} === Use as-syntax for catching exceptions === When catching exceptions in a variable, don't use the comma-syntax: {{{#!python try: ... except Exception, e: # deprecated ... }}} Instead, use the as-keyword: {{{#!python try: ... except Exception as e: # new standard ... }}} === No exec statements === In Python-3, the {{{exec}}} statement has become a function {{{exec()}}}. Python-2.7 accepts the function-syntax as well, so we use it throughout: {{{#!python # Deprecated: exec pyexpr # New Standard: exec(pyexpr) }}} NB the Python-2.7 {{{exec}}} statement also accepts a 3-tuple as parameter {{{exec(expr, globals, locals)}}} which syntax is equivalent to the exec-function in Python-3. == Usage == === No implicit package-relative imports === Python-3 does not search for modules relative to the current module in the same package - unless explicitly indicated by leading {{{.}}} or {{{..}}} in the module path. {{{#!python from s3datetime import s3_format_datetime # inside modules/s3, not working in Python-3 }}} Python-2.7 would search relative to the current module, but on the other hand, it supports the explicit-relative syntax as well. So we decide that only explicit paths shall be used in imports. To import a module in the same package (e.g. within s3), either use explicit-relative syntax: {{{#!python from .s3datetime import s3_format_datetime # inside modules/s3, preferred variant }}} ...or an absolute path relative to modules (or the global python path): {{{#!python from s3.s3datetime import s3_format_datetime # inside modules/s3, acceptable alternative }}} Outside of modules/s3, you should always import from the top-level of the s3 package (because the package structure may change over time): {{{#!python from s3 import s3_format_datetime # outside modules/s3 }}} === Alternative Imports === As the locations and names of some libraries have changed in Python-3, we use the compatibility module ({{{modules/s3compat.py}}}) to implement suitable alternatives. Similarily, the compat module provides alternatives for other objects such as types, functions and certain common patterns. Where an object name is provided by modules/s3compat.py, it '''must''' be imported from there if used. The following objects are provided by s3compat: ||= '''Name''' =||= '''Type''' =||= '''Import''' =||= '''!Comments/Caveats''' =|| ||PY2||boolean||{{{from s3compat import PY2}}}||Constant indicating whether we're currently running on Py2, should only be used if alternatives cannot be generalized|| ||StringIO||class||{{{from s3compat import StringIO}}}|| || ||pickle||module||{{{from s3compat import pickle}}}||replaces cPickle in Py2|| ||basestring||type||{{{from s3compat import basestring}}}|| || ||unichr||function||{{{from s3compat import unichr}}}|| || ||STRING_TYPES||tuple||{{{from s3compat import STRING_TYPES}}}||maps to tuple of all known string types: (str,unicode) in Py2, (str,) in Py3, for type checking|| ||long||type||{{{from s3compat import long}}}||same as {{{int}}} in Py3, so can occasionally lead to redundancy|| ||INTEGER_TYPES||tuple||{{{from s3compat import INTEGER_TYPES}}}||maps to tuple of all known integer types: (int,long) in Py2, (int,) in Py3, for use with {{{isinstance}}}|| ||urlparse||module||{{{from s3compat import urlparse}}}||maps to urllib.parse in Py3|| ||urllib2||module||{{{from s3compat import urllib2}}}||maps to urllib.requests in Py3, which contains only part of Py2's urllib2 - some urllib2 objects therefore need to be imported separately (see below)|| ||HTTPError||class||{{{from s3compat import HTTPError}}}||replaces urllib2.HTTPError in Py2|| ||urlopen||function||{{{from s3compat import urlopen}}}||replaces urllib.urlopen and urllib2.urlopen in Py2|| ||urlencode||function||{{{from s3compat import urlencode}}}||replaces urllib.urlencode in Py2|| ||HTMLParser||class||{{{from s3compat import HTMLParser}}}||replaces HTMLParser.HTMLParser in Py2|| === No dict.iteritems, iterkeys or itervalues === Python-3 does no longer have {{{dict.iteritems()}}}. We use {{{dict.items()}}} instead: {{{#!python # Deprecated: for x, y in d.iteritems(): # Compatible: for x, y in d.items(): }}} '''NB''' In Python-2, {{{dict.items()}}} returns a list of tuples, but in Python-3 it returns a dict view object that is sensitive to changes of the dict. If a list is required, or the dict is changed inside the loop, the result of {{{dict.items()}}} must be converted into a list explicitly. The same applies to {{{dict.iterkeys()}}} (use {{{dict.keys()}}} instead) and {{{dict.itervalues()}}} (use {{{dict.values()}}} instead). === Map and Zip return generators === In Python-3, the {{{map()}}} and {{{zip()}}} functions return generator objects rather than lists. This is fine when we want to iterate over the result, especially when there is a chance to break out of the loop early. But where lists are required, the return value must be converted explicitly using the list constructor. {{{#!python # This is fine: for item in map(func, values): # This could be wrong: result = map(func, values) # This is better: result = list(map(func, values)) # This could be even better: result = [func(v) for v in values] }}} '''NB''' For building a list from a single iterable argument, we prefer list comprehensions over {{{map()}}} for readability and speed.