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