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
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
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
63class 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
116class 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
141class 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
163tracer_impl = TracerClass()
164
165
166def 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
195def enable(t=None):
196	global tracer_impl
197	if t:
198		tracer_impl = t
199	sys.settrace(the_tracer_entrypoint)
200
201def disable():
202	sys.settrace(None)
203
204class 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
218def f(x,y=None):
219	if x > 0:
220		return 2 + f(x-2)
221	return 35
222
223def g(x):
224	return 1.134 / x
225
226def 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
231def 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
239if __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()
250