summaryrefslogtreecommitdiffstats
path: root/Demo/metaclasses/Trace.py
blob: ed3944f2b9818142f4799768a2af64027511f2f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""Tracing metaclass."""

import types

class TraceMetaClass:
    """Metaclass for tracing.

    Classes defined using this metaclass have an automatic tracing
    feature -- by setting the __trace_output__ instance (or class)
    variable to a file object, trace messages about all calls are
    written to the file.  The trace formatting can be changed by
    defining a suitable __trace_call__ method.

    """

    __inited = 0

    def __init__(self, name, bases, dict):
	self.__name__ = name
	self.__bases__ = bases
	self.__dict = dict
	# XXX Can't define __dict__, alas
	self.__inited = 1

    def __getattr__(self, name):
	try:
	    return self.__dict[name]
	except KeyError:
	    for base in self.__bases__:
		try:
		    return getattr(base, name)
		except AttributeError:
		    pass
	    raise AttributeError, name

    def __setattr__(self, name, value):
	if not self.__inited:
	    self.__dict__[name] = value
	else:
	    self.__dict[name] = value

    def __call__(self, *args, **kw):
	inst = TracingInstance()
	inst.__meta_init__(self)
	try:
	    init = inst.__getattr__('__init__')
	except AttributeError:
	    init = lambda: None
	apply(init, args, kw)
	return inst

    __trace_output__ = None

class TracingInstance:
    """Helper class to represent an instance of a tracing class."""

    def __trace_call__(self, fp, fmt, *args):
	fp.write((fmt+'\n') % args)

    def __meta_init__(self, klass):
	self.__class = klass

    def __getattr__(self, name):
	# Invoked for any attr not in the instance's __dict__
	try:
	    raw = self.__class.__getattr__(name)
	except AttributeError:
	    raise AttributeError, name
	if type(raw) != types.FunctionType:
	    return raw
	# It's a function
	fullname = self.__class.__name__ + "." + name
	if not self.__trace_output__ or name == '__trace_call__':
	    return NotTracingWrapper(fullname, raw, self)
	else:
	    return TracingWrapper(fullname, raw, self)

class NotTracingWrapper:
    def __init__(self, name, func, inst):
	self.__name__ = name
	self.func = func
	self.inst = inst
    def __call__(self, *args, **kw):
	return apply(self.func, (self.inst,) + args, kw)

class TracingWrapper(NotTracingWrapper):
    def __call__(self, *args, **kw):
	self.inst.__trace_call__(self.inst.__trace_output__,
				 "calling %s, inst=%s, args=%s, kw=%s",
				 self.__name__, self.inst, args, kw)
	try:
	    rv = apply(self.func, (self.inst,) + args, kw)
	except:
	    t, v, tb = sys.exc_info()
	    self.inst.__trace_call__(self.inst.__trace_output__,
				     "returning from %s with exception %s: %s",
				     self.__name__, t, v)
	    raise t, v, tb
	else:
	    self.inst.__trace_call__(self.inst.__trace_output__,
				     "returning from %s with value %s",
				     self.__name__, rv)
	    return rv

Traced = TraceMetaClass('Traced', (), {'__trace_output__': None})


def _test():
    import sys
    class C(Traced):
	def __init__(self, x=0): self.x = x
	def m1(self, x): self.x = x
	def m2(self, y): return self.x + y
    C.__trace_output__ = sys.stdout
    x = C(4321)
    print x
    print x.x
    print x.m1(100)
    print x.m1(10)
    print x.m2(33)
    print x.m1(5)
    print x.m2(4000)
    print x.x

if __name__ == '__main__':
    _test()