[Python-checkins] python/dist/src/Lib/test test_decorators.py, 1.4, 1.5

mwh at users.sourceforge.net mwh at users.sourceforge.net
Tue Aug 17 19:29:28 CEST 2004


Update of /cvsroot/python/python/dist/src/Lib/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1056/Lib/test
Modified Files:
	test_decorators.py 
Log Message:
This is Mark Russell's patch:
[ 1009560 ] Fix @decorator evaluation order
>From the description:

Changes in this patch:
- Change Grammar/Grammar to require
newlines between adjacent decorators.
- Fix order of evaluation of decorators
in the C (compile.c) and python
(Lib/compiler/pycodegen.py) compilers
- Add better order of evaluation check
to test_decorators.py (test_eval_order)
- Update the decorator documentation in
the reference manual (improve description
of evaluation order and update syntax
description)
and the comment:
Used Brett's evaluation order (see
http://mail.python.org/pipermail/python-dev/2004-August/047835.html)
(I'm checking this in for Anthony who was having problems getting SF to
talk to him)
Index: test_decorators.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/test/test_decorators.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** test_decorators.py	15 Aug 2004 07:21:25 -0000	1.4
--- test_decorators.py	17 Aug 2004 17:29:15 -0000	1.5
***************
*** 41,52 ****
 "Decorator to count calls to a function"
 def decorate(func):
! name = func.func_name
! counts[name] = 0
 def call(*args, **kwds):
! counts[name] += 1
 return func(*args, **kwds)
! # XXX: Would like to say: call.func_name = func.func_name here
! # to make nested decorators work in any order, but func_name
! # is a readonly attribute
 return call
 return decorate
--- 41,50 ----
 "Decorator to count calls to a function"
 def decorate(func):
! func_name = func.func_name
! counts[func_name] = 0
 def call(*args, **kwds):
! counts[func_name] += 1
 return func(*args, **kwds)
! call.func_name = func_name
 return call
 return decorate
***************
*** 66,69 ****
--- 64,68 ----
 # Unhashable argument
 return func(*args)
+ call.func_name = func.func_name
 return call
 
***************
*** 127,137 ****
 
 def test_memoize(self):
- # XXX: This doesn't work unless memoize is the last decorator -
- # see the comment in countcalls.
 counts = {}
 @memoize
 @countcalls(counts)
 def double(x):
 return x * 2
 
 self.assertEqual(counts, dict(double=0))
--- 126,136 ----
 
 def test_memoize(self):
 counts = {}
+ 
 @memoize
 @countcalls(counts)
 def double(x):
 return x * 2
+ self.assertEqual(double.func_name, 'double')
 
 self.assertEqual(counts, dict(double=0))
***************
*** 163,166 ****
--- 162,170 ----
 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")
+ 
 # Test runtime errors
 
***************
*** 188,205 ****
 
 def test_order(self):
! # Test that decorators are conceptually applied right-recursively;
! # that means bottom-up
! def ordercheck(num):
! def deco(func):
! return lambda: num
! return deco
 
! # Should go ordercheck(1)(ordercheck(2)(blah)) which should lead to
! # blah() == 1
! @ordercheck(1)
! @ordercheck(2)
! def blah(): pass
! self.assertEqual(blah(), 1, "decorators are meant to be applied "
! "bottom-up")
 
 def test_main():
--- 192,263 ----
 
 def test_order(self):
! class C(object):
! @staticmethod
! @funcattrs(abc=1)
! def foo(): return 42
! # This wouldn't work if staticmethod was called first
! self.assertEqual(C.foo(), 42)
! self.assertEqual(C().foo(), 42)
 
! 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
! # 4: Call the 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
! # appear in the source.
! 
! actions = []
! 
! def make_decorator(tag):
! actions.append('makedec' + tag)
! def decorate(func):
! actions.append('calldec' + tag)
! return func
! return decorate
! 
! class NameLookupTracer (object):
! def __init__(self, index):
! self.index = index
! 
! def __getattr__(self, fname):
! if fname == 'make_decorator':
! opname, res = ('evalname', make_decorator)
! elif fname == 'arg':
! opname, res = ('evalargs', str(self.index))
! else:
! assert False, "Unknown attrname %s" % fname
! actions.append('%s%d' % (opname, self.index))
! return res
! 
! c1, c2, c3 = map(NameLookupTracer, [ 1, 2, 3 ])
! 
! expected_actions = [ 'evalname1', 'evalargs1', 'makedec1',
! 'evalname2', 'evalargs2', 'makedec2',
! 'evalname3', 'evalargs3', 'makedec3',
! 'calldec3', 'calldec2', 'calldec1' ]
! 
! actions = []
! @c1.make_decorator(c1.arg)
! @c2.make_decorator(c2.arg)
! @c3.make_decorator(c3.arg)
! def foo(): return 42
! self.assertEqual(foo(), 42)
! 
! self.assertEqual(actions, expected_actions)
! 
! # Test the equivalence claim in chapter 7 of the reference manual.
! #
! actions = []
! def bar(): return 42
! 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)
 
 def test_main():


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /