--- /dev/null
+"""Tracing metaclass.\r
+\r
+XXX This is very much a work in progress.\r
+\r
+"""\r
+\r
+import types, sys\r
+\r
+class TraceMetaClass:\r
+ """Metaclass for tracing.\r
+\r
+ Classes defined using this metaclass have an automatic tracing\r
+ feature -- by setting the __trace_output__ instance (or class)\r
+ variable to a file object, trace messages about all calls are\r
+ written to the file. The trace formatting can be changed by\r
+ defining a suitable __trace_call__ method.\r
+\r
+ """\r
+\r
+ __inited = 0\r
+\r
+ def __init__(self, name, bases, dict):\r
+ self.__name__ = name\r
+ self.__bases__ = bases\r
+ self.__dict = dict\r
+ # XXX Can't define __dict__, alas\r
+ self.__inited = 1\r
+\r
+ def __getattr__(self, name):\r
+ try:\r
+ return self.__dict[name]\r
+ except KeyError:\r
+ for base in self.__bases__:\r
+ try:\r
+ return base.__getattr__(name)\r
+ except AttributeError:\r
+ pass\r
+ raise AttributeError, name\r
+\r
+ def __setattr__(self, name, value):\r
+ if not self.__inited:\r
+ self.__dict__[name] = value\r
+ else:\r
+ self.__dict[name] = value\r
+\r
+ def __call__(self, *args, **kw):\r
+ inst = TracingInstance()\r
+ inst.__meta_init__(self)\r
+ try:\r
+ init = inst.__getattr__('__init__')\r
+ except AttributeError:\r
+ init = lambda: None\r
+ apply(init, args, kw)\r
+ return inst\r
+\r
+ __trace_output__ = None\r
+\r
+class TracingInstance:\r
+ """Helper class to represent an instance of a tracing class."""\r
+\r
+ def __trace_call__(self, fp, fmt, *args):\r
+ fp.write((fmt+'\n') % args)\r
+\r
+ def __meta_init__(self, klass):\r
+ self.__class = klass\r
+\r
+ def __getattr__(self, name):\r
+ # Invoked for any attr not in the instance's __dict__\r
+ try:\r
+ raw = self.__class.__getattr__(name)\r
+ except AttributeError:\r
+ raise AttributeError, name\r
+ if type(raw) != types.FunctionType:\r
+ return raw\r
+ # It's a function\r
+ fullname = self.__class.__name__ + "." + name\r
+ if not self.__trace_output__ or name == '__trace_call__':\r
+ return NotTracingWrapper(fullname, raw, self)\r
+ else:\r
+ return TracingWrapper(fullname, raw, self)\r
+\r
+class NotTracingWrapper:\r
+ def __init__(self, name, func, inst):\r
+ self.__name__ = name\r
+ self.func = func\r
+ self.inst = inst\r
+ def __call__(self, *args, **kw):\r
+ return apply(self.func, (self.inst,) + args, kw)\r
+\r
+class TracingWrapper(NotTracingWrapper):\r
+ def __call__(self, *args, **kw):\r
+ self.inst.__trace_call__(self.inst.__trace_output__,\r
+ "calling %s, inst=%s, args=%s, kw=%s",\r
+ self.__name__, self.inst, args, kw)\r
+ try:\r
+ rv = apply(self.func, (self.inst,) + args, kw)\r
+ except:\r
+ t, v, tb = sys.exc_info()\r
+ self.inst.__trace_call__(self.inst.__trace_output__,\r
+ "returning from %s with exception %s: %s",\r
+ self.__name__, t, v)\r
+ raise t, v, tb\r
+ else:\r
+ self.inst.__trace_call__(self.inst.__trace_output__,\r
+ "returning from %s with value %s",\r
+ self.__name__, rv)\r
+ return rv\r
+\r
+Traced = TraceMetaClass('Traced', (), {'__trace_output__': None})\r
+\r
+\r
+def _test():\r
+ global C, D\r
+ class C(Traced):\r
+ def __init__(self, x=0): self.x = x\r
+ def m1(self, x): self.x = x\r
+ def m2(self, y): return self.x + y\r
+ __trace_output__ = sys.stdout\r
+ class D(C):\r
+ def m2(self, y): print "D.m2(%r)" % (y,); return C.m2(self, y)\r
+ __trace_output__ = None\r
+ x = C(4321)\r
+ print x\r
+ print x.x\r
+ print x.m1(100)\r
+ print x.m1(10)\r
+ print x.m2(33)\r
+ print x.m1(5)\r
+ print x.m2(4000)\r
+ print x.x\r
+\r
+ print C.__init__\r
+ print C.m2\r
+ print D.__init__\r
+ print D.m2\r
+\r
+ y = D()\r
+ print y\r
+ print y.m1(10)\r
+ print y.m2(100)\r
+ print y.x\r
+\r
+if __name__ == '__main__':\r
+ _test()\r