home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Demo / metaclasses / Trace.py < prev   
Text File  |  1997-08-25  |  4KB  |  146 lines

  1. """Tracing metaclass.
  2.  
  3. XXX This is very much a work in progress.
  4.  
  5. """
  6.  
  7. import types, sys
  8.  
  9. class 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.  
  58. class 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.  
  82. class 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.  
  90. class 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.  
  109. Traced = TraceMetaClass('Traced', (), {'__trace_output__': None})
  110.  
  111.  
  112. def _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(%s)" % `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.  
  143. if __name__ == '__main__':
  144.     _test()
  145.  
  146.