ยปCore Development>Code coverage>Lib/test/test_decorators.py

Python code coverage for Lib/test/test_decorators.py

#countcontent
1n/aimport unittest
2n/a
3n/adef funcattrs(**kwds):
4n/a def decorate(func):
5n/a func.__dict__.update(kwds)
6n/a return func
7n/a return decorate
8n/a
9n/aclass MiscDecorators (object):
10n/a @staticmethod
11n/a def author(name):
12n/a def decorate(func):
13n/a func.__dict__['author'] = name
14n/a return func
15n/a return decorate
16n/a
17n/a# -----------------------------------------------
18n/a
19n/aclass DbcheckError (Exception):
20n/a def __init__(self, exprstr, func, args, kwds):
21n/a # A real version of this would set attributes here
22n/a Exception.__init__(self, "dbcheck %r failed (func=%s args=%s kwds=%s)" %
23n/a (exprstr, func, args, kwds))
24n/a
25n/a
26n/adef dbcheck(exprstr, globals=None, locals=None):
27n/a "Decorator to implement debugging assertions"
28n/a def decorate(func):
29n/a expr = compile(exprstr, "dbcheck-%s" % func.__name__, "eval")
30n/a def check(*args, **kwds):
31n/a if not eval(expr, globals, locals):
32n/a raise DbcheckError(exprstr, func, args, kwds)
33n/a return func(*args, **kwds)
34n/a return check
35n/a return decorate
36n/a
37n/a# -----------------------------------------------
38n/a
39n/adef countcalls(counts):
40n/a "Decorator to count calls to a function"
41n/a def decorate(func):
42n/a func_name = func.__name__
43n/a counts[func_name] = 0
44n/a def call(*args, **kwds):
45n/a counts[func_name] += 1
46n/a return func(*args, **kwds)
47n/a call.__name__ = func_name
48n/a return call
49n/a return decorate
50n/a
51n/a# -----------------------------------------------
52n/a
53n/adef memoize(func):
54n/a saved = {}
55n/a def call(*args):
56n/a try:
57n/a return saved[args]
58n/a except KeyError:
59n/a res = func(*args)
60n/a saved[args] = res
61n/a return res
62n/a except TypeError:
63n/a # Unhashable argument
64n/a return func(*args)
65n/a call.__name__ = func.__name__
66n/a return call
67n/a
68n/a# -----------------------------------------------
69n/a
70n/aclass TestDecorators(unittest.TestCase):
71n/a
72n/a def test_single(self):
73n/a class C(object):
74n/a @staticmethod
75n/a def foo(): return 42
76n/a self.assertEqual(C.foo(), 42)
77n/a self.assertEqual(C().foo(), 42)
78n/a
79n/a def test_staticmethod_function(self):
80n/a @staticmethod
81n/a def notamethod(x):
82n/a return x
83n/a self.assertRaises(TypeError, notamethod, 1)
84n/a
85n/a def test_dotted(self):
86n/a decorators = MiscDecorators()
87n/a @decorators.author('Cleese')
88n/a def foo(): return 42
89n/a self.assertEqual(foo(), 42)
90n/a self.assertEqual(foo.author, 'Cleese')
91n/a
92n/a def test_argforms(self):
93n/a # A few tests of argument passing, as we use restricted form
94n/a # of expressions for decorators.
95n/a
96n/a def noteargs(*args, **kwds):
97n/a def decorate(func):
98n/a setattr(func, 'dbval', (args, kwds))
99n/a return func
100n/a return decorate
101n/a
102n/a args = ( 'Now', 'is', 'the', 'time' )
103n/a kwds = dict(one=1, two=2)
104n/a @noteargs(*args, **kwds)
105n/a def f1(): return 42
106n/a self.assertEqual(f1(), 42)
107n/a self.assertEqual(f1.dbval, (args, kwds))
108n/a
109n/a @noteargs('terry', 'gilliam', eric='idle', john='cleese')
110n/a def f2(): return 84
111n/a self.assertEqual(f2(), 84)
112n/a self.assertEqual(f2.dbval, (('terry', 'gilliam'),
113n/a dict(eric='idle', john='cleese')))
114n/a
115n/a @noteargs(1, 2,)
116n/a def f3(): pass
117n/a self.assertEqual(f3.dbval, ((1, 2), {}))
118n/a
119n/a def test_dbcheck(self):
120n/a @dbcheck('args[1] is not None')
121n/a def f(a, b):
122n/a return a + b
123n/a self.assertEqual(f(1, 2), 3)
124n/a self.assertRaises(DbcheckError, f, 1, None)
125n/a
126n/a def test_memoize(self):
127n/a counts = {}
128n/a
129n/a @memoize
130n/a @countcalls(counts)
131n/a def double(x):
132n/a return x * 2
133n/a self.assertEqual(double.__name__, 'double')
134n/a
135n/a self.assertEqual(counts, dict(double=0))
136n/a
137n/a # Only the first call with a given argument bumps the call count:
138n/a #
139n/a self.assertEqual(double(2), 4)
140n/a self.assertEqual(counts['double'], 1)
141n/a self.assertEqual(double(2), 4)
142n/a self.assertEqual(counts['double'], 1)
143n/a self.assertEqual(double(3), 6)
144n/a self.assertEqual(counts['double'], 2)
145n/a
146n/a # Unhashable arguments do not get memoized:
147n/a #
148n/a self.assertEqual(double([10]), [10, 10])
149n/a self.assertEqual(counts['double'], 3)
150n/a self.assertEqual(double([10]), [10, 10])
151n/a self.assertEqual(counts['double'], 4)
152n/a
153n/a def test_errors(self):
154n/a # Test syntax restrictions - these are all compile-time errors:
155n/a #
156n/a for expr in [ "1+2", "x[3]", "(1, 2)" ]:
157n/a # Sanity check: is expr is a valid expression by itself?
158n/a compile(expr, "testexpr", "exec")
159n/a
160n/a codestr = "@%s\ndef f(): pass" % expr
161n/a self.assertRaises(SyntaxError, compile, codestr, "test", "exec")
162n/a
163n/a # You can't put multiple decorators on a single line:
164n/a #
165n/a self.assertRaises(SyntaxError, compile,
166n/a "@f1 @f2\ndef f(): pass", "test", "exec")
167n/a
168n/a # Test runtime errors
169n/a
170n/a def unimp(func):
171n/a raise NotImplementedError
172n/a context = dict(nullval=None, unimp=unimp)
173n/a
174n/a for expr, exc in [ ("undef", NameError),
175n/a ("nullval", TypeError),
176n/a ("nullval.attr", AttributeError),
177n/a ("unimp", NotImplementedError)]:
178n/a codestr = "@%s\ndef f(): pass\nassert f() is None" % expr
179n/a code = compile(codestr, "test", "exec")
180n/a self.assertRaises(exc, eval, code, context)
181n/a
182n/a def test_double(self):
183n/a class C(object):
184n/a @funcattrs(abc=1, xyz="haha")
185n/a @funcattrs(booh=42)
186n/a def foo(self): return 42
187n/a self.assertEqual(C().foo(), 42)
188n/a self.assertEqual(C.foo.abc, 1)
189n/a self.assertEqual(C.foo.xyz, "haha")
190n/a self.assertEqual(C.foo.booh, 42)
191n/a
192n/a def test_order(self):
193n/a # Test that decorators are applied in the proper order to the function
194n/a # they are decorating.
195n/a def callnum(num):
196n/a """Decorator factory that returns a decorator that replaces the
197n/a passed-in function with one that returns the value of 'num'"""
198n/a def deco(func):
199n/a return lambda: num
200n/a return deco
201n/a @callnum(2)
202n/a @callnum(1)
203n/a def foo(): return 42
204n/a self.assertEqual(foo(), 2,
205n/a "Application order of decorators is incorrect")
206n/a
207n/a def test_eval_order(self):
208n/a # Evaluating a decorated function involves four steps for each
209n/a # decorator-maker (the function that returns a decorator):
210n/a #
211n/a # 1: Evaluate the decorator-maker name
212n/a # 2: Evaluate the decorator-maker arguments (if any)
213n/a # 3: Call the decorator-maker to make a decorator
214n/a # 4: Call the decorator
215n/a #
216n/a # When there are multiple decorators, these steps should be
217n/a # performed in the above order for each decorator, but we should
218n/a # iterate through the decorators in the reverse of the order they
219n/a # appear in the source.
220n/a
221n/a actions = []
222n/a
223n/a def make_decorator(tag):
224n/a actions.append('makedec' + tag)
225n/a def decorate(func):
226n/a actions.append('calldec' + tag)
227n/a return func
228n/a return decorate
229n/a
230n/a class NameLookupTracer (object):
231n/a def __init__(self, index):
232n/a self.index = index
233n/a
234n/a def __getattr__(self, fname):
235n/a if fname == 'make_decorator':
236n/a opname, res = ('evalname', make_decorator)
237n/a elif fname == 'arg':
238n/a opname, res = ('evalargs', str(self.index))
239n/a else:
240n/a assert False, "Unknown attrname %s" % fname
241n/a actions.append('%s%d' % (opname, self.index))
242n/a return res
243n/a
244n/a c1, c2, c3 = map(NameLookupTracer, [ 1, 2, 3 ])
245n/a
246n/a expected_actions = [ 'evalname1', 'evalargs1', 'makedec1',
247n/a 'evalname2', 'evalargs2', 'makedec2',
248n/a 'evalname3', 'evalargs3', 'makedec3',
249n/a 'calldec3', 'calldec2', 'calldec1' ]
250n/a
251n/a actions = []
252n/a @c1.make_decorator(c1.arg)
253n/a @c2.make_decorator(c2.arg)
254n/a @c3.make_decorator(c3.arg)
255n/a def foo(): return 42
256n/a self.assertEqual(foo(), 42)
257n/a
258n/a self.assertEqual(actions, expected_actions)
259n/a
260n/a # Test the equivalence claim in chapter 7 of the reference manual.
261n/a #
262n/a actions = []
263n/a def bar(): return 42
264n/a bar = c1.make_decorator(c1.arg)(c2.make_decorator(c2.arg)(c3.make_decorator(c3.arg)(bar)))
265n/a self.assertEqual(bar(), 42)
266n/a self.assertEqual(actions, expected_actions)
267n/a
268n/aclass TestClassDecorators(unittest.TestCase):
269n/a
270n/a def test_simple(self):
271n/a def plain(x):
272n/a x.extra = 'Hello'
273n/a return x
274n/a @plain
275n/a class C(object): pass
276n/a self.assertEqual(C.extra, 'Hello')
277n/a
278n/a def test_double(self):
279n/a def ten(x):
280n/a x.extra = 10
281n/a return x
282n/a def add_five(x):
283n/a x.extra += 5
284n/a return x
285n/a
286n/a @add_five
287n/a @ten
288n/a class C(object): pass
289n/a self.assertEqual(C.extra, 15)
290n/a
291n/a def test_order(self):
292n/a def applied_first(x):
293n/a x.extra = 'first'
294n/a return x
295n/a def applied_second(x):
296n/a x.extra = 'second'
297n/a return x
298n/a @applied_second
299n/a @applied_first
300n/a class C(object): pass
301n/a self.assertEqual(C.extra, 'second')
302n/a
303n/aif __name__ == "__main__":
304n/a unittest.main()