Old snakes

The first obstacle on my project steeplechase was Python versions supported by QEMU. I, a kid spoiled by Python 2.7-3.x sweets, was rather discouraged by the limitation to versions 2.4-2.7. Well, for pessimists obstacles are difficulties, for optimists they are opportunities.

In this post I'll collect all ambiguities I meet during project and ways I resolve them. Also there are some notes about the state of things in Python 3.x.

Context manager

I have the test runner that will execute several tests in a row. Tests are instances of Test class. The goal is to clean up work environment after each test (close files, change the current directory and so on). The ideal case for context managers!

class Test(object):

    def execute(self):
        # Run test
        pass

    def __enter__(self):
        return self

    def __exit__(self, *args):
        # Do cleanup
        pass

with Test() as test:
    test.execute()

But their birth dated by Python 2.5. Well, let's return back to roots. Unwrap “with … as” construction to explicit setup, execution and finalization, forming last two in “try … finally” statement. Here we are!

class Test(object):

    def execute(self):
        # Run test
        pass

    def finish(self):
        # Do cleanup
        pass

test = Test()
try:
    test.execute()
finally:
    test.finish()

But there is one little note… What if you want to catch exceptions?

Exceptions

A piece of cake! Even a three month child can tap by her foot

try:
    raise Exception
except:
    print 'Internal error'
finally:
    print 'Bye!'

But her mom that remembers the times of Python 2.4 would wish to correct it on the sly to

try:
    try:
        raise Exception
    except:
        print 'Internal error'
finally:
    print 'Bye!'

Because Python 2.4 doesn't support “except” and “finally” clauses in the same “try” statement.

But she is a good mom and lets her child live in the world with less lines of code.

Another thing that is worth to keep in mind is the exception root class. Python 2.4 has Exception class as a root one. From Python 2.5 the root class is BaseException, and SystemExit, KeyboardInterrupt, GeneratorExit classes don't inherit from Exception class anymore. So a catch of “except Exception” statement depends on the python version.

The problem can be resolved by special handling of unnecessary exceptions. In my case every exception but related to the program exit should be logged.

import sys

try:
    raise Exception
    # Silent exit on user break
    except (KeyboardInterrupt, SystemExit):
        sys.exit(1)
    except:
        e = sys.exc_info()[1]
        print e
        sys.exit(1)

Hey, adepts of Universality and Portability bordering with Nerdiness, if you're going to write something Python 2.4-3.x compatible, remember the power of sys.exc_info() because reasons.

Parsing of command line arguments

This task wasn't an issue if optparse library wouldn't be deprecated since Python 2.7. Of course, there is some stability in the world and the name of this stability is entropy “getopt”. The one point worth to be noted here is that presence of mandatory positional parameters should be checked by script itself. The library doesn't provide this option.

Oops, it looks like my keyboard are running out of ink. I will update this post as soon as I tank up my keyboard and find something exiting on this topic.