1- """Support Eiffel-style preconditions and postconditions."""
1+ #!/usr/bin/env python3
22
3+ """
4+ Support Eiffel-style preconditions and postconditions for functions.
5+
6+ An example for Python metaclasses.
7+ """
8+
9+ import unittest
310from types import FunctionType as function
411
512class EiffelBaseMetaClass (type ):
613
714 def __new__ (meta , name , bases , dict ):
815 meta .convert_methods (dict )
9- return super (EiffelBaseMetaClass , meta ).__new__ (meta , name , bases ,
10- dict )
16+ return super (EiffelBaseMetaClass , meta ).__new__ (
17+ meta , name , bases , dict )
1118
1219 @classmethod
1320 def convert_methods (cls , dict ):
@@ -31,6 +38,7 @@ def convert_methods(cls, dict):
3138 if pre or post :
3239 dict [k ] = cls .make_eiffel_method (dict [m ], pre , post )
3340
41+
3442class EiffelMetaClass1 (EiffelBaseMetaClass ):
3543 # an implementation of the "eiffel" meta class that uses nested functions
3644
@@ -39,16 +47,17 @@ def make_eiffel_method(func, pre, post):
3947 def method (self , * args , ** kwargs ):
4048 if pre :
4149 pre (self , * args , ** kwargs )
42- x = func (self , * args , ** kwargs )
50+ rv = func (self , * args , ** kwargs )
4351 if post :
44- post (self , x , * args , ** kwargs )
45- return x
52+ post (self , rv , * args , ** kwargs )
53+ return rv
4654
4755 if func .__doc__ :
4856 method .__doc__ = func .__doc__
4957
5058 return method
5159
60+
5261class EiffelMethodWrapper :
5362
5463 def __init__ (self , inst , descr ):
@@ -58,7 +67,8 @@ def __init__(self, inst, descr):
5867 def __call__ (self , * args , ** kwargs ):
5968 return self ._descr .callmethod (self ._inst , args , kwargs )
6069
61- class EiffelDescriptor (object ):
70+
71+ class EiffelDescriptor :
6272
6373 def __init__ (self , func , pre , post ):
6474 self ._func = func
@@ -79,63 +89,58 @@ def callmethod(self, inst, args, kwargs):
7989 self ._post (inst , x , * args , ** kwargs )
8090 return x
8191
92+
8293class EiffelMetaClass2 (EiffelBaseMetaClass ):
8394 # an implementation of the "eiffel" meta class that uses descriptors
8495
8596 make_eiffel_method = EiffelDescriptor
8697
87- def _test (metaclass ):
88- class Eiffel (metaclass = metaclass ):
89- pass
90-
91- class Test (Eiffel ):
92-
93- def m (self , arg ):
94- """Make it a little larger"""
95- return arg + 1
96-
97- def m2 (self , arg ):
98- """Make it a little larger"""
99- return arg + 1
100-
101- def m2_pre (self , arg ):
102- assert arg > 0
103-
104- def m2_post (self , result , arg ):
105- assert result > arg
106-
107- class Sub (Test ):
108- def m2 (self , arg ):
109- return arg ** 2
110- def m2_post (self , Result , arg ):
111- super (Sub , self ).m2_post (Result , arg )
112- assert Result < 100
113-
114- t = Test ()
115- t .m (1 )
116- t .m2 (1 )
117- try :
118- t .m2 (0 )
119- except AssertionError :
120- pass
121- else :
122- assert False
123-
124- s = Sub ()
125- try :
126- s .m2 (1 )
127- except AssertionError :
128- pass # result == arg
129- else :
130- assert False
131- try :
132- s .m2 (10 )
133- except AssertionError :
134- pass # result == 100
135- else :
136- assert False
137- s .m2 (5 )
98+
99+ class Tests (unittest .TestCase ):
100+
101+ def testEiffelMetaClass1 (self ):
102+ self ._test (EiffelMetaClass1 )
103+
104+ def testEiffelMetaClass2 (self ):
105+ self ._test (EiffelMetaClass2 )
106+
107+ def _test (self , metaclass ):
108+ class Eiffel (metaclass = metaclass ):
109+ pass
110+
111+ class Test (Eiffel ):
112+ def m (self , arg ):
113+ """Make it a little larger"""
114+ return arg + 1
115+
116+ def m2 (self , arg ):
117+ """Make it a little larger"""
118+ return arg + 1
119+
120+ def m2_pre (self , arg ):
121+ assert arg > 0
122+
123+ def m2_post (self , result , arg ):
124+ assert result > arg
125+
126+ class Sub (Test ):
127+ def m2 (self , arg ):
128+ return arg ** 2
129+
130+ def m2_post (self , Result , arg ):
131+ super (Sub , self ).m2_post (Result , arg )
132+ assert Result < 100
133+
134+ t = Test ()
135+ self .assertEqual (t .m (1 ), 2 )
136+ self .assertEqual (t .m2 (1 ), 2 )
137+ self .assertRaises (AssertionError , t .m2 , 0 )
138+
139+ s = Sub ()
140+ self .assertRaises (AssertionError , s .m2 , 1 )
141+ self .assertRaises (AssertionError , s .m2 , 10 )
142+ self .assertEqual (s .m2 (5 ), 25 )
143+
138144
139145if __name__ == "__main__" :
140- _test (EiffelMetaClass1 )
141- _test (EiffelMetaClass2 )
146+ unittest .main ()
0 commit comments