from test
import test_support
func
.__dict
__.update(kwds
)
class MiscDecorators (object):
func
.__dict
__['author'] = name
# -----------------------------------------------
class DbcheckError (Exception):
def __init__(self
, exprstr
, func
, args
, kwds
):
# A real version of this would set attributes here
Exception.__init
__(self
, "dbcheck %r failed (func=%s args=%s kwds=%s)" %
(exprstr
, func
, args
, kwds
))
def dbcheck(exprstr
, globals=None, locals=None):
"Decorator to implement debugging assertions"
expr
= compile(exprstr
, "dbcheck-%s" % func
.func_name
, "eval")
def check(*args
, **kwds
):
if not eval(expr
, globals, locals):
raise DbcheckError(exprstr
, func
, args
, kwds
)
return func(*args
, **kwds
)
# -----------------------------------------------
"Decorator to count calls to a function"
func_name
= func
.func_name
return func(*args
, **kwds
)
call
.func_name
= func_name
# -----------------------------------------------
call
.func_name
= func
.func_name
# -----------------------------------------------
class TestDecorators(unittest
.TestCase
):
self
.assertEqual(C
.foo(), 42)
self
.assertEqual(C().foo(), 42)
def test_staticmethod_function(self
):
self
.assertRaises(TypeError, notamethod
, 1)
decorators
= MiscDecorators()
@decorators.author('Cleese')
self
.assertEqual(foo(), 42)
self
.assertEqual(foo
.author
, 'Cleese')
# A few tests of argument passing, as we use restricted form
# of expressions for decorators.
def noteargs(*args
, **kwds
):
setattr(func
, 'dbval', (args
, kwds
))
args
= ( 'Now', 'is', 'the', 'time' )
kwds
= dict(one
=1, two
=2)
self
.assertEqual(f1(), 42)
self
.assertEqual(f1
.dbval
, (args
, kwds
))
@noteargs('terry', 'gilliam', eric
='idle', john
='cleese')
self
.assertEqual(f2(), 84)
self
.assertEqual(f2
.dbval
, (('terry', 'gilliam'),
dict(eric
='idle', john
='cleese')))
self
.assertEqual(f3
.dbval
, ((1, 2), {}))
@dbcheck('args[1] is not None')
self
.assertEqual(f(1, 2), 3)
self
.assertRaises(DbcheckError
, f
, 1, None)
self
.assertEqual(double
.func_name
, 'double')
self
.assertEqual(counts
, dict(double
=0))
# Only the first call with a given argument bumps the call count:
self
.assertEqual(double(2), 4)
self
.assertEqual(counts
['double'], 1)
self
.assertEqual(double(2), 4)
self
.assertEqual(counts
['double'], 1)
self
.assertEqual(double(3), 6)
self
.assertEqual(counts
['double'], 2)
# Unhashable arguments do not get memoized:
self
.assertEqual(double([10]), [10, 10])
self
.assertEqual(counts
['double'], 3)
self
.assertEqual(double([10]), [10, 10])
self
.assertEqual(counts
['double'], 4)
# Test syntax restrictions - these are all compile-time errors:
for expr
in [ "1+2", "x[3]", "(1, 2)" ]:
# Sanity check: is expr is a valid expression by itself?
compile(expr
, "testexpr", "exec")
codestr
= "@%s\ndef f(): pass" % expr
self
.assertRaises(SyntaxError, compile, codestr
, "test", "exec")
# You can't put multiple decorators on a single line:
self
.assertRaises(SyntaxError, compile,
"@f1 @f2\ndef f(): pass", "test", "exec")
raise NotImplementedError
context
= dict(nullval
=None, unimp
=unimp
)
for expr
, exc
in [ ("undef", NameError),
("nullval.attr", AttributeError),
("unimp", NotImplementedError)]:
codestr
= "@%s\ndef f(): pass\nassert f() is None" % expr
code
= compile(codestr
, "test", "exec")
self
.assertRaises(exc
, eval, code
, context
)
@funcattrs(abc
=1, xyz
="haha")
self
.assertEqual(C().foo(), 42)
self
.assertEqual(C
.foo
.abc
, 1)
self
.assertEqual(C
.foo
.xyz
, "haha")
self
.assertEqual(C
.foo
.booh
, 42)
# Test that decorators are applied in the proper order to the function
"""Decorator factory that returns a decorator that replaces the
passed-in function with one that returns the value of 'num'"""
self
.assertEqual(foo(), 2,
"Application order of decorators is incorrect")
def test_eval_order(self
):
# Evaluating a decorated function involves four steps for each
# decorator-maker (the function that returns a decorator):
# 1: Evaluate the decorator-maker name
# 2: Evaluate the decorator-maker arguments (if any)
# 3: Call the decorator-maker to make a decorator
# When there are multiple decorators, these steps should be
# performed in the above order for each decorator, but we should
# iterate through the decorators in the reverse of the order they
actions
.append('makedec' + tag
)
actions
.append('calldec' + tag
)
class NameLookupTracer (object):
def __init__(self
, index
):
def __getattr__(self
, fname
):
if fname
== 'make_decorator':
opname
, res
= ('evalname', make_decorator
)
opname
, res
= ('evalargs', str(self
.index
))
assert False, "Unknown attrname %s" % fname
actions
.append('%s%d' % (opname
, self
.index
))
c1
, c2
, c3
= map(NameLookupTracer
, [ 1, 2, 3 ])
expected_actions
= [ 'evalname1', 'evalargs1', 'makedec1',
'evalname2', 'evalargs2', 'makedec2',
'evalname3', 'evalargs3', 'makedec3',
'calldec3', 'calldec2', 'calldec1' ]
@c1.make_decorator(c1
.arg
)
@c2.make_decorator(c2
.arg
)
@c3.make_decorator(c3
.arg
)
self
.assertEqual(foo(), 42)
self
.assertEqual(actions
, expected_actions
)
# Test the equivalence claim in chapter 7 of the reference manual.
bar
= c1
.make_decorator(c1
.arg
)(c2
.make_decorator(c2
.arg
)(c3
.make_decorator(c3
.arg
)(bar
)))
self
.assertEqual(bar(), 42)
self
.assertEqual(actions
, expected_actions
)
test_support
.run_unittest(TestDecorators
)