test_whitespace_eater_unicode(): Make this test Python 2.1 compatible.
[python/dscho.git] / Demo / newmetaclasses / Eiffel.py
blob1dd7e6c17f04e078b672d35ab0d746deda00dd73
1 """Support Eiffel-style preconditions and postconditions."""
3 from new import function
5 class EiffelBaseMetaClass(type):
7 def __new__(meta, name, bases, dict):
8 meta.convert_methods(dict)
9 return super(EiffelBaseMetaClass, meta).__new__(meta, name, bases,
10 dict)
12 def convert_methods(cls, dict):
13 """Replace functions in dict with EiffelMethod wrappers.
15 The dict is modified in place.
17 If a method ends in _pre or _post, it is removed from the dict
18 regardless of whether there is a corresponding method.
19 """
20 # find methods with pre or post conditions
21 methods = []
22 for k, v in dict.iteritems():
23 if k.endswith('_pre') or k.endswith('_post'):
24 assert isinstance(v, function)
25 elif isinstance(v, function):
26 methods.append(k)
27 for m in methods:
28 pre = dict.get("%s_pre" % m)
29 post = dict.get("%s_post" % m)
30 if pre or post:
31 dict[k] = cls.make_eiffel_method(dict[m], pre, post)
33 convert_methods = classmethod(convert_methods)
35 class EiffelMetaClass1(EiffelBaseMetaClass):
36 # an implementation of the "eiffel" meta class that uses nested functions
38 def make_eiffel_method(func, pre, post):
39 def method(self, *args, **kwargs):
40 if pre:
41 pre(self, *args, **kwargs)
42 x = func(self, *args, **kwargs)
43 if post:
44 post(self, x, *args, **kwargs)
45 return x
47 if func.__doc__:
48 method.__doc__ = func.__doc__
50 return method
52 make_eiffel_method = staticmethod(make_eiffel_method)
54 class EiffelMethodWrapper:
56 def __init__(self, inst, descr):
57 self._inst = inst
58 self._descr = descr
60 def __call__(self, *args, **kwargs):
61 return self._descr.callmethod(self._inst, args, kwargs)
63 class EiffelDescriptor(object):
65 def __init__(self, func, pre, post):
66 self._func = func
67 self._pre = pre
68 self._post = post
70 self.__name__ = func.__name__
71 self.__doc__ = func.__doc__
73 def __get__(self, obj, cls):
74 return EiffelMethodWrapper(obj, self)
76 def callmethod(self, inst, args, kwargs):
77 if self._pre:
78 self._pre(inst, *args, **kwargs)
79 x = self._func(inst, *args, **kwargs)
80 if self._post:
81 self._post(inst, x, *args, **kwargs)
82 return x
84 class EiffelMetaClass2(EiffelBaseMetaClass):
85 # an implementation of the "eiffel" meta class that uses descriptors
87 make_eiffel_method = EiffelDescriptor
89 def _test(metaclass):
90 class Eiffel:
91 __metaclass__ = metaclass
93 class Test(Eiffel):
95 def m(self, arg):
96 """Make it a little larger"""
97 return arg + 1
99 def m2(self, arg):
100 """Make it a little larger"""
101 return arg + 1
103 def m2_pre(self, arg):
104 assert arg > 0
106 def m2_post(self, result, arg):
107 assert result > arg
109 class Sub(Test):
110 def m2(self, arg):
111 return arg**2
112 def m2_post(self, Result, arg):
113 super(Sub, self).m2_post(Result, arg)
114 assert Result < 100
116 t = Test()
117 t.m(1)
118 t.m2(1)
119 try:
120 t.m2(0)
121 except AssertionError:
122 pass
123 else:
124 assert False
126 s = Sub()
127 try:
128 s.m2(1)
129 except AssertionError:
130 pass # result == arg
131 else:
132 assert False
133 try:
134 s.m2(10)
135 except AssertionError:
136 pass # result == 100
137 else:
138 assert False
139 s.m2(5)
141 if __name__ == "__main__":
142 _test(EiffelMetaClass1)
143 _test(EiffelMetaClass2)