wiki:DeveloperGuidelines/Py_2_3

Version 20 (modified by Dominic König, 6 years ago) ( diff )

--

Python 2/3 Compatibility

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:

print "example" # deprecated

You shouldn't use the print-function either, because it can clash with uWSGI:

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:

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):

current.log.debug("example") # even better

Use as-syntax for catching exceptions

When catching exceptions in a variable, don't use the comma-syntax:

try:
    ...
except Exception, e: # deprecated
    ...

Instead, use the as-keyword:

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:

# 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.

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:

from .s3datetime import s3_format_datetime # inside modules/s3, preferred variant

...or an absolute path relative to modules (or the global python path):

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):

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 Import Comments/Caveats
PY2from s3compat import PY2Constant indicating whether we're currently running on Py2, should only be used if alternatives cannot be generalized
StringIOfrom s3compat import StringIO
basestringfrom s3compat import basestring
INTEGER_TYPESfrom s3compat import INTEGER_TYPESmaps to tuple of all known integer types: (int,long) in Py2, (int,) in Py3), for use with isinstance
urllib2{{{from s3compat import urllib2maps to urllib.requests in Py3, which is though only part of urllib2 - some urllib2 objects may need to be imported separately (see below)
HTTPError{{{from s3compat import HTTPErrorreplaces urllib2.HTTPError in Py2
urlopen{{{from s3compat import urlopenreplaces urllib.urlopen and urllib2.urlopen in Py2
urlencode{{{from s3compat import urlencodereplaces urllib.urlencode in Py2

No dict.iteritems, iterkeys or itervalues

Python-3 does no longer have dict.iteritems(). We use dict.items() instead:

# 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).

Note: See TracWiki for help on using the wiki.