Monday, December 8, 2008

Clean Exception Tracking, Beautiful Code

I really wanted a way to track my exceptions without making my code look ugly. The answer seemed to be to use Python's decorators in some way, but they introduce a twist... decorators return functions, so checking the exception information always yields an exception inside a decorator.

As an example, see the following:


import inspect
def test_handler(func):
def func2(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception, e:
print "excepted"
(file,line,fname,context,ind) =
inspect.getframeinfo(inspect.currentframe())
print "Func: %s" % (str(fname))
return func2

@test_handler
def run_user_codes():
k = {}
k['test']

if __name__ == "__main__":
print "In main"
try:
run_user_codes()
except Exception, e:
str(e)


When you run that, the output will be:

In main
excepted
Func: func2


To solve this problem, you can use the traceback module to get accurate data regarding the actual exception. For example:


import sys, traceback

class TrackException(Exception):
def __init__(self, tb=None):
self._tb = []
self._tb.append(tb)

def add_tb(self, tb):
self._tb.append(tb)

def __str__(self):
string = ""
count = 0
for t in self._tb:
(file, line, funcname, codestr) = t
string += "[%d] %s (line %d, in %s): %s\n" %
(count,file, line, funcname, codestr)
count += 1
return string

def __getitem__(self, y):
return self._tb[y]

def inspected_exception_handler(func):
def func2(*args, **kwargs):
try:
return func(*args, **kwargs)
except TrackException, e:
(t,v,tb) = sys.exc_info()
list = traceback.extract_tb(tb)
e.add_tb(list[1])
raise e
except Exception, e:
(t,v,tb) = sys.exc_info()
list = traceback.extract_tb(tb)
raise TrackException(list[1])
return func2


@inspected_exception_handler
def run_user_code():
k = {}
k['test']

@inspected_exception_handler
def func():
run_user_code()

if __name__ == "__main__":
try:
func()
except Exception, e:
print str(e)



Including the above code in your code makes tracking function exceptions much easier without cluttering your code.

No comments: