127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum"""Support Eiffel-style preconditions and postconditions. 227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van RossumFor example, 427e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 527e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumclass C: 627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1(self, arg): 74117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum require arg > 0 84117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum return whatever 927e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum ensure Result > arg 1027e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 1127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumcan be written (clumsily, I agree) as: 1227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 1327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumclass C(Eiffel): 1427e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1(self, arg): 154117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum return whatever 1627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1_pre(self, arg): 174117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum assert arg > 0 1827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1_post(self, Result, arg): 194117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum assert Result > arg 2027e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 2127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van RossumPre- and post-conditions for a method, being implemented as methods 2227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumthemselves, are inherited independently from the method. This gives 2327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossummuch of the same effect of Eiffel, where pre- and post-conditions are 2427e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossuminherited when a method is overridden by a derived class. However, 2527e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumwhen a derived class in Python needs to extend a pre- or 2627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumpost-condition, it must manually merge the base class' pre- or 2727e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumpost-condition with that defined in the derived class', for example: 2827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 2927e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumclass D(C): 3027e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1(self, arg): 31425a8ec05e2b14ee2738d4930b1776a01305d023Jeremy Hylton return arg**2 3227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1_post(self, Result, arg): 334117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum C.m1_post(self, Result, arg) 344117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum assert Result < 100 3527e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 3627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van RossumThis gives derived classes more freedom but also more responsibility 3727e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumthan in Eiffel, where the compiler automatically takes care of this. 3827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 3927e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van RossumIn Eiffel, pre-conditions combine using contravariance, meaning a 4027e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumderived class can only make a pre-condition weaker; in Python, this is 4127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumup to the derived class. For example, a derived class that takes away 4227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumthe requirement that arg > 0 could write: 4327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 4427e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1_pre(self, arg): 454117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum pass 4627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 4727e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumbut one could equally write a derived class that makes a stronger 4827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumrequirement: 4927e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 5027e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def m1_pre(self, arg): 514117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum require arg > 50 5227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 5327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van RossumIt would be easy to modify the classes shown here so that pre- and 5427e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumpost-conditions can be disabled (separately, on a per-class basis). 5527e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 5627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van RossumA different design would have the pre- or post-condition testing 5727e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumfunctions return true for success and false for failure. This would 5827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossummake it possible to implement automatic combination of inherited 5927e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumand new pre-/post-conditions. All this is left as an exercise to the 6027e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumreader. 6127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 6227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum""" 6327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 6427e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumfrom Meta import MetaClass, MetaHelper, MetaMethodWrapper 6527e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 6627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumclass EiffelMethodWrapper(MetaMethodWrapper): 6727e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 6827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def __init__(self, func, inst): 694117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum MetaMethodWrapper.__init__(self, func, inst) 704117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum # Note that the following causes recursive wrappers around 714117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum # the pre-/post-condition testing methods. These are harmless 724117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum # but inefficient; to avoid them, the lookup must be done 734117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum # using the class. 744117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum try: 754117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum self.pre = getattr(inst, self.__name__ + "_pre") 764117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum except AttributeError: 774117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum self.pre = None 784117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum try: 794117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum self.post = getattr(inst, self.__name__ + "_post") 804117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum except AttributeError: 814117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum self.post = None 8227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 8327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum def __call__(self, *args, **kw): 844117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum if self.pre: 854117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum apply(self.pre, args, kw) 864117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum Result = apply(self.func, (self.inst,) + args, kw) 874117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum if self.post: 884117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum apply(self.post, (Result,) + args, kw) 894117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum return Result 90e6ddc8b20b493fef2e7cffb2e1351fe1d238857eTim Peters 9127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumclass EiffelHelper(MetaHelper): 9227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum __methodwrapper__ = EiffelMethodWrapper 9327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 9427e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumclass EiffelMetaClass(MetaClass): 9527e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum __helper__ = EiffelHelper 9627e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 9727e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van RossumEiffel = EiffelMetaClass('Eiffel', (), {}) 9827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 9927e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 10027e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumdef _test(): 10127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum class C(Eiffel): 1024117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum def m1(self, arg): 1034117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum return arg+1 1044117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum def m1_pre(self, arg): 1054117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum assert arg > 0, "precondition for m1 failed" 1064117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum def m1_post(self, Result, arg): 1074117e5428bf1ff3d26d23bd77472265412473ad9Guido van Rossum assert Result > arg 10827e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum x = C() 10927e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum x.m1(12) 1100cdb88767647cc4a684cdb61b9fd9aa9a89d75cbGuido van Rossum## x.m1(-1) 11127e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum 11227e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossumif __name__ == '__main__': 11327e4aa316875b1e5ad7fd85f93707ddf4fdba063Guido van Rossum _test() 114