This recipe allows a user to place debug messages, error messages and standard messages throughout a program. The function name and line number will be added to each debug and error message before it is printed out. In addition, each of these messages can be passed to multiple handler objects that can direct the output to log files, e-mails, stdout, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #messaging.py
#this is a module used for messaging. It allows multiple classes
#to handle various types of messages. It should work on all python
#versions >= 1.5.2
import sys, string, exceptions
#this flag determines whether debug output is sent to debug handlers themselves
debug = 1
def setDebugging(debugging):
global debug
debug = debugging
class MessagingException(exceptions.Exception):
"""an exception class for any errors that may occur in
a messaging function"""
def __init__(self, args=None):
self.args = args
class FakeException(exceptions.Exception):
"""an exception that is thrown and then caught
to get a reference to the current execution frame"""
pass
class MessageHandler:
"""All message handlers should inherit this class. Each method will be
passed a string when the executing program passes calls a messaging function"""
def handleStdMsg(self, msg):
"""do something with a standard message from the program"""
pass
def handleErrMsg(self, msg):
"""do something with an error message. This will already include the
class, method, and line of the call"""
pass
def handleDbgMsg(self, msg):
"""do something with a debug message. This will already include the
class, method, and line of the call"""
pass
class defaultMessageHandler(MessageHandler):
"""This is a default message handler. It simply spits all strings to
standard out"""
def handleStdMsg(self, msg):
sys.stdout.write(msg + "\n")
def handleErrMsg(self, msg):
sys.stderr.write(msg + "\n")
def handleDbgMsg(self, msg):
sys.stdout.write(msg + "\n")
#this keeps track of the handlers
_messageHandlers = []
#call this with the handler to register it for receiving messages
def registerMessageHandler(handler):
"""we're not going to check for inheritance, but we should check to make
sure that it has the correct methods"""
for methodName in ["handleStdMsg", "handleErrMsg", "handleDbgMsg"]:
try:
getattr(handler, methodName)
except:
raise MessagingException, "The class " + handler.__class__.__name__ + " is missing a " + methodName + " method"
_messageHandlers.append(handler)
def getCallString(level):
#this gets us the frame of the caller and will work
#in python versions 1.5.2 and greater (there are better
#ways starting in 2.1
try:
raise FakeException("this is fake")
except Exception, e:
#get the current execution frame
f = sys.exc_info()[2].tb_frame
#go back as many call-frames as was specified
while level >= 0:
f = f.f_back
level = level-1
#if there is a self variable in the caller's local namespace then
#we'll make the assumption that the caller is a class method
obj = f.f_locals.get("self", None)
functionName = f.f_code.co_name
if obj:
callStr = obj.__class__.__name__+"::"+f.f_code.co_name+" (line "+str(f.f_lineno)+")"
else:
callStr = f.f_code.co_name+" (line "+str(f.f_lineno)+")"
return callStr
#send this message to all handlers of std messages
def stdMsg(*args):
stdStr = string.join(map(str, args), " ")
for handler in _messageHandlers:
handler.handleStdMsg(stdStr)
#send this message to all handlers of error messages
def errMsg(*args):
errStr = "Error in "+getCallString(1)+" : "+string.join(map(str, args), " ")
for handler in _messageHandlers:
handler.handleErrMsg(errStr)
#send this message to all handlers of debug messages
def dbgMsg(*args):
if not debug:
return
errStr = getCallString(1)+" : "+string.join(map(str, args), " ")
for handler in _messageHandlers:
handler.handleDbgMsg(errStr)
registerMessageHandler(defaultMessageHandler())
#end of messaging.py
#test.py
#here is a simple use case for the above module
from messaging import stdMsg, dbgMsg, errMsg, setDebugging
setDebugging(0)
dbgMsg("this won't be printed")
stdMsg("but this will")
setDebugging(1)
def foo():
dbgMsg("this is a debug message in", "foo")
class bar:
def baz(self):
errMsg("this is an error message in bar")
foo()
b = bar()
b.baz()
#end of test.py
output is :
but this will
foo (line 12) : this is a debug message in foo
Error in bar::baz (line 16) : this is an error message in bar
|
I use debug statements regularly in my code and often include the name of the function in the debug message. This is tedious and error prone if I copy and paste debug statements between different functions. In addition, I have often desired the ability to easily turn off debug statements without needing to use if not debug: print "stuff" statements everywhere.
This code automatically inserts the function name, class name, and line number of the debug statement or error statement when it is printed out. This makes it easy to jump straight to that line in an editor. Each of the messaging methods: stdMsg, errMsg, and dbgMsg can be used just like a print statement in that you can separate an arbitrary number of arguments with commas and each one will be converted to a string. e.g. dbgMsg("the value of X is", x)
The other nice thing about this type of system is that you can create multiple "handler" objects by subclassing MessageHandler. Each registered handler gets sent each string when a program calls a Msg function so it's possible that one Msg call could send text to a log file, a gui window, and standard out.