symbian-qemu-0.9.1-12/python-2.6.1/Demo/metaclasses/Eiffel.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """Support Eiffel-style preconditions and postconditions.
       
     2 
       
     3 For example,
       
     4 
       
     5 class C:
       
     6     def m1(self, arg):
       
     7         require arg > 0
       
     8         return whatever
       
     9         ensure Result > arg
       
    10 
       
    11 can be written (clumsily, I agree) as:
       
    12 
       
    13 class C(Eiffel):
       
    14     def m1(self, arg):
       
    15         return whatever
       
    16     def m1_pre(self, arg):
       
    17         assert arg > 0
       
    18     def m1_post(self, Result, arg):
       
    19         assert Result > arg
       
    20 
       
    21 Pre- and post-conditions for a method, being implemented as methods
       
    22 themselves, are inherited independently from the method.  This gives
       
    23 much of the same effect of Eiffel, where pre- and post-conditions are
       
    24 inherited when a method is overridden by a derived class.  However,
       
    25 when a derived class in Python needs to extend a pre- or
       
    26 post-condition, it must manually merge the base class' pre- or
       
    27 post-condition with that defined in the derived class', for example:
       
    28 
       
    29 class D(C):
       
    30     def m1(self, arg):
       
    31         return arg**2
       
    32     def m1_post(self, Result, arg):
       
    33         C.m1_post(self, Result, arg)
       
    34         assert Result < 100
       
    35 
       
    36 This gives derived classes more freedom but also more responsibility
       
    37 than in Eiffel, where the compiler automatically takes care of this.
       
    38 
       
    39 In Eiffel, pre-conditions combine using contravariance, meaning a
       
    40 derived class can only make a pre-condition weaker; in Python, this is
       
    41 up to the derived class.  For example, a derived class that takes away
       
    42 the requirement that arg > 0 could write:
       
    43 
       
    44     def m1_pre(self, arg):
       
    45         pass
       
    46 
       
    47 but one could equally write a derived class that makes a stronger
       
    48 requirement:
       
    49 
       
    50     def m1_pre(self, arg):
       
    51         require arg > 50
       
    52 
       
    53 It would be easy to modify the classes shown here so that pre- and
       
    54 post-conditions can be disabled (separately, on a per-class basis).
       
    55 
       
    56 A different design would have the pre- or post-condition testing
       
    57 functions return true for success and false for failure.  This would
       
    58 make it possible to implement automatic combination of inherited
       
    59 and new pre-/post-conditions.  All this is left as an exercise to the
       
    60 reader.
       
    61 
       
    62 """
       
    63 
       
    64 from Meta import MetaClass, MetaHelper, MetaMethodWrapper
       
    65 
       
    66 class EiffelMethodWrapper(MetaMethodWrapper):
       
    67 
       
    68     def __init__(self, func, inst):
       
    69         MetaMethodWrapper.__init__(self, func, inst)
       
    70         # Note that the following causes recursive wrappers around
       
    71         # the pre-/post-condition testing methods.  These are harmless
       
    72         # but inefficient; to avoid them, the lookup must be done
       
    73         # using the class.
       
    74         try:
       
    75             self.pre = getattr(inst, self.__name__ + "_pre")
       
    76         except AttributeError:
       
    77             self.pre = None
       
    78         try:
       
    79             self.post = getattr(inst, self.__name__ + "_post")
       
    80         except AttributeError:
       
    81             self.post = None
       
    82 
       
    83     def __call__(self, *args, **kw):
       
    84         if self.pre:
       
    85             apply(self.pre, args, kw)
       
    86         Result = apply(self.func, (self.inst,) + args, kw)
       
    87         if self.post:
       
    88             apply(self.post, (Result,) + args, kw)
       
    89         return Result
       
    90 
       
    91 class EiffelHelper(MetaHelper):
       
    92     __methodwrapper__ = EiffelMethodWrapper
       
    93 
       
    94 class EiffelMetaClass(MetaClass):
       
    95     __helper__ = EiffelHelper
       
    96 
       
    97 Eiffel = EiffelMetaClass('Eiffel', (), {})
       
    98 
       
    99 
       
   100 def _test():
       
   101     class C(Eiffel):
       
   102         def m1(self, arg):
       
   103             return arg+1
       
   104         def m1_pre(self, arg):
       
   105             assert arg > 0, "precondition for m1 failed"
       
   106         def m1_post(self, Result, arg):
       
   107             assert Result > arg
       
   108     x = C()
       
   109     x.m1(12)
       
   110 ##    x.m1(-1)
       
   111 
       
   112 if __name__ == '__main__':
       
   113     _test()