| 
									
										
										
										
											2002-07-11 21:08:06 +00:00
										 |  |  | """Support Eiffel-style preconditions and postconditions.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from new import function | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EiffelBaseMetaClass(type): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-12 15:42:10 +00:00
										 |  |  |     def __new__(meta, name, bases, dict): | 
					
						
							|  |  |  |         meta.convert_methods(dict) | 
					
						
							|  |  |  |         return super(EiffelBaseMetaClass, meta).__new__(meta, name, bases, | 
					
						
							|  |  |  |                                                         dict) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-11 21:08:06 +00:00
										 |  |  |     def convert_methods(cls, dict): | 
					
						
							|  |  |  |         """Replace functions in dict with EiffelMethod wrappers.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The dict is modified in place. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If a method ends in _pre or _post, it is removed from the dict | 
					
						
							|  |  |  |         regardless of whether there is a corresponding method. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # find methods with pre or post conditions | 
					
						
							|  |  |  |         methods = [] | 
					
						
							|  |  |  |         for k, v in dict.iteritems(): | 
					
						
							|  |  |  |             if k.endswith('_pre') or k.endswith('_post'): | 
					
						
							|  |  |  |                 assert isinstance(v, function) | 
					
						
							|  |  |  |             elif isinstance(v, function): | 
					
						
							|  |  |  |                 methods.append(k) | 
					
						
							|  |  |  |         for m in methods: | 
					
						
							|  |  |  |             pre = dict.get("%s_pre" % m) | 
					
						
							|  |  |  |             post = dict.get("%s_post" % m) | 
					
						
							|  |  |  |             if pre or post: | 
					
						
							|  |  |  |                 dict[k] = cls.make_eiffel_method(dict[m], pre, post) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     convert_methods = classmethod(convert_methods) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-11 21:17:26 +00:00
										 |  |  | class EiffelMetaClass1(EiffelBaseMetaClass): | 
					
						
							|  |  |  |     # an implementation of the "eiffel" meta class that uses nested functions | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-11 21:08:06 +00:00
										 |  |  |     def make_eiffel_method(func, pre, post): | 
					
						
							|  |  |  |         def method(self, *args, **kwargs): | 
					
						
							|  |  |  |             if pre: | 
					
						
							|  |  |  |                 pre(self, *args, **kwargs) | 
					
						
							|  |  |  |             x = func(self, *args, **kwargs) | 
					
						
							|  |  |  |             if post: | 
					
						
							|  |  |  |                 post(self, x, *args, **kwargs) | 
					
						
							|  |  |  |             return x | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if func.__doc__: | 
					
						
							|  |  |  |             method.__doc__ = func.__doc__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return method | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     make_eiffel_method = staticmethod(make_eiffel_method) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EiffelMethodWrapper: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, inst, descr): | 
					
						
							|  |  |  |         self._inst = inst | 
					
						
							|  |  |  |         self._descr = descr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __call__(self, *args, **kwargs): | 
					
						
							|  |  |  |         return self._descr.callmethod(self._inst, args, kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EiffelDescriptor(object): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, func, pre, post): | 
					
						
							|  |  |  |         self._func = func | 
					
						
							|  |  |  |         self._pre = pre | 
					
						
							|  |  |  |         self._post = post | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.__name__ = func.__name__ | 
					
						
							|  |  |  |         self.__doc__ = func.__doc__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __get__(self, obj, cls): | 
					
						
							|  |  |  |         return EiffelMethodWrapper(obj, self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def callmethod(self, inst, args, kwargs): | 
					
						
							|  |  |  |         if self._pre: | 
					
						
							|  |  |  |             self._pre(inst, *args, **kwargs) | 
					
						
							|  |  |  |         x = self._func(inst, *args, **kwargs) | 
					
						
							|  |  |  |         if self._post: | 
					
						
							|  |  |  |             self._post(inst, x, *args, **kwargs) | 
					
						
							|  |  |  |         return x | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-12 15:42:10 +00:00
										 |  |  | class EiffelMetaClass2(EiffelBaseMetaClass): | 
					
						
							| 
									
										
										
										
											2002-07-11 21:08:06 +00:00
										 |  |  |     # an implementation of the "eiffel" meta class that uses descriptors | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     make_eiffel_method = EiffelDescriptor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _test(metaclass): | 
					
						
							|  |  |  |     class Eiffel: | 
					
						
							|  |  |  |         __metaclass__ = metaclass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class Test(Eiffel): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def m(self, arg): | 
					
						
							|  |  |  |             """Make it a little larger""" | 
					
						
							|  |  |  |             return arg + 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def m2(self, arg): | 
					
						
							|  |  |  |             """Make it a little larger""" | 
					
						
							|  |  |  |             return arg + 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def m2_pre(self, arg): | 
					
						
							|  |  |  |             assert arg > 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def m2_post(self, result, arg): | 
					
						
							|  |  |  |             assert result > arg | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class Sub(Test): | 
					
						
							|  |  |  |         def m2(self, arg): | 
					
						
							|  |  |  |             return arg**2 | 
					
						
							|  |  |  |         def m2_post(self, Result, arg): | 
					
						
							|  |  |  |             super(Sub, self).m2_post(Result, arg) | 
					
						
							|  |  |  |             assert Result < 100 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     t = Test() | 
					
						
							|  |  |  |     t.m(1) | 
					
						
							|  |  |  |     t.m2(1) | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         t.m2(0) | 
					
						
							|  |  |  |     except AssertionError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         assert False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s = Sub() | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         s.m2(1) | 
					
						
							|  |  |  |     except AssertionError: | 
					
						
							|  |  |  |         pass # result == arg | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         assert False | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         s.m2(10) | 
					
						
							|  |  |  |     except AssertionError: | 
					
						
							|  |  |  |         pass # result ==  100 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         assert False | 
					
						
							| 
									
										
										
										
											2002-07-11 21:09:34 +00:00
										 |  |  |     s.m2(5) | 
					
						
							| 
									
										
										
										
											2002-07-11 21:08:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     _test(EiffelMetaClass1) | 
					
						
							|  |  |  |     _test(EiffelMetaClass2) | 
					
						
							|  |  |  | 
 |