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