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