Stop writing Python 4 incompatible code

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Jan 13 02:30:19 EST 2016


Quote:

    With the end of support for Python 2 on the horizon (in 2020),
    many package developers have made their packages compatible
    with both Python 2 and Python 3 by using constructs such as:

        if sys.version_info[0] == 2:
            # Python 2 code
        else:
            # Python 3 code

    [...]
    Another example of problematic code is the following:

        if six.PY2:
            # Python 2 code
        elif six.PY3:
            # Python 3 code

    In this case, no code will get executed on Python 4 at all!


[end quote]


http://astrofrog.github.io/blog/2016/01/12/stop-writing-python-4-
incompatible-code/

or http://tinyurl.com/jskt54s


Better still, don't do version checks *at all*. There is almost never any 
need for a version check. Better is to use feature detection:


try:
    xrange  # Succeeds in Python 2.
except NameError:
    xrange = range


Not only is this almost entirely future-proof[1] and avoids overly-specific 
version tests, but if your code happens to find itself running in a 
customized environment which has already monkey-patched builtins to re-add 
xrange, the first lookup will succeed and no extra work need be done.

You can be extremely fine-grained too:

try:
    casefold = str.casefold  # Succeeds in Python 3.3 or better
else:
    casefold = str.lower  # Poor Man's case-folding.
    # Or if you prefer:
    def casefold(s):
        ...


About the only thing I use version checking for is to decide which prompt I 
want to use:

if sys.version_info[0] >= 3 and os.name == 'posix':
    # Make the main prompt bold in Python 3. This probably won't
    # work on Windows, put it should(?) work on any POSIX system.
    sys.ps1 = '\001\x1b[1m\002py> \001\x1b[0m\002'
else:
    sys.ps1 = 'py> '


In Python 3, that highlights the prompt py> in bold.

You can detect the return type:

if isinstance(map(len, []), list):
    print("Python 2 version of map")
else:
    # Python 3, or Python 2 with future_builtins.
    print("Python 3 version of map")



and even the number and kind of arguments:

try:
    sorted([], cmp=None)
except TypeError:
    print("use key not cmp in Python 3")
    _sorted = sorted
    def sorted(L, cmp=None):
        if cmp is None:
            return _sorted(L)
        return _sorted(L, key=functools.cmp_to_key(cmp))



So remember, use feature detection, not version checking.






[1] It might fail if some distant version of Python adds an xrange which 
does something completely unexpected, or if it removes range; but surely 
nobody can be expected to write forward-compatible code in the face of 
changes of that magnitude.


-- 
Steve




More information about the Python-list mailing list