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