2 from test
import test_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
.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
.func_name
45 def call(*args
, **kwds
):
46 counts
[func_name
] += 1
47 return func(*args
, **kwds
)
48 call
.func_name
= func_name
52 # -----------------------------------------------
66 call
.func_name
= func
.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
.func_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 test_support
.run_unittest(TestDecorators
)
306 test_support
.run_unittest(TestClassDecorators
)
308 if __name__
=="__main__":