Old snakes. Part II

I've scraped up another sack of sweets, tanked up my keyboard with a fresh Oxygen font and ready to tell stories about writing a code compatible with Python 2.4-2.7 versions using only standard libraries.

Module imports

Sometimes modules have to be imported dynamically. Yes, yes, let in your little cozy kawaii code something that you don't even know by name.

Python 2.4+ has the imp library that takes in your guests, cleans their boots and shows their places. But Python 3.1+ trained another guard the importlib and made the imp retire from the Python version 3.4.

No one wants to use deprecated libraries. So no guards! Let's be good hosts by ourselves.

The sys library has the sys.path attribute, that contains all module search paths, default and specified via the PYTHONPATH environment variable.

import sys
sys.path
>>>
['',
 '/usr/bin',
 '/something/from/env/variable',
 '/usr/lib64/python27.zip',
 '/usr/lib64/python2.7',
 '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk',
 '/usr/lib64/python2.7/lib-old',
 '/usr/lib64/python2.7/lib-dynload',
 '/usr/lib64/python2.7/site-packages',
 '/usr/lib64/python2.7/site-packages/gtk-2.0',
 '/usr/lib/python2.7/site-packages',
 '/usr/lib/python2.7/site-packages/IPython/extensions']

And it's possible to add your own path to sys.path in your code:

import sys
import os

module_path = '../module_dir/module'
sys.path.append(os.path.realpath(os.path.dirname(module_path)))

The os.path.realpath method is used to convert symlinks and relative paths to absolute ones.

Note, that os.path.realpath cuts the trailing slash. It doesn't matter for the example above, but can be painful while processing paths that can end with a directory. In these situations os.path.dirname should be called before os.path.realpath, otherwise one level of the directory tree will be missed.

And now it's time to say “Hello, dear guest!”

# Obtain the module name and remove its extension if any
module_name = os.path.splitext(os.path.basename(module_path))[0]

# End this!
try:
    module_var = __import__(module_name)
except ImportError:
    print "Error: The module cannot be imported."

Short circuit conditions

Sometimes I have a great notion… to feel self fashionable, contemporary, hipster and write something long and artistic using list comprehensions:

[light  + ' is black' if light in pierre_soulages
 else light + ' is a wave' for light in art_and_science]

But Python 2.4 is an utilitarian language for writing of good programs, not good poems and it doesn't support shortcut conditional expressions. So if you want to write a poem just wrap the condition up in a small auxiliary function:

def write_poem(word):
    if rhyme(word):
        return word
    else:
        return 'shm' + word[1:]

new_poem = [write_poem(w) for w in text]

Of course, if you're an adept of Conceptual art, logical operators are always at your service.

JSON

Sometimes it's necessary to send some nontrivial configuration information to the script. The conventional way to do it is to use JSON or YAML. Python has no standard tools for YAML support. So JSON is the only choice or would be it, if the standard json library appeared in the Python version earlier than 2.6. There is the third-party simplejson library that is spread sufficiently to rely on the assumption that it's installed on most of machines with old Python versions. But simplejson stopped supporting Python 2.4 some versions ago. It's still possible to install simplejson under Python 2.4 but with some pain as a bonus.

I found no panacea for this. I just cowardly dropped users of the Python 2.4 with no simplejson installed from a part of absolutely-not-so-important functionality.

try:
    import json
except ImportError:
    try:
        import simplejson as json
    except ImportError:
        print "Sorry. No milk for you."

But if the configuration object is not too sophisticated, alternative methods can be tried, e.g. good old ini files.

Well, my sack looks empty. Oh, I'm lying! There are useful links on its bottom: