1 """Support Eiffel-style preconditions and postconditions.
11 can be written (clumsily, I agree) as:
16 def m1_pre(self, arg):
18 def m1_post(self, Result, arg):
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:
32 def m1_post(self, Result, arg):
33 C.m1_post(self, Result, arg)
36 This gives derived classes more freedom but also more responsibility
37 than in Eiffel, where the compiler automatically takes care of this.
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:
44 def m1_pre(self, arg):
47 but one could equally write a derived class that makes a stronger
50 def m1_pre(self, arg):
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).
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
64 from Meta
import MetaClass
, MetaHelper
, MetaMethodWrapper
66 class EiffelMethodWrapper(MetaMethodWrapper
):
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
75 self
.pre
= getattr(inst
, self
.__name
__ + "_pre")
76 except AttributeError:
79 self
.post
= getattr(inst
, self
.__name
__ + "_post")
80 except AttributeError:
83 def __call__(self
, *args
, **kw
):
85 apply(self
.pre
, args
, kw
)
86 Result
= apply(self
.func
, (self
.inst
,) + args
, kw
)
88 apply(self
.post
, (Result
,) + args
, kw
)
91 class EiffelHelper(MetaHelper
):
92 __methodwrapper__
= EiffelMethodWrapper
94 class EiffelMetaClass(MetaClass
):
95 __helper__
= EiffelHelper
97 Eiffel
= EiffelMetaClass('Eiffel', (), {})
104 def m1_pre(self
, arg
):
105 assert arg
> 0, "precondition for m1 failed"
106 def m1_post(self
, Result
, arg
):
112 if __name__
== '__main__':