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