ยปCore Development>Code coverage>Tools/demo/eiffel.py

Python code coverage for Tools/demo/eiffel.py

#countcontent
1n/a#!/usr/bin/env python3
2n/a
3n/a"""
4n/aSupport Eiffel-style preconditions and postconditions for functions.
5n/a
6n/aAn example for Python metaclasses.
7n/a"""
8n/a
9n/aimport unittest
10n/afrom types import FunctionType as function
11n/a
12n/aclass EiffelBaseMetaClass(type):
13n/a
14n/a def __new__(meta, name, bases, dict):
15n/a meta.convert_methods(dict)
16n/a return super(EiffelBaseMetaClass, meta).__new__(
17n/a meta, name, bases, dict)
18n/a
19n/a @classmethod
20n/a def convert_methods(cls, dict):
21n/a """Replace functions in dict with EiffelMethod wrappers.
22n/a
23n/a The dict is modified in place.
24n/a
25n/a If a method ends in _pre or _post, it is removed from the dict
26n/a regardless of whether there is a corresponding method.
27n/a """
28n/a # find methods with pre or post conditions
29n/a methods = []
30n/a for k, v in dict.items():
31n/a if k.endswith('_pre') or k.endswith('_post'):
32n/a assert isinstance(v, function)
33n/a elif isinstance(v, function):
34n/a methods.append(k)
35n/a for m in methods:
36n/a pre = dict.get("%s_pre" % m)
37n/a post = dict.get("%s_post" % m)
38n/a if pre or post:
39n/a dict[m] = cls.make_eiffel_method(dict[m], pre, post)
40n/a
41n/a
42n/aclass EiffelMetaClass1(EiffelBaseMetaClass):
43n/a # an implementation of the "eiffel" meta class that uses nested functions
44n/a
45n/a @staticmethod
46n/a def make_eiffel_method(func, pre, post):
47n/a def method(self, *args, **kwargs):
48n/a if pre:
49n/a pre(self, *args, **kwargs)
50n/a rv = func(self, *args, **kwargs)
51n/a if post:
52n/a post(self, rv, *args, **kwargs)
53n/a return rv
54n/a
55n/a if func.__doc__:
56n/a method.__doc__ = func.__doc__
57n/a
58n/a return method
59n/a
60n/a
61n/aclass EiffelMethodWrapper:
62n/a
63n/a def __init__(self, inst, descr):
64n/a self._inst = inst
65n/a self._descr = descr
66n/a
67n/a def __call__(self, *args, **kwargs):
68n/a return self._descr.callmethod(self._inst, args, kwargs)
69n/a
70n/a
71n/aclass EiffelDescriptor:
72n/a
73n/a def __init__(self, func, pre, post):
74n/a self._func = func
75n/a self._pre = pre
76n/a self._post = post
77n/a
78n/a self.__name__ = func.__name__
79n/a self.__doc__ = func.__doc__
80n/a
81n/a def __get__(self, obj, cls):
82n/a return EiffelMethodWrapper(obj, self)
83n/a
84n/a def callmethod(self, inst, args, kwargs):
85n/a if self._pre:
86n/a self._pre(inst, *args, **kwargs)
87n/a x = self._func(inst, *args, **kwargs)
88n/a if self._post:
89n/a self._post(inst, x, *args, **kwargs)
90n/a return x
91n/a
92n/a
93n/aclass EiffelMetaClass2(EiffelBaseMetaClass):
94n/a # an implementation of the "eiffel" meta class that uses descriptors
95n/a
96n/a make_eiffel_method = EiffelDescriptor
97n/a
98n/a
99n/aclass Tests(unittest.TestCase):
100n/a
101n/a def testEiffelMetaClass1(self):
102n/a self._test(EiffelMetaClass1)
103n/a
104n/a def testEiffelMetaClass2(self):
105n/a self._test(EiffelMetaClass2)
106n/a
107n/a def _test(self, metaclass):
108n/a class Eiffel(metaclass=metaclass):
109n/a pass
110n/a
111n/a class Test(Eiffel):
112n/a def m(self, arg):
113n/a """Make it a little larger"""
114n/a return arg + 1
115n/a
116n/a def m2(self, arg):
117n/a """Make it a little larger"""
118n/a return arg + 1
119n/a
120n/a def m2_pre(self, arg):
121n/a assert arg > 0
122n/a
123n/a def m2_post(self, result, arg):
124n/a assert result > arg
125n/a
126n/a class Sub(Test):
127n/a def m2(self, arg):
128n/a return arg**2
129n/a
130n/a def m2_post(self, Result, arg):
131n/a super(Sub, self).m2_post(Result, arg)
132n/a assert Result < 100
133n/a
134n/a t = Test()
135n/a self.assertEqual(t.m(1), 2)
136n/a self.assertEqual(t.m2(1), 2)
137n/a self.assertRaises(AssertionError, t.m2, 0)
138n/a
139n/a s = Sub()
140n/a self.assertRaises(AssertionError, s.m2, 1)
141n/a self.assertRaises(AssertionError, s.m2, 10)
142n/a self.assertEqual(s.m2(5), 25)
143n/a
144n/a
145n/aif __name__ == "__main__":
146n/a unittest.main()