1"""Tracing metaclass. 2 3XXX This is very much a work in progress. 4 5""" 6 7import types, sys 8 9class TraceMetaClass: 10 """Metaclass for tracing. 11 12 Classes defined using this metaclass have an automatic tracing 13 feature -- by setting the __trace_output__ instance (or class) 14 variable to a file object, trace messages about all calls are 15 written to the file. The trace formatting can be changed by 16 defining a suitable __trace_call__ method. 17 18 """ 19 20 __inited = 0 21 22 def __init__(self, name, bases, dict): 23 self.__name__ = name 24 self.__bases__ = bases 25 self.__dict = dict 26 # XXX Can't define __dict__, alas 27 self.__inited = 1 28 29 def __getattr__(self, name): 30 try: 31 return self.__dict[name] 32 except KeyError: 33 for base in self.__bases__: 34 try: 35 return base.__getattr__(name) 36 except AttributeError: 37 pass 38 raise AttributeError, name 39 40 def __setattr__(self, name, value): 41 if not self.__inited: 42 self.__dict__[name] = value 43 else: 44 self.__dict[name] = value 45 46 def __call__(self, *args, **kw): 47 inst = TracingInstance() 48 inst.__meta_init__(self) 49 try: 50 init = inst.__getattr__('__init__') 51 except AttributeError: 52 init = lambda: None 53 apply(init, args, kw) 54 return inst 55 56 __trace_output__ = None 57 58class TracingInstance: 59 """Helper class to represent an instance of a tracing class.""" 60 61 def __trace_call__(self, fp, fmt, *args): 62 fp.write((fmt+'\n') % args) 63 64 def __meta_init__(self, klass): 65 self.__class = klass 66 67 def __getattr__(self, name): 68 # Invoked for any attr not in the instance's __dict__ 69 try: 70 raw = self.__class.__getattr__(name) 71 except AttributeError: 72 raise AttributeError, name 73 if type(raw) != types.FunctionType: 74 return raw 75 # It's a function 76 fullname = self.__class.__name__ + "." + name 77 if not self.__trace_output__ or name == '__trace_call__': 78 return NotTracingWrapper(fullname, raw, self) 79 else: 80 return TracingWrapper(fullname, raw, self) 81 82class NotTracingWrapper: 83 def __init__(self, name, func, inst): 84 self.__name__ = name 85 self.func = func 86 self.inst = inst 87 def __call__(self, *args, **kw): 88 return apply(self.func, (self.inst,) + args, kw) 89 90class TracingWrapper(NotTracingWrapper): 91 def __call__(self, *args, **kw): 92 self.inst.__trace_call__(self.inst.__trace_output__, 93 "calling %s, inst=%s, args=%s, kw=%s", 94 self.__name__, self.inst, args, kw) 95 try: 96 rv = apply(self.func, (self.inst,) + args, kw) 97 except: 98 t, v, tb = sys.exc_info() 99 self.inst.__trace_call__(self.inst.__trace_output__, 100 "returning from %s with exception %s: %s", 101 self.__name__, t, v) 102 raise t, v, tb 103 else: 104 self.inst.__trace_call__(self.inst.__trace_output__, 105 "returning from %s with value %s", 106 self.__name__, rv) 107 return rv 108 109Traced = TraceMetaClass('Traced', (), {'__trace_output__': None}) 110 111 112def _test(): 113 global C, D 114 class C(Traced): 115 def __init__(self, x=0): self.x = x 116 def m1(self, x): self.x = x 117 def m2(self, y): return self.x + y 118 __trace_output__ = sys.stdout 119 class D(C): 120 def m2(self, y): print "D.m2(%r)" % (y,); return C.m2(self, y) 121 __trace_output__ = None 122 x = C(4321) 123 print x 124 print x.x 125 print x.m1(100) 126 print x.m1(10) 127 print x.m2(33) 128 print x.m1(5) 129 print x.m2(4000) 130 print x.x 131 132 print C.__init__ 133 print C.m2 134 print D.__init__ 135 print D.m2 136 137 y = D() 138 print y 139 print y.m1(10) 140 print y.m2(100) 141 print y.x 142 143if __name__ == '__main__': 144 _test() 145