1import sys
2import inspect
3from collections import OrderedDict
4
5
6class TracebackFancy:
7
8    def __init__(self, traceback):
9        self.t = traceback
10
11    def getFrame(self):
12        return FrameFancy(self.t.tb_frame)
13
14    def getLineNumber(self):
15        return self.t.tb_lineno if self.t is not None else None
16
17    def getNext(self):
18        return TracebackFancy(self.t.tb_next)
19
20    def __str__(self):
21        if self.t is None:
22            return ""
23        str_self = "%s @ %s" % (
24            self.getFrame().getName(), self.getLineNumber())
25        return str_self + "\n" + self.getNext().__str__()
26
27
28class ExceptionFancy:
29
30    def __init__(self, frame):
31        self.etraceback = frame.f_exc_traceback
32        self.etype = frame.exc_type
33        self.evalue = frame.f_exc_value
34
35    def __init__(self, tb, ty, va):
36        self.etraceback = tb
37        self.etype = ty
38        self.evalue = va
39
40    def getTraceback(self):
41        return TracebackFancy(self.etraceback)
42
43    def __nonzero__(self):
44        return self.etraceback is not None or self.etype is not None or self.evalue is not None
45
46    def getType(self):
47        return str(self.etype)
48
49    def getValue(self):
50        return self.evalue
51
52
53class CodeFancy:
54
55    def __init__(self, code):
56        self.c = code
57
58    def getArgCount(self):
59        return self.c.co_argcount if self.c is not None else 0
60
61    def getFilename(self):
62        return self.c.co_filename if self.c is not None else ""
63
64    def getVariables(self):
65        return self.c.co_varnames if self.c is not None else []
66
67    def getName(self):
68        return self.c.co_name if self.c is not None else ""
69
70    def getFileName(self):
71        return self.c.co_filename if self.c is not None else ""
72
73
74class ArgsFancy:
75
76    def __init__(self, frame, arginfo):
77        self.f = frame
78        self.a = arginfo
79
80    def __str__(self):
81        args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs()
82        ret = ""
83        count = 0
84        size = len(args)
85        for arg in args:
86            ret = ret + ("%s = %s" % (arg, args[arg]))
87            count = count + 1
88            if count < size:
89                ret = ret + ", "
90        if varargs:
91            if size > 0:
92                ret = ret + " "
93            ret = ret + "varargs are " + str(varargs)
94        if kwargs:
95            if size > 0:
96                ret = ret + " "
97            ret = ret + "kwargs are " + str(kwargs)
98        return ret
99
100    def getNumArgs(wantVarargs=False, wantKWArgs=False):
101        args, varargs, keywords, values = self.a
102        size = len(args)
103        if varargs and wantVarargs:
104            size = size + len(self.getVarArgs())
105        if keywords and wantKWArgs:
106            size = size + len(self.getKWArgs())
107        return size
108
109    def getArgs(self):
110        args, _, _, values = self.a
111        argWValues = OrderedDict()
112        for arg in args:
113            argWValues[arg] = values[arg]
114        return argWValues
115
116    def getVarArgs(self):
117        _, vargs, _, _ = self.a
118        if vargs:
119            return self.f.f_locals[vargs]
120        return ()
121
122    def getKWArgs(self):
123        _, _, kwargs, _ = self.a
124        if kwargs:
125            return self.f.f_locals[kwargs]
126        return {}
127
128
129class FrameFancy:
130
131    def __init__(self, frame):
132        self.f = frame
133
134    def getCaller(self):
135        return FrameFancy(self.f.f_back)
136
137    def getLineNumber(self):
138        return self.f.f_lineno if self.f is not None else 0
139
140    def getCodeInformation(self):
141        return CodeFancy(self.f.f_code) if self.f is not None else None
142
143    def getExceptionInfo(self):
144        return ExceptionFancy(self.f) if self.f is not None else None
145
146    def getName(self):
147        return self.getCodeInformation().getName() if self.f is not None else ""
148
149    def getFileName(self):
150        return self.getCodeInformation().getFileName() if self.f is not None else ""
151
152    def getLocals(self):
153        return self.f.f_locals if self.f is not None else {}
154
155    def getArgumentInfo(self):
156        return ArgsFancy(
157            self.f, inspect.getargvalues(
158                self.f)) if self.f is not None else None
159
160
161class TracerClass:
162
163    def callEvent(self, frame):
164        pass
165
166    def lineEvent(self, frame):
167        pass
168
169    def returnEvent(self, frame, retval):
170        pass
171
172    def exceptionEvent(self, frame, exception, value, traceback):
173        pass
174
175    def cCallEvent(self, frame, cfunct):
176        pass
177
178    def cReturnEvent(self, frame, cfunct):
179        pass
180
181    def cExceptionEvent(self, frame, cfunct):
182        pass
183
184tracer_impl = TracerClass()
185
186
187def the_tracer_entrypoint(frame, event, args):
188    if tracer_impl is None:
189        return None
190    if event == "call":
191        call_retval = tracer_impl.callEvent(FrameFancy(frame))
192        if not call_retval:
193            return None
194        return the_tracer_entrypoint
195    elif event == "line":
196        line_retval = tracer_impl.lineEvent(FrameFancy(frame))
197        if not line_retval:
198            return None
199        return the_tracer_entrypoint
200    elif event == "return":
201        tracer_impl.returnEvent(FrameFancy(frame), args)
202    elif event == "exception":
203        exty, exva, extb = args
204        exception_retval = tracer_impl.exceptionEvent(
205            FrameFancy(frame), ExceptionFancy(extb, exty, exva))
206        if not exception_retval:
207            return None
208        return the_tracer_entrypoint
209    elif event == "c_call":
210        tracer_impl.cCallEvent(FrameFancy(frame), args)
211    elif event == "c_return":
212        tracer_impl.cReturnEvent(FrameFancy(frame), args)
213    elif event == "c_exception":
214        tracer_impl.cExceptionEvent(FrameFancy(frame), args)
215    return None
216
217
218def enable(t=None):
219    global tracer_impl
220    if t:
221        tracer_impl = t
222    sys.settrace(the_tracer_entrypoint)
223
224
225def disable():
226    sys.settrace(None)
227
228
229class LoggingTracer:
230
231    def callEvent(self, frame):
232        print("call " + frame.getName() + " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()))
233
234    def lineEvent(self, frame):
235        print("running " + frame.getName() + " @ " + str(frame.getLineNumber()) + " locals are " + str(frame.getLocals()) + " in " + frame.getFileName())
236
237    def returnEvent(self, frame, retval):
238        print("return from " + frame.getName() + " value is " + str(retval) + " locals are " + str(frame.getLocals()))
239
240    def exceptionEvent(self, frame, exception):
241        print("exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber()))
242        print("tb: " + str(exception.getTraceback()))
243
244# the same functionality as LoggingTracer, but with a little more
245# lldb-specific smarts
246
247
248class LLDBAwareTracer:
249
250    def callEvent(self, frame):
251        if frame.getName() == "<module>":
252            return
253        if frame.getName() == "run_one_line":
254            print("call run_one_line(%s)" % (frame.getArgumentInfo().getArgs()["input_string"]))
255            return
256        if "Python.framework" in frame.getFileName():
257            print("call into Python at " + frame.getName())
258            return
259        if frame.getName() == "__init__" and frame.getCaller().getName(
260        ) == "run_one_line" and frame.getCaller().getLineNumber() == 101:
261            return False
262        strout = "call " + frame.getName()
263        if (frame.getCaller().getFileName() == ""):
264            strout += " from LLDB - args are "
265            args = frame.getArgumentInfo().getArgs()
266            for arg in args:
267                if arg == "dict" or arg == "internal_dict":
268                    continue
269                strout = strout + ("%s = %s " % (arg, args[arg]))
270        else:
271            strout += " from " + frame.getCaller().getName() + " @ " + \
272                str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo())
273        print(strout)
274
275    def lineEvent(self, frame):
276        if frame.getName() == "<module>":
277            return
278        if frame.getName() == "run_one_line":
279            print("running run_one_line(%s) @ %s" % (frame.getArgumentInfo().getArgs()["input_string"], frame.getLineNumber()))
280            return
281        if "Python.framework" in frame.getFileName():
282            print("running into Python at " + frame.getName() + " @ " + str(frame.getLineNumber()))
283            return
284        strout = "running " + frame.getName() + " @ " + str(frame.getLineNumber()) + \
285            " locals are "
286        if (frame.getCaller().getFileName() == ""):
287            locals = frame.getLocals()
288            for local in locals:
289                if local == "dict" or local == "internal_dict":
290                    continue
291                strout = strout + ("%s = %s " % (local, locals[local]))
292        else:
293            strout = strout + str(frame.getLocals())
294        strout = strout + " in " + frame.getFileName()
295        print(strout)
296
297    def returnEvent(self, frame, retval):
298        if frame.getName() == "<module>":
299            return
300        if frame.getName() == "run_one_line":
301            print("return from run_one_line(%s) return value is %s" % (frame.getArgumentInfo().getArgs()["input_string"], retval))
302            return
303        if "Python.framework" in frame.getFileName():
304            print("return from Python at " + frame.getName() + " return value is " + str(retval))
305            return
306        strout = "return from " + frame.getName() + " return value is " + \
307            str(retval) + " locals are "
308        if (frame.getCaller().getFileName() == ""):
309            locals = frame.getLocals()
310            for local in locals:
311                if local == "dict" or local == "internal_dict":
312                    continue
313                strout = strout + ("%s = %s " % (local, locals[local]))
314        else:
315            strout = strout + str(frame.getLocals())
316        strout = strout + " in " + frame.getFileName()
317        print(strout)
318
319    def exceptionEvent(self, frame, exception):
320        if frame.getName() == "<module>":
321            return
322        print("exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber()))
323        print("tb: " + str(exception.getTraceback()))
324
325
326def f(x, y=None):
327    if x > 0:
328        return 2 + f(x - 2)
329    return 35
330
331
332def g(x):
333    return 1.134 / x
334
335
336def print_keyword_args(**kwargs):
337    # kwargs is a dict of the keyword args passed to the function
338    for key, value in kwargs.items():
339        print("%s = %s" % (key, value))
340
341
342def total(initial=5, *numbers, **keywords):
343    count = initial
344    for number in numbers:
345        count += number
346    for key in keywords:
347        count += keywords[key]
348    return count
349
350if __name__ == "__main__":
351    enable(LoggingTracer())
352    f(5)
353    f(5, 1)
354    print_keyword_args(first_name="John", last_name="Doe")
355    total(10, 1, 2, 3, vegetables=50, fruits=100)
356    try:
357        g(0)
358    except:
359        pass
360    disable()
361