/Demo/metaclasses/Trace.py

http://unladen-swallow.googlecode.com/ · Python · 144 lines · 106 code · 20 blank · 18 comment · 18 complexity · b0416f70ed0cff68471a9bbfb88f99a6 MD5 · raw file

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