2
from test import support
6
func.__dict__.update(kwds)
10
class MiscDecorators (object):
14
func.__dict__['author'] = name
18
# -----------------------------------------------
20
class DbcheckError (Exception):
21
def __init__(self, exprstr, func, args, kwds):
22
# A real version of this would set attributes here
23
Exception.__init__(self, "dbcheck %r failed (func=%s args=%s kwds=%s)" %
24
(exprstr, func, args, kwds))
27
def dbcheck(exprstr, globals=None, locals=None):
28
"Decorator to implement debugging assertions"
30
expr = compile(exprstr, "dbcheck-%s" % func.__name__, "eval")
31
def check(*args, **kwds):
32
if not eval(expr, globals, locals):
33
raise DbcheckError(exprstr, func, args, kwds)
34
return func(*args, **kwds)
38
# -----------------------------------------------
40
def countcalls(counts):
41
"Decorator to count calls to a function"
43
func_name = func.__name__
45
def call(*args, **kwds):
46
counts[func_name] += 1
47
return func(*args, **kwds)
48
call.__name__ = func_name
52
# -----------------------------------------------
66
call.__name__ = func.__name__
69
# -----------------------------------------------
71
class TestDecorators(unittest.TestCase):
73
def test_single(self):
77
self.assertEqual(C.foo(), 42)
78
self.assertEqual(C().foo(), 42)
80
def test_staticmethod_function(self):
84
self.assertRaises(TypeError, notamethod, 1)
86
def test_dotted(self):
87
decorators = MiscDecorators()
88
@decorators.author('Cleese')
90
self.assertEqual(foo(), 42)
91
self.assertEqual(foo.author, 'Cleese')
93
def test_argforms(self):
94
# A few tests of argument passing, as we use restricted form
95
# of expressions for decorators.
97
def noteargs(*args, **kwds):
99
setattr(func, 'dbval', (args, kwds))
103
args = ( 'Now', 'is', 'the', 'time' )
104
kwds = dict(one=1, two=2)
105
@noteargs(*args, **kwds)
107
self.assertEqual(f1(), 42)
108
self.assertEqual(f1.dbval, (args, kwds))
110
@noteargs('terry', 'gilliam', eric='idle', john='cleese')
112
self.assertEqual(f2(), 84)
113
self.assertEqual(f2.dbval, (('terry', 'gilliam'),
114
dict(eric='idle', john='cleese')))
118
self.assertEqual(f3.dbval, ((1, 2), {}))
120
def test_dbcheck(self):
121
@dbcheck('args[1] is not None')
124
self.assertEqual(f(1, 2), 3)
125
self.assertRaises(DbcheckError, f, 1, None)
127
def test_memoize(self):
134
self.assertEqual(double.__name__, 'double')
136
self.assertEqual(counts, dict(double=0))
138
# Only the first call with a given argument bumps the call count:
140
self.assertEqual(double(2), 4)
141
self.assertEqual(counts['double'], 1)
142
self.assertEqual(double(2), 4)
143
self.assertEqual(counts['double'], 1)
144
self.assertEqual(double(3), 6)
145
self.assertEqual(counts['double'], 2)
147
# Unhashable arguments do not get memoized:
149
self.assertEqual(double([10]), [10, 10])
150
self.assertEqual(counts['double'], 3)
151
self.assertEqual(double([10]), [10, 10])
152
self.assertEqual(counts['double'], 4)
154
def test_errors(self):
155
# Test syntax restrictions - these are all compile-time errors:
157
for expr in [ "1+2", "x[3]", "(1, 2)" ]:
158
# Sanity check: is expr is a valid expression by itself?
159
compile(expr, "testexpr", "exec")
161
codestr = "@%s\ndef f(): pass" % expr
162
self.assertRaises(SyntaxError, compile, codestr, "test", "exec")
164
# You can't put multiple decorators on a single line:
166
self.assertRaises(SyntaxError, compile,
167
"@f1 @f2\ndef f(): pass", "test", "exec")
169
# Test runtime errors
172
raise NotImplementedError
173
context = dict(nullval=None, unimp=unimp)
175
for expr, exc in [ ("undef", NameError),
176
("nullval", TypeError),
177
("nullval.attr", AttributeError),
178
("unimp", NotImplementedError)]:
179
codestr = "@%s\ndef f(): pass\nassert f() is None" % expr
180
code = compile(codestr, "test", "exec")
181
self.assertRaises(exc, eval, code, context)
183
def test_double(self):
185
@funcattrs(abc=1, xyz="haha")
187
def foo(self): return 42
188
self.assertEqual(C().foo(), 42)
189
self.assertEqual(C.foo.abc, 1)
190
self.assertEqual(C.foo.xyz, "haha")
191
self.assertEqual(C.foo.booh, 42)
193
def test_order(self):
194
# Test that decorators are applied in the proper order to the function
195
# they are decorating.
197
"""Decorator factory that returns a decorator that replaces the
198
passed-in function with one that returns the value of 'num'"""
205
self.assertEqual(foo(), 2,
206
"Application order of decorators is incorrect")
208
def test_eval_order(self):
209
# Evaluating a decorated function involves four steps for each
210
# decorator-maker (the function that returns a decorator):
212
# 1: Evaluate the decorator-maker name
213
# 2: Evaluate the decorator-maker arguments (if any)
214
# 3: Call the decorator-maker to make a decorator
215
# 4: Call the decorator
217
# When there are multiple decorators, these steps should be
218
# performed in the above order for each decorator, but we should
219
# iterate through the decorators in the reverse of the order they
220
# appear in the source.
224
def make_decorator(tag):
225
actions.append('makedec' + tag)
227
actions.append('calldec' + tag)
231
class NameLookupTracer (object):
232
def __init__(self, index):
235
def __getattr__(self, fname):
236
if fname == 'make_decorator':
237
opname, res = ('evalname', make_decorator)
239
opname, res = ('evalargs', str(self.index))
241
assert False, "Unknown attrname %s" % fname
242
actions.append('%s%d' % (opname, self.index))
245
c1, c2, c3 = map(NameLookupTracer, [ 1, 2, 3 ])
247
expected_actions = [ 'evalname1', 'evalargs1', 'makedec1',
248
'evalname2', 'evalargs2', 'makedec2',
249
'evalname3', 'evalargs3', 'makedec3',
250
'calldec3', 'calldec2', 'calldec1' ]
253
@c1.make_decorator(c1.arg)
254
@c2.make_decorator(c2.arg)
255
@c3.make_decorator(c3.arg)
257
self.assertEqual(foo(), 42)
259
self.assertEqual(actions, expected_actions)
261
# Test the equivalence claim in chapter 7 of the reference manual.
265
bar = c1.make_decorator(c1.arg)(c2.make_decorator(c2.arg)(c3.make_decorator(c3.arg)(bar)))
266
self.assertEqual(bar(), 42)
267
self.assertEqual(actions, expected_actions)
269
class TestClassDecorators(unittest.TestCase):
271
def test_simple(self):
276
class C(object): pass
277
self.assertEqual(C.extra, 'Hello')
279
def test_double(self):
289
class C(object): pass
290
self.assertEqual(C.extra, 15)
292
def test_order(self):
293
def applied_first(x):
296
def applied_second(x):
301
class C(object): pass
302
self.assertEqual(C.extra, 'second')
305
support.run_unittest(TestDecorators)
306
support.run_unittest(TestClassDecorators)
308
if __name__=="__main__":