Enrico Granata | c8b36f1 | 2013-05-07 19:49:59 +0000 | [diff] [blame] | 1 | import sys |
| 2 | import inspect |
| 3 | from collections import OrderedDict |
| 4 | |
| 5 | class TracebackFancy: |
| 6 | def __init__(self,traceback): |
| 7 | self.t = traceback |
| 8 | |
| 9 | def getFrame(self): |
| 10 | return FrameFancy(self.t.tb_frame) |
| 11 | |
| 12 | def getLineNumber(self): |
| 13 | return self.t.tb_lineno |
| 14 | |
| 15 | def getNext(self): |
| 16 | return TracebackFancy(self.t.tb_next) |
| 17 | |
| 18 | def __str__(self): |
| 19 | if self.t == None: |
| 20 | return "" |
| 21 | str_self = "%s @ %s" % (self.getFrame().getName(), self.getLineNumber()) |
| 22 | return str_self + "\n" + self.getNext().__str__() |
| 23 | |
| 24 | class ExceptionFancy: |
| 25 | def __init__(self,frame): |
| 26 | self.etraceback = frame.f_exc_traceback |
| 27 | self.etype = frame.exc_type |
| 28 | self.evalue = frame.f_exc_value |
| 29 | |
| 30 | def __init__(self,tb,ty,va): |
| 31 | self.etraceback = tb |
| 32 | self.etype = ty |
| 33 | self.evalue = va |
| 34 | |
| 35 | def getTraceback(self): |
| 36 | return TracebackFancy(self.etraceback) |
| 37 | |
| 38 | def __nonzero__(self): |
| 39 | return self.etraceback != None or self.etype != None or self.evalue != None |
| 40 | |
| 41 | def getType(self): |
| 42 | return str(self.etype) |
| 43 | |
| 44 | def getValue(self): |
| 45 | return self.evalue |
| 46 | |
| 47 | class CodeFancy: |
| 48 | def __init__(self,code): |
| 49 | self.c = code |
| 50 | |
| 51 | def getArgCount(self): |
| 52 | return self.c.co_argcount |
| 53 | |
| 54 | def getFilename(self): |
| 55 | return self.c.co_filename |
| 56 | |
| 57 | def getVariables(self): |
| 58 | return self.c.co_varnames |
| 59 | |
| 60 | def getName(self): |
| 61 | return self.c.co_name |
| 62 | |
| 63 | class ArgsFancy: |
| 64 | def __init__(self,frame,arginfo): |
| 65 | self.f = frame |
| 66 | self.a = arginfo |
| 67 | |
| 68 | def __str__(self): |
| 69 | args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs() |
| 70 | ret = "" |
| 71 | count = 0 |
| 72 | size = len(args) |
| 73 | for arg in args: |
| 74 | ret = ret + ("%s = %s" % (arg, args[arg])) |
| 75 | count = count + 1 |
| 76 | if count < size: |
| 77 | ret = ret + ", " |
| 78 | if varargs: |
| 79 | if size > 0: |
| 80 | ret = ret + " " |
| 81 | ret = ret + "varargs are " + str(varargs) |
| 82 | if kwargs: |
| 83 | if size > 0: |
| 84 | ret = ret + " " |
| 85 | ret = ret + "kwargs are " + str(kwargs) |
| 86 | return ret |
| 87 | |
| 88 | def getNumArgs(wantVarargs = False, wantKWArgs=False): |
| 89 | args, varargs, keywords, values = self.a |
| 90 | size = len(args) |
| 91 | if varargs and wantVarargs: |
| 92 | size = size+len(self.getVarArgs()) |
| 93 | if keywords and wantKWArgs: |
| 94 | size = size+len(self.getKWArgs()) |
| 95 | return size |
| 96 | |
| 97 | def getArgs(self): |
| 98 | args, _, _, values = self.a |
| 99 | argWValues = OrderedDict() |
| 100 | for arg in args: |
| 101 | argWValues[arg] = values[arg] |
| 102 | return argWValues |
| 103 | |
| 104 | def getVarArgs(self): |
| 105 | _, vargs, _, _ = self.a |
| 106 | if vargs: |
| 107 | return self.f.f_locals[vargs] |
| 108 | return () |
| 109 | |
| 110 | def getKWArgs(self): |
| 111 | _, _, kwargs, _ = self.a |
| 112 | if kwargs: |
| 113 | return self.f.f_locals[kwargs] |
| 114 | return {} |
| 115 | |
| 116 | class FrameFancy: |
| 117 | def __init__(self,frame): |
| 118 | self.f = frame |
| 119 | |
| 120 | def getCaller(self): |
| 121 | return FrameFancy(self.f.f_back) |
| 122 | |
| 123 | def getLineNumber(self): |
| 124 | return self.f.f_lineno |
| 125 | |
| 126 | def getCodeInformation(self): |
| 127 | return CodeFancy(self.f.f_code) |
| 128 | |
| 129 | def getExceptionInfo(self): |
| 130 | return ExceptionFancy(self.f) |
| 131 | |
| 132 | def getName(self): |
| 133 | return self.f.f_code.co_name |
| 134 | |
| 135 | def getLocals(self): |
| 136 | return self.f.f_locals |
| 137 | |
| 138 | def getArgumentInfo(self): |
| 139 | return ArgsFancy(self.f,inspect.getargvalues(self.f)) |
| 140 | |
| 141 | class TracerClass: |
| 142 | def callEvent(self,frame): |
| 143 | pass |
| 144 | |
| 145 | def lineEvent(self,frame): |
| 146 | pass |
| 147 | |
| 148 | def returnEvent(self,frame,retval): |
| 149 | pass |
| 150 | |
| 151 | def exceptionEvent(self,frame,exception,value,traceback): |
| 152 | pass |
| 153 | |
| 154 | def cCallEvent(self,frame,cfunct): |
| 155 | pass |
| 156 | |
| 157 | def cReturnEvent(self,frame,cfunct): |
| 158 | pass |
| 159 | |
| 160 | def cExceptionEvent(self,frame,cfunct): |
| 161 | pass |
| 162 | |
| 163 | tracer_impl = TracerClass() |
| 164 | |
| 165 | |
| 166 | def the_tracer_entrypoint(frame,event,args): |
| 167 | if tracer_impl == None: |
| 168 | return None |
| 169 | if event == "call": |
| 170 | call_retval = tracer_impl.callEvent(FrameFancy(frame)) |
| 171 | if call_retval == False: |
| 172 | return None |
| 173 | return the_tracer_entrypoint |
| 174 | elif event == "line": |
| 175 | line_retval = tracer_impl.lineEvent(FrameFancy(frame)) |
| 176 | if line_retval == False: |
| 177 | return None |
| 178 | return the_tracer_entrypoint |
| 179 | elif event == "return": |
| 180 | tracer_impl.returnEvent(FrameFancy(frame),args) |
| 181 | elif event == "exception": |
| 182 | exty,exva,extb = args |
| 183 | exception_retval = tracer_impl.exceptionEvent(FrameFancy(frame),ExceptionFancy(extb,exty,exva)) |
| 184 | if exception_retval == False: |
| 185 | return None |
| 186 | return the_tracer_entrypoint |
| 187 | elif event == "c_call": |
| 188 | tracer_impl.cCallEvent(FrameFancy(frame),args) |
| 189 | elif event == "c_return": |
| 190 | tracer_impl.cReturnEvent(FrameFancy(frame),args) |
| 191 | elif event == "c_exception": |
| 192 | tracer_impl.cExceptionEvent(FrameFancy(frame),args) |
| 193 | return None |
| 194 | |
| 195 | def enable(t=None): |
| 196 | global tracer_impl |
| 197 | if t: |
| 198 | tracer_impl = t |
| 199 | sys.settrace(the_tracer_entrypoint) |
| 200 | |
| 201 | def disable(): |
| 202 | sys.settrace(None) |
| 203 | |
| 204 | class LoggingTracer: |
| 205 | def callEvent(self,frame): |
| 206 | print "call " + frame.getName() + " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()) |
| 207 | |
| 208 | def lineEvent(self,frame): |
| 209 | print "running " + frame.getName() + " @ " + str(frame.getLineNumber()) + " locals are " + str(frame.getLocals()) |
| 210 | |
| 211 | def returnEvent(self,frame,retval): |
| 212 | print "return from " + frame.getName() + " value is " + str(retval) + " locals are " + str(frame.getLocals()) |
| 213 | |
| 214 | def exceptionEvent(self,frame,exception): |
| 215 | print "exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber()) |
| 216 | print "tb: " + str(exception.getTraceback()) |
| 217 | |
| 218 | def f(x,y=None): |
| 219 | if x > 0: |
| 220 | return 2 + f(x-2) |
| 221 | return 35 |
| 222 | |
| 223 | def g(x): |
| 224 | return 1.134 / x |
| 225 | |
| 226 | def print_keyword_args(**kwargs): |
| 227 | # kwargs is a dict of the keyword args passed to the function |
| 228 | for key, value in kwargs.iteritems(): |
| 229 | print "%s = %s" % (key, value) |
| 230 | |
| 231 | def total(initial=5, *numbers, **keywords): |
| 232 | count = initial |
| 233 | for number in numbers: |
| 234 | count += number |
| 235 | for key in keywords: |
| 236 | count += keywords[key] |
| 237 | return count |
| 238 | |
| 239 | if __name__ == "__main__": |
| 240 | enable(LoggingTracer()) |
| 241 | f(5) |
| 242 | f(5,1) |
| 243 | print_keyword_args(first_name="John", last_name="Doe") |
| 244 | total(10, 1, 2, 3, vegetables=50, fruits=100) |
| 245 | try: |
| 246 | g(0) |
| 247 | except: |
| 248 | pass |
| 249 | disable() |