Phase objects: simple decent error reporting for Python programs
For a lot of the sort of utility programs I write in Python, the only thing the program can really do when it runs into an operating system level error is report whatever the problem is and exit. So programs start out as one big try:/except: block:
try: fp1 = open(fnameA, "r") .... fp2 = open(fnameB, "r") .... except EnvironmentError, e: die("OS error: %s" % str(e))
The problem with this is that the error reported, while accurate, is basically without context; you don't know what the program was doing when it reported 'permission denied' or whatever. So simple programs mutate slightly to keep track of what they are doing in a variable:
try: phase = "opening %s" % fnameA fp1 = open(fnameA, "r") phase = "parsing %s" % fnameA .... except EnvironmentError, e: die("%s: error: %s" % (phase, str(e)))
(Well, my simple programs do, since I have no desire to wrap each separate operation that might fail in its own try:/except: block. That's too much work.)
The problem with this approach is that it doesn't work very well when
your program grows subroutines, because updating a global variable to
track the phase is a pain. The solution I have adopted is to create a
special class to track what phase of execution the program is in; with
my typical (lack of) imagination, I call this class Phase
, and it
looks like this:
class Phase(object): def __init__(self): self.phase = ["starting",] def __call__(self, ph): self.phase = [ph, ] def __str__(self): return ": ".join(self.phase) def push(self, ph): self.phase.append(ph) def pop(self): self.phase.pop()
It is used as phase("reading file whatever")
. It supports push()
and pop()
operations so that you can have nested situations (such
as the ability of configuration files to include other configuration
files) that report the full chain of events of how you wound up doing
whatever it was that failed.
My programs just make a single global Phase
instance and then use it
directly as a global variable, but I suppose a more elegant and reusable
method would be to create the instance at the top level and pass it down
through the call chain.
Comments on this page:
|
|