Index: Doc/lib/libfuncs.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libfuncs.tex,v retrieving revision 1.174 diff -c -r1.174 libfuncs.tex *** Doc/lib/libfuncs.tex 25 Aug 2004 10:42:39 -0000 1.174 --- Doc/lib/libfuncs.tex 26 Aug 2004 16:07:58 -0000 *************** *** 109,119 **** \begin{verbatim} class C: ! @classmethod def f(cls, arg1, arg2, ...): ... \end{verbatim} ! The \code{@classmethod} form is a function decorator -- see the description of function definitions in chapter 7 of the \citetitle[../ref/ref.html]{Python Reference Manual} for details. --- 109,120 ---- \begin{verbatim} class C: ! using: ! classmethod def f(cls, arg1, arg2, ...): ... \end{verbatim} ! The \code{using: classmethod} form is a function decorator -- see the description of function definitions in chapter 7 of the \citetitle[../ref/ref.html]{Python Reference Manual} for details. *************** *** 942,952 **** \begin{verbatim} class C: ! @staticmethod def f(arg1, arg2, ...): ... \end{verbatim} ! The \code{@staticmethod} form is a function decorator -- see the description of function definitions in chapter 7 of the \citetitle[../ref/ref.html]{Python Reference Manual} for details. --- 943,954 ---- \begin{verbatim} class C: ! using: ! staticmethod def f(arg1, arg2, ...): ... \end{verbatim} ! The \code{using: staticmethod} form is a function decorator -- see the description of function definitions in chapter 7 of the \citetitle[../ref/ref.html]{Python Reference Manual} for details. Index: Doc/ref/ref7.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/ref/ref7.tex,v retrieving revision 1.40 diff -c -r1.40 ref7.tex *** Doc/ref/ref7.tex 17 Aug 2004 17:29:13 -0000 1.40 --- Doc/ref/ref7.tex 26 Aug 2004 16:07:59 -0000 *************** *** 315,326 **** \begin{productionlist} \production{funcdef} ! {[\token{decorators}] "def" \token{funcname} "(" [\token{parameter_list}] ")" ":" \token{suite}} \production{decorators} {\token{decorator}+} \production{decorator} ! {"@" \token{dotted_name} ["(" [\token{argument_list} [","]] ")"] NEWLINE} \production{parameter_list} {(\token{defparameter} ",")*} \productioncont{("*" \token{identifier} [, "**" \token{identifier}]} --- 315,326 ---- \begin{productionlist} \production{funcdef} ! {['using' ':' (\token{decorator} | NEWLINE INDENT \token{decorators} DEDENT) ] "def" \token{funcname} "(" [\token{parameter_list}] ")" ":" \token{suite}} \production{decorators} {\token{decorator}+} \production{decorator} ! {\token{dotted_name} ["(" [\token{argument_list} [","]] ")"] NEWLINE} \production{parameter_list} {(\token{defparameter} ",")*} \productioncont{("*" \token{identifier} [, "**" \token{identifier}]} *************** *** 356,363 **** For example, the following code: \begin{verbatim} ! @f1(arg) ! @f2 def func(): pass \end{verbatim} --- 356,364 ---- For example, the following code: \begin{verbatim} ! using: ! f1(arg) ! f2 def func(): pass \end{verbatim} Index: Grammar/Grammar =================================================================== RCS file: /cvsroot/python/python/dist/src/Grammar/Grammar,v retrieving revision 1.51 diff -c -r1.51 Grammar *** Grammar/Grammar 17 Aug 2004 17:29:14 -0000 1.51 --- Grammar/Grammar 26 Aug 2004 16:08:07 -0000 *************** *** 28,36 **** file_input: (NEWLINE | stmt)* ENDMARKER eval_input: testlist NEWLINE* ENDMARKER ! decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE decorators: decorator+ ! funcdef: [decorators] 'def' NAME parameters ':' suite parameters: '(' [varargslist] ')' varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [','] fpdef: NAME | '(' fplist ')' --- 28,36 ---- file_input: (NEWLINE | stmt)* ENDMARKER eval_input: testlist NEWLINE* ENDMARKER ! decorator: dotted_name [ '(' [arglist] ')' ] NEWLINE decorators: decorator+ ! funcdef: ['using' ':' (decorator |NEWLINE INDENT decorators DEDENT) ] 'def' NAME parameters ':' suite parameters: '(' [varargslist] ')' varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [','] fpdef: NAME | '(' fplist ')' Index: Include/compile.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/compile.h,v retrieving revision 2.41 diff -c -r2.41 compile.h *** Include/compile.h 12 Feb 2004 15:28:26 -0000 2.41 --- Include/compile.h 26 Aug 2004 16:08:07 -0000 *************** *** 48,53 **** --- 48,54 ---- in effect when the code block was compiled. */ #define CO_GENERATOR_ALLOWED 0x1000 /* no longer used in an essential way */ #define CO_FUTURE_DIVISION 0x2000 + #define CO_FUTURE_DECORATORS 0x4000 PyAPI_DATA(PyTypeObject) PyCode_Type; *************** *** 80,85 **** --- 81,88 ---- #define FUTURE_NESTED_SCOPES "nested_scopes" #define FUTURE_GENERATORS "generators" #define FUTURE_DIVISION "division" + #define FUTURE_DECORATORS "decorators" + #ifdef __cplusplus } Index: Include/token.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/token.h,v retrieving revision 2.21 diff -c -r2.21 token.h *** Include/token.h 2 Aug 2004 06:09:53 -0000 2.21 --- Include/token.h 26 Aug 2004 16:08:07 -0000 *************** *** 56,67 **** #define RIGHTSHIFTEQUAL 46 #define DOUBLESTAREQUAL 47 #define DOUBLESLASH 48 ! #define DOUBLESLASHEQUAL 49 ! #define AT 50 /* Don't forget to update the table _PyParser_TokenNames in tokenizer.c! */ ! #define OP 51 ! #define ERRORTOKEN 52 ! #define N_TOKENS 53 /* Special definitions for cooperation with parser */ --- 56,66 ---- #define RIGHTSHIFTEQUAL 46 #define DOUBLESTAREQUAL 47 #define DOUBLESLASH 48 ! #define DOUBLESLASHEQUAL 49 /* Don't forget to update the table _PyParser_TokenNames in tokenizer.c! */ ! #define OP 50 ! #define ERRORTOKEN 51 ! #define N_TOKENS 52 /* Special definitions for cooperation with parser */ Index: Lib/__future__.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/__future__.py,v retrieving revision 1.13 diff -c -r1.13 __future__.py *** Lib/__future__.py 24 Aug 2001 17:13:54 -0000 1.13 --- Lib/__future__.py 26 Aug 2004 16:08:07 -0000 *************** *** 51,56 **** --- 51,57 ---- "nested_scopes", "generators", "division", + "decorators" ] __all__ = ["all_feature_names"] + all_feature_names *************** *** 62,67 **** --- 63,69 ---- CO_NESTED = 0x0010 # nested_scopes CO_GENERATOR_ALLOWED = 0x1000 # generators CO_FUTURE_DIVISION = 0x2000 # division + CO_FUTURE_DECORATORS = 0x4000 # decorators class _Feature: def __init__(self, optionalRelease, mandatoryRelease, compiler_flag): *************** *** 102,104 **** --- 104,110 ---- division = _Feature((2, 2, 0, "alpha", 2), (3, 0, 0, "alpha", 0), CO_FUTURE_DIVISION) + + decorators = _Feature((2, 4, 0, "alpha", 2), + (3, 0, 0, "alpha", 0), + CO_FUTURE_DECORATORS) Index: Lib/inspect.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/inspect.py,v retrieving revision 1.54 diff -c -r1.54 inspect.py *** Lib/inspect.py 18 Aug 2004 12:40:30 -0000 1.54 --- Lib/inspect.py 26 Aug 2004 16:08:07 -0000 *************** *** 409,415 **** lines = linecache.getlines(file) if not lines: raise IOError('could not get source code') - if ismodule(object): return lines, 0 --- 409,414 ---- *************** *** 433,439 **** if not hasattr(object, 'co_firstlineno'): raise IOError('could not find function definition') lnum = object.co_firstlineno - 1 ! pat = re.compile(r'^(\s*def\s)|(.*\slambda(:|\s))|^(\s*@)') while lnum > 0: if pat.match(lines[lnum]): break lnum = lnum - 1 --- 432,438 ---- if not hasattr(object, 'co_firstlineno'): raise IOError('could not find function definition') lnum = object.co_firstlineno - 1 ! pat = re.compile(r'^(\s*def\s)|(.*\slambda(:|\s))|^(\s*using:)') while lnum > 0: if pat.match(lines[lnum]): break lnum = lnum - 1 *************** *** 502,512 **** class BlockFinder: """Provide a tokeneater() method to detect the end of a code block.""" ! def __init__(self): self.indent = 0 self.started = 0 self.last = 0 ! def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): if not self.started: if '@' in line: pass --- 501,511 ---- class BlockFinder: """Provide a tokeneater() method to detect the end of a code block.""" ! def __init__(self, count=1): self.indent = 0 self.started = 0 self.last = 0 ! self.count = count def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): if not self.started: if '@' in line: pass *************** *** 518,536 **** elif type == tokenize.DEDENT: self.indent = self.indent - 1 if self.indent == 0: ! raise EndOfBlock, self.last elif type == tokenize.NAME and scol == 0: ! raise EndOfBlock, self.last ! def getblock(lines): """Extract the block of code at the top of the given list of lines.""" try: ! tokenize.tokenize(ListReader(lines).readline, BlockFinder().tokeneater) except EndOfBlock, eob: return lines[:eob.args[0]] # Fooling the indent/dedent logic implies a one-line definition return lines[:1] def getsourcelines(object): """Return a list of source lines and starting line number for an object. --- 517,544 ---- elif type == tokenize.DEDENT: self.indent = self.indent - 1 if self.indent == 0: ! self.count -= 1 ! if self.count == 0: ! raise EndOfBlock, self.last elif type == tokenize.NAME and scol == 0: ! if not (self.count >0): ! raise EndOfBlock, self.last ! def getblock(lines, count=1): """Extract the block of code at the top of the given list of lines.""" try: ! tokenize.tokenize(ListReader(lines).readline, BlockFinder(count).tokeneater) except EndOfBlock, eob: return lines[:eob.args[0]] # Fooling the indent/dedent logic implies a one-line definition return lines[:1] + def _isdecoratedfunction(lines,lnum): + pat = re.compile(r'^(\s*using:)') + if pat.match(lines[lnum]): + return True + return False + def getsourcelines(object): """Return a list of source lines and starting line number for an object. *************** *** 540,548 **** original source file the first line of code was found. An IOError is raised if the source code cannot be retrieved.""" lines, lnum = findsource(object) - if ismodule(object): return lines, 0 ! else: return getblock(lines[lnum:]), lnum + 1 def getsource(object): """Return the text of the source code for an object. --- 548,560 ---- original source file the first line of code was found. An IOError is raised if the source code cannot be retrieved.""" lines, lnum = findsource(object) if ismodule(object): return lines, 0 ! else: ! if _isdecoratedfunction(lines,lnum): ! blockcount = 2 ! else: ! blockcount = 1 ! return getblock(lines[lnum:],blockcount), lnum + 1 def getsource(object): """Return the text of the source code for an object. Index: Lib/token.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/token.py,v retrieving revision 1.14 diff -c -r1.14 token.py *** Lib/token.py 2 Aug 2004 06:09:53 -0000 1.14 --- Lib/token.py 26 Aug 2004 16:08:07 -0000 *************** *** 60,69 **** DOUBLESTAREQUAL = 47 DOUBLESLASH = 48 DOUBLESLASHEQUAL = 49 ! AT = 50 ! OP = 51 ! ERRORTOKEN = 52 ! N_TOKENS = 53 NT_OFFSET = 256 #--end constants-- --- 60,68 ---- DOUBLESTAREQUAL = 47 DOUBLESLASH = 48 DOUBLESLASHEQUAL = 49 ! OP = 50 ! ERRORTOKEN = 51 ! N_TOKENS = 52 NT_OFFSET = 256 #--end constants-- Index: Lib/tokenize.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/tokenize.py,v retrieving revision 1.36 diff -c -r1.36 tokenize.py *** Lib/tokenize.py 2 Aug 2004 06:09:53 -0000 1.36 --- Lib/tokenize.py 26 Aug 2004 16:08:07 -0000 *************** *** 83,89 **** r"~") Bracket = '[][(){}]' ! Special = group(r'\r?\n', r'[:;.,`@]') Funny = group(Operator, Bracket, Special) PlainToken = group(Number, Funny, String, Name) --- 83,89 ---- r"~") Bracket = '[][(){}]' ! Special = group(r'\r?\n', r'[:;.,`]') Funny = group(Operator, Bracket, Special) PlainToken = group(Number, Funny, String, Name) Index: Lib/webbrowser.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/webbrowser.py,v retrieving revision 1.37 diff -c -r1.37 webbrowser.py *** Lib/webbrowser.py 10 Jul 2004 22:07:02 -0000 1.37 --- Lib/webbrowser.py 26 Aug 2004 16:08:07 -0000 *************** *** 15,24 **** """Register a browser connector and, optionally, connection.""" _browsers[name.lower()] = [klass, instance] ! def get(using=None): """Return a browser launcher instance appropriate for the environment.""" ! if using is not None: ! alternatives = [using] else: alternatives = _tryorder for browser in alternatives: --- 15,24 ---- """Register a browser connector and, optionally, connection.""" _browsers[name.lower()] = [klass, instance] ! def get(Using=None): """Return a browser launcher instance appropriate for the environment.""" ! if Using is not None: ! alternatives = [Using] else: alternatives = _tryorder for browser in alternatives: Index: Lib/compiler/future.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/compiler/future.py,v retrieving revision 1.3 diff -c -r1.3 future.py *** Lib/compiler/future.py 18 Oct 2001 21:57:37 -0000 1.3 --- Lib/compiler/future.py 26 Aug 2004 16:08:07 -0000 *************** *** 15,21 **** class FutureParser: ! features = ("nested_scopes", "generators", "division") def __init__(self): self.found = {} # set --- 15,21 ---- class FutureParser: ! features = ("nested_scopes", "generators", "division", "decorators") def __init__(self): self.found = {} # set Index: Lib/compiler/transformer.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/compiler/transformer.py,v retrieving revision 1.43 diff -c -r1.43 transformer.py *** Lib/compiler/transformer.py 17 Aug 2004 17:29:14 -0000 1.43 --- Lib/compiler/transformer.py 26 Aug 2004 16:08:07 -0000 *************** *** 200,216 **** return item def decorator(self, nodelist): ! # '@' dotted_name [ '(' [arglist] ')' ] ! assert len(nodelist) in (3, 5, 6) ! assert nodelist[0][0] == token.AT assert nodelist[-1][0] == token.NEWLINE ! assert nodelist[1][0] == symbol.dotted_name ! funcname = self.decorator_name(nodelist[1][1:]) ! if len(nodelist) > 3: ! assert nodelist[2][0] == token.LPAR ! expr = self.com_call_function(funcname, nodelist[3]) else: expr = funcname --- 200,215 ---- return item def decorator(self, nodelist): ! # dotted_name [ '(' [arglist] ')' ] NEWLINE ! assert len(nodelist) in (2, 4, 5) assert nodelist[-1][0] == token.NEWLINE ! assert nodelist[0][0] == symbol.dotted_name ! funcname = self.decorator_name(nodelist[0][1:]) ! if len(nodelist) > 2: ! assert nodelist[1][0] == token.LPAR ! expr = self.com_call_function(funcname, nodelist[2]) # FIXME: doesn't look right else: expr = funcname *************** *** 225,237 **** return Decorators(items) def funcdef(self, nodelist): ! # -6 -5 -4 -3 -2 -1 ! # funcdef: [decorators] 'def' NAME parameters ':' suite # parameters: '(' [varargslist] ')' ! if len(nodelist) == 6: ! assert nodelist[0][0] == symbol.decorators ! decorators = self.decorators(nodelist[0][1:]) else: assert len(nodelist) == 5 decorators = None --- 224,238 ---- return Decorators(items) def funcdef(self, nodelist): ! # funcdef: ['using' ':' ( decorator | NEWLINE INDENT decorators DEDENT) ] 'def' NAME parameters ':' suite # parameters: '(' [varargslist] ')' ! if len(nodelist) == 11: ! assert nodelist[4][0] == symbol.decorators ! decorators = self.decorators(nodelist[4][1:]) ! elif len(nodelist) == 8: ! assert nodelist[2][0] == symbol.decorator ! decorators = self.decorators(nodelist[2][1:]) else: assert len(nodelist) == 5 decorators = None Index: Lib/test/pyclbr_input.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/pyclbr_input.py,v retrieving revision 1.2 diff -c -r1.2 pyclbr_input.py *** Lib/test/pyclbr_input.py 4 Aug 2004 02:36:18 -0000 1.2 --- Lib/test/pyclbr_input.py 26 Aug 2004 16:08:09 -0000 *************** *** 1,9 **** """Test cases for test_pyclbr.py""" def f(): pass class Other(object): ! @classmethod def foo(c): pass def om(self): pass --- 1,12 ---- """Test cases for test_pyclbr.py""" + from __future__ import decorators + def f(): pass class Other(object): ! using: ! classmethod def foo(c): pass def om(self): pass *************** *** 26,33 **** def m(self): pass ! @staticmethod def sm(self): pass ! @classmethod def cm(self): pass --- 29,38 ---- def m(self): pass ! using: ! staticmethod def sm(self): pass ! using: ! classmethod def cm(self): pass Index: Lib/test/test_decorators.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_decorators.py,v retrieving revision 1.6 diff -c -r1.6 test_decorators.py *** Lib/test/test_decorators.py 19 Aug 2004 03:48:24 -0000 1.6 --- Lib/test/test_decorators.py 26 Aug 2004 16:08:09 -0000 *************** *** 1,3 **** --- 1,4 ---- + from __future__ import decorators import unittest from test import test_support *************** *** 8,14 **** return decorate class MiscDecorators (object): ! @staticmethod def author(name): def decorate(func): func.__dict__['author'] = name --- 9,16 ---- return decorate class MiscDecorators (object): ! using: ! staticmethod def author(name): def decorate(func): func.__dict__['author'] = name *************** *** 72,91 **** def test_single(self): class C(object): ! @staticmethod def foo(): return 42 self.assertEqual(C.foo(), 42) self.assertEqual(C().foo(), 42) def test_staticmethod_function(self): ! @staticmethod def notamethod(x): return x self.assertRaises(TypeError, notamethod, 1) def test_dotted(self): decorators = MiscDecorators() ! @decorators.author('Cleese') def foo(): return 42 self.assertEqual(foo(), 42) self.assertEqual(foo.author, 'Cleese') --- 74,96 ---- def test_single(self): class C(object): ! using: ! staticmethod def foo(): return 42 self.assertEqual(C.foo(), 42) self.assertEqual(C().foo(), 42) def test_staticmethod_function(self): ! using: ! staticmethod def notamethod(x): return x self.assertRaises(TypeError, notamethod, 1) def test_dotted(self): decorators = MiscDecorators() ! using: ! decorators.author('Cleese') def foo(): return 42 self.assertEqual(foo(), 42) self.assertEqual(foo.author, 'Cleese') *************** *** 102,124 **** args = ( 'Now', 'is', 'the', 'time' ) kwds = dict(one=1, two=2) ! @noteargs(*args, **kwds) def f1(): return 42 self.assertEqual(f1(), 42) self.assertEqual(f1.dbval, (args, kwds)) ! @noteargs('terry', 'gilliam', eric='idle', john='cleese') def f2(): return 84 self.assertEqual(f2(), 84) self.assertEqual(f2.dbval, (('terry', 'gilliam'), dict(eric='idle', john='cleese'))) ! @noteargs(1, 2,) def f3(): pass self.assertEqual(f3.dbval, ((1, 2), {})) def test_dbcheck(self): ! @dbcheck('args[1] is not None') def f(a, b): return a + b self.assertEqual(f(1, 2), 3) --- 107,133 ---- args = ( 'Now', 'is', 'the', 'time' ) kwds = dict(one=1, two=2) ! using: ! noteargs(*args, **kwds) def f1(): return 42 self.assertEqual(f1(), 42) self.assertEqual(f1.dbval, (args, kwds)) ! using: ! noteargs('terry', 'gilliam', eric='idle', john='cleese') def f2(): return 84 self.assertEqual(f2(), 84) self.assertEqual(f2.dbval, (('terry', 'gilliam'), dict(eric='idle', john='cleese'))) ! using: ! noteargs(1, 2,) def f3(): pass self.assertEqual(f3.dbval, ((1, 2), {})) def test_dbcheck(self): ! using: ! dbcheck('args[1] is not None') def f(a, b): return a + b self.assertEqual(f(1, 2), 3) *************** *** 127,134 **** def test_memoize(self): counts = {} ! @memoize ! @countcalls(counts) def double(x): return x * 2 self.assertEqual(double.func_name, 'double') --- 136,144 ---- def test_memoize(self): counts = {} ! using: ! memoize ! countcalls(counts) def double(x): return x * 2 self.assertEqual(double.func_name, 'double') *************** *** 158,170 **** # 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") # Test runtime errors --- 168,180 ---- # Sanity check: is expr is a valid expression by itself? compile(expr, "testexpr", "exec") ! codestr = "from __future__ import decorators\nusing:\n %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, ! "from __future__ import decorators\nusing:\n f1 f2\ndef f(): pass", "test", "exec") # Test runtime errors *************** *** 176,189 **** ("nullval", TypeError), ("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) def test_double(self): class C(object): ! @funcattrs(abc=1, xyz="haha") ! @funcattrs(booh=42) def foo(self): return 42 self.assertEqual(C().foo(), 42) self.assertEqual(C.foo.abc, 1) --- 186,200 ---- ("nullval", TypeError), ("nullval.attr", AttributeError), ("unimp", NotImplementedError)]: ! codestr = "from __future__ import decorators\nusing:\n %s\ndef f(): pass\nassert f() is None" % expr code = compile(codestr, "test", "exec") self.assertRaises(exc, eval, code, context) def test_double(self): class C(object): ! using: ! funcattrs(abc=1, xyz="haha") ! funcattrs(booh=42) def foo(self): return 42 self.assertEqual(C().foo(), 42) self.assertEqual(C.foo.abc, 1) *************** *** 199,206 **** def deco(func): return lambda: num return deco ! @callnum(2) ! @callnum(1) def foo(): return 42 self.assertEqual(foo(), 2, "Application order of decorators is incorrect") --- 210,218 ---- def deco(func): return lambda: num return deco ! using: ! callnum(2) ! callnum(1) def foo(): return 42 self.assertEqual(foo(), 2, "Application order of decorators is incorrect") *************** *** 250,258 **** '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) --- 262,271 ---- 'calldec3', 'calldec2', 'calldec1' ] actions = [] ! using: ! c1.make_decorator(c1.arg) ! c2.make_decorator(c2.arg) ! c3.make_decorator(c3.arg) def foo(): return 42 self.assertEqual(foo(), 42) Index: Lib/test/test_inspect.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_inspect.py,v retrieving revision 1.16 diff -c -r1.16 test_inspect.py *** Lib/test/test_inspect.py 18 Aug 2004 12:40:31 -0000 1.16 --- Lib/test/test_inspect.py 26 Aug 2004 16:08:10 -0000 *************** *** 204,210 **** # Test for decorators as well. ! source = r""" def wrap(foo=None): def wrapper(func): return func --- 204,210 ---- # Test for decorators as well. ! source = r"""from __future__ import decorators def wrap(foo=None): def wrapper(func): return func *************** *** 216,227 **** return insteadfunc # two decorators, one with argument ! @wrap() ! @wrap(wrap) def wrapped(): pass ! @replace def gone(): pass""" --- 216,229 ---- return insteadfunc # two decorators, one with argument ! using: ! wrap() ! wrap(wrap) def wrapped(): pass ! using: ! replace def gone(): pass""" *************** *** 232,238 **** mod2 = imp.load_source("testmod3", TESTFN + "2") ! test(inspect.getsource(mod2.wrapped) == sourcerange(13, 16), "inspect.getsource(mod.wrapped)") test(inspect.getsource(mod2.gone) == sourcerange(8, 9), "inspect.getsource(mod.gone)") --- 234,240 ---- mod2 = imp.load_source("testmod3", TESTFN + "2") ! test(inspect.getsource(mod2.wrapped) == sourcerange(13, 17), "inspect.getsource(mod.wrapped)") test(inspect.getsource(mod2.gone) == sourcerange(8, 9), "inspect.getsource(mod.gone)") Index: Lib/test/test_parser.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_parser.py,v retrieving revision 1.19 diff -c -r1.19 test_parser.py *** Lib/test/test_parser.py 2 Aug 2004 06:09:54 -0000 1.19 --- Lib/test/test_parser.py 26 Aug 2004 16:08:10 -0000 *************** *** 119,130 **** self.check_suite("def f(a, b, foo=bar, *args, **kw): pass") self.check_suite("def f(a, b, foo=bar, **kw): pass") ! self.check_suite("@staticmethod\n" "def f(): pass") ! self.check_suite("@staticmethod\n" ! "@funcattrs(x, y)\n" "def f(): pass") ! self.check_suite("@funcattrs()\n" "def f(): pass") def test_import_from_statement(self): --- 119,136 ---- self.check_suite("def f(a, b, foo=bar, *args, **kw): pass") self.check_suite("def f(a, b, foo=bar, **kw): pass") ! self.check_suite("from __future__ import decorators\n" ! "using:\n" ! " staticmethod\n" "def f(): pass") ! self.check_suite("from __future__ import decorators\n" ! "using:\n" ! " staticmethod\n" ! " funcattrs(x, y)\n" "def f(): pass") ! self.check_suite("from __future__ import decorators\n" ! "using:\n" ! " funcattrs()\n" "def f(): pass") def test_import_from_statement(self): Index: Lib/test/tokenize_tests.txt =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/tokenize_tests.txt,v retrieving revision 1.2 diff -c -r1.2 tokenize_tests.txt *** Lib/test/tokenize_tests.txt 2 Aug 2004 06:09:54 -0000 1.2 --- Lib/test/tokenize_tests.txt 26 Aug 2004 16:08:10 -0000 *************** *** 173,178 **** import sys, time x = sys.modules['time'].time() ! @staticmethod def foo(): pass --- 173,179 ---- import sys, time x = sys.modules['time'].time() ! using: ! staticmethod def foo(): pass Index: Lib/test/output/test_tokenize =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/output/test_tokenize,v retrieving revision 1.9 diff -c -r1.9 test_tokenize *** Lib/test/output/test_tokenize 2 Aug 2004 06:09:54 -0000 1.9 --- Lib/test/output/test_tokenize 26 Aug 2004 16:08:11 -0000 *************** *** 645,659 **** 174,29-174,30: OP ')' 174,30-174,31: NEWLINE '\n' 175,0-175,1: NL '\n' ! 176,0-176,1: OP '@' ! 176,1-176,13: NAME 'staticmethod' ! 176,13-176,14: NEWLINE '\n' ! 177,0-177,3: NAME 'def' ! 177,4-177,7: NAME 'foo' ! 177,7-177,8: OP '(' ! 177,8-177,9: OP ')' ! 177,9-177,10: OP ':' ! 177,11-177,15: NAME 'pass' ! 177,15-177,16: NEWLINE '\n' ! 178,0-178,1: NL '\n' ! 179,0-179,0: ENDMARKER '' --- 645,663 ---- 174,29-174,30: OP ')' 174,30-174,31: NEWLINE '\n' 175,0-175,1: NL '\n' ! 176,0-176,5: NAME 'using' ! 176,5-176,6: OP ':' ! 176,6-176,7: NEWLINE '\n' ! 177,0-177,4: INDENT ' ' ! 177,4-177,16: NAME 'staticmethod' ! 177,16-177,17: NEWLINE '\n' ! 178,0-178,0: DEDENT '' ! 178,0-178,3: NAME 'def' ! 178,4-178,7: NAME 'foo' ! 178,7-178,8: OP '(' ! 178,8-178,9: OP ')' ! 178,9-178,10: OP ':' ! 178,11-178,15: NAME 'pass' ! 178,15-178,16: NEWLINE '\n' ! 179,0-179,1: NL '\n' ! 180,0-180,0: ENDMARKER '' Index: Modules/parsermodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/parsermodule.c,v retrieving revision 2.84 diff -c -r2.84 parsermodule.c *** Modules/parsermodule.c 17 Aug 2004 17:29:15 -0000 2.84 --- Modules/parsermodule.c 26 Aug 2004 16:08:16 -0000 *************** *** 824,830 **** #define validate_vbar(ch) validate_terminal(ch, VBAR, "|") #define validate_doublestar(ch) validate_terminal(ch, DOUBLESTAR, "**") #define validate_dot(ch) validate_terminal(ch, DOT, ".") - #define validate_at(ch) validate_terminal(ch, AT, "@") #define validate_name(ch, str) validate_terminal(ch, NAME, str) #define VALIDATER(n) static int validate_##n(node *tree) --- 824,829 ---- *************** *** 2364,2370 **** } /* decorator: ! * '@' dotted_name [ '(' [arglist] ')' ] NEWLINE */ static int validate_decorator(node *tree) --- 2363,2369 ---- } /* decorator: ! * dotted_name [ '(' [arglist] ')' ] NEWLINE */ static int validate_decorator(node *tree) *************** *** 2372,2388 **** int ok; int nch = NCH(tree); ok = (validate_ntype(tree, decorator) && ! (nch == 3 || nch == 5 || nch == 6) && ! validate_at(CHILD(tree, 0)) && ! validate_dotted_name(CHILD(tree, 1)) && validate_newline(RCHILD(tree, -1))); ! if (ok && nch != 3) { ! ok = (validate_lparen(CHILD(tree, 2)) && validate_rparen(RCHILD(tree, -2))); ! if (ok && nch == 6) ! ok = validate_arglist(CHILD(tree, 3)); } return ok; --- 2371,2386 ---- int ok; int nch = NCH(tree); ok = (validate_ntype(tree, decorator) && ! (nch == 2 || nch == 4 || nch == 5) && ! validate_dotted_name(CHILD(tree, 0)) && validate_newline(RCHILD(tree, -1))); ! if (ok && nch != 2) { ! ok = (validate_lparen(CHILD(tree, 1)) && validate_rparen(RCHILD(tree, -2))); ! if (ok && nch == 5) ! ok = validate_arglist(CHILD(tree, 2)); } return ok; *************** *** 2406,2428 **** /* funcdef: * ! * -6 -5 -4 -3 -2 -1 ! * [decorators] 'def' NAME parameters ':' suite */ static int validate_funcdef(node *tree) { int nch = NCH(tree); int ok = (validate_ntype(tree, funcdef) ! && ((nch == 5) || (nch == 6)) && validate_name(RCHILD(tree, -5), "def") && validate_ntype(RCHILD(tree, -4), NAME) && validate_colon(RCHILD(tree, -2)) && validate_parameters(RCHILD(tree, -3)) && validate_suite(RCHILD(tree, -1))); ! if (ok && (nch == 6)) ! ok = validate_decorators(CHILD(tree, 0)); return ok; } --- 2404,2428 ---- /* funcdef: * ! * ['using' ':' (decorator | NEWLINE INDENT decorators DEDENT) ] 'def' NAME parameters ':' suite */ static int validate_funcdef(node *tree) { int nch = NCH(tree); int ok = (validate_ntype(tree, funcdef) ! && ((nch == 5) || (nch == 11) || (nch == 8)) && validate_name(RCHILD(tree, -5), "def") && validate_ntype(RCHILD(tree, -4), NAME) && validate_colon(RCHILD(tree, -2)) && validate_parameters(RCHILD(tree, -3)) && validate_suite(RCHILD(tree, -1))); ! if (ok && (nch == 11)) ! ok = validate_decorators(CHILD(tree, 4)); ! ! if (ok && (nch == 8)) ! ok = validate_decorator(CHILD(tree, 2)); return ok; } Index: Parser/parser.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Parser/parser.c,v retrieving revision 2.22 diff -c -r2.22 parser.c *** Parser/parser.c 22 Mar 2002 23:38:11 -0000 2.22 --- Parser/parser.c 26 Aug 2004 16:08:16 -0000 *************** *** 82,87 **** --- 82,89 ---- #if 0 /* future keyword */ ps->p_generators = 0; #endif + /* future keyword */ + ps->p_decorators = 0; ps->p_tree = PyNode_New(start); if (ps->p_tree == NULL) { PyMem_DEL(ps); *************** *** 153,158 **** --- 155,166 ---- strcmp(s, "yield") == 0) break; /* not a keyword */ #endif + /* future keyword */ + if (!ps->p_decorators && + s[0] == 'u' && + strcmp(s, "using") == 0) + break; /* not a keyword */ + D(printf("It's a keyword\n")); return n - i; } *************** *** 174,180 **** return -1; } ! #if 0 /* future keyword */ static void future_hack(parser_state *ps) { --- 182,191 ---- return -1; } ! /* Reactivated the following code, changing operation from generators to decorators. ! The following looks to see if decorators are being imported from __future__ and ! sets a flag p_decorators to signify the user requested decorators */ ! #if 1 /* future keyword */ static void future_hack(parser_state *ps) { *************** *** 190,202 **** for (i = 3; i < NCH(n); i += 2) { ch = CHILD(n, i); if (NCH(ch) >= 1 && TYPE(CHILD(ch, 0)) == NAME && ! strcmp(STR(CHILD(ch, 0)), "generators") == 0) { ! ps->p_generators = 1; break; } } } ! #endif /* future keyword */ int PyParser_AddToken(register parser_state *ps, register int type, char *str, --- 201,213 ---- for (i = 3; i < NCH(n); i += 2) { ch = CHILD(n, i); if (NCH(ch) >= 1 && TYPE(CHILD(ch, 0)) == NAME && ! strcmp(STR(CHILD(ch, 0)), "decorators") == 0) { ! ps->p_decorators = 1; break; } } } ! #endif int PyParser_AddToken(register parser_state *ps, register int type, char *str, *************** *** 255,261 **** "Direct pop.\n", d->d_name, ps->p_stack.s_top->s_state)); ! #if 0 /* future keyword */ if (d->d_name[0] == 'i' && strcmp(d->d_name, "import_stmt") == 0) --- 266,272 ---- "Direct pop.\n", d->d_name, ps->p_stack.s_top->s_state)); ! #if 1 /* future keyword */ if (d->d_name[0] == 'i' && strcmp(d->d_name, "import_stmt") == 0) *************** *** 273,279 **** } if (s->s_accept) { ! #if 0 /* future keyword */ if (d->d_name[0] == 'i' && strcmp(d->d_name, "import_stmt") == 0) future_hack(ps); --- 284,290 ---- } if (s->s_accept) { ! #if 1 /* future keyword */ if (d->d_name[0] == 'i' && strcmp(d->d_name, "import_stmt") == 0) future_hack(ps); Index: Parser/parser.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Parser/parser.h,v retrieving revision 2.16 diff -c -r2.16 parser.h *** Parser/parser.h 22 Mar 2002 23:52:49 -0000 2.16 --- Parser/parser.h 26 Aug 2004 16:08:16 -0000 *************** *** 28,33 **** --- 28,34 ---- #if 0 /* future keyword */ int p_generators; /* 1 if yield is a keyword */ #endif + int p_decorators; /* 1 if using is a keyword */ } parser_state; parser_state *PyParser_New(grammar *g, int start); Index: Parser/tokenizer.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Parser/tokenizer.c,v retrieving revision 2.76 diff -c -r2.76 tokenizer.c *** Parser/tokenizer.c 4 Aug 2004 17:36:41 -0000 2.76 --- Parser/tokenizer.c 26 Aug 2004 16:08:17 -0000 *************** *** 92,98 **** "DOUBLESTAREQUAL", "DOUBLESLASH", "DOUBLESLASHEQUAL", - "AT", /* This table must match the #defines in token.h! */ "OP", "", --- 92,97 ---- *************** *** 909,915 **** case '}': return RBRACE; case '^': return CIRCUMFLEX; case '~': return TILDE; - case '@': return AT; default: return OP; } } --- 908,913 ---- Index: Python/compile.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.325 diff -c -r2.325 compile.c *** Python/compile.c 25 Aug 2004 17:19:38 -0000 2.325 --- Python/compile.c 26 Aug 2004 16:08:22 -0000 *************** *** 4203,4220 **** static void com_decorator(struct compiling *c, node *n) { ! /* decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE */ int nch = NCH(n); ! assert(nch >= 3); REQ(CHILD(n, 0), AT); REQ(RCHILD(n, -1), NEWLINE); ! com_decorator_name(c, CHILD(n, 1)); ! if (nch > 3) { ! assert(nch == 5 || nch == 6); ! REQ(CHILD(n, 2), LPAR); REQ(RCHILD(n, -2), RPAR); ! com_call_function(c, CHILD(n, 3)); } } --- 4203,4220 ---- static void com_decorator(struct compiling *c, node *n) { ! /* decorator: dotted_name [ '(' [arglist] ')' ] NEWLINE */ int nch = NCH(n); ! assert(nch >= 2); REQ(CHILD(n, 0), AT); REQ(RCHILD(n, -1), NEWLINE); ! com_decorator_name(c, CHILD(n, 0)); ! if (nch > 2) { ! assert(nch == 4 || nch == 5); ! REQ(CHILD(n, 1), LPAR); REQ(RCHILD(n, -2), RPAR); ! com_call_function(c, CHILD(n, 2)); } } *************** *** 4246,4256 **** REQ(n, funcdef); /* -6 -5 -4 -3 -2 -1 funcdef: [decorators] 'def' NAME parameters ':' suite */ ! if (NCH(n) == 6) ! ndecorators = com_decorators(c, CHILD(n, 0)); ! else ! ndecorators = 0; ndefs = com_argdefs(c, n); if (ndefs < 0) --- 4246,4264 ---- REQ(n, funcdef); /* -6 -5 -4 -3 -2 -1 funcdef: [decorators] 'def' NAME parameters ':' suite */ + /* -7 -6 -5 -4 -3 -2 -1 + funcdef: ['using' ':' (decorator | NEWLINE INDENT decorators DEDENT ) ] 'def' NAME parameters ':' suite */ + ! ! if (NCH(n) == 11 && CO_FUTURE_DECORATORS) { ! ndecorators = com_decorators(c, CHILD(n, 4)); ! } else if (NCH(n) == 8 && CO_FUTURE_DECORATORS) { ! com_decorator(c, CHILD(n, 2)); ! ndecorators = 1; ! } else { ! ndecorators = 0; ! } ndefs = com_argdefs(c, n); if (ndefs < 0) *************** *** 4258,4263 **** --- 4266,4272 ---- symtable_enter_scope(c->c_symtable, STR(RCHILD(n, -4)), TYPE(n), n->n_lineno); co = (PyObject *)icompile(n, c); + symtable_exit_scope(c->c_symtable); if (co == NULL) c->c_errors++; *************** *** 5837,5844 **** switch (TYPE(n)) { case funcdef: { char *func_name; ! if (NCH(n) == 6) ! symtable_node(st, CHILD(n, 0)); func_name = STR(RCHILD(n, -4)); symtable_add_def(st, func_name, DEF_LOCAL); symtable_default_args(st, RCHILD(n, -3)); --- 5846,5857 ---- switch (TYPE(n)) { case funcdef: { char *func_name; ! if (NCH(n) == 11 && CO_FUTURE_DECORATORS) { ! symtable_node(st, CHILD(n, 4)); ! } ! if (NCH(n) == 8 && CO_FUTURE_DECORATORS) { ! symtable_node(st, CHILD(n, 2)); ! } func_name = STR(RCHILD(n, -4)); symtable_add_def(st, func_name, DEF_LOCAL); symtable_default_args(st, RCHILD(n, -3)); *************** *** 5988,5996 **** */ case decorator: if (TYPE(n) == decorator) { ! /* decorator: '@' dotted_name [ '(' [arglist] ')' ] */ node *name, *varname; ! name = CHILD(n, 1); REQ(name, dotted_name); varname = CHILD(name, 0); REQ(varname, NAME); --- 6001,6009 ---- */ case decorator: if (TYPE(n) == decorator) { ! /* decorator: dotted_name [ '(' [arglist] ')' ] */ node *name, *varname; ! name = CHILD(n, 0); REQ(name, dotted_name); varname = CHILD(name, 0); REQ(varname, NAME); Index: Python/future.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/future.c,v retrieving revision 2.13 diff -c -r2.13 future.c *** Python/future.c 11 Dec 2002 14:04:59 -0000 2.13 --- Python/future.c 26 Aug 2004 16:08:23 -0000 *************** *** 38,43 **** --- 38,45 ---- continue; } else if (strcmp(feature, FUTURE_DIVISION) == 0) { ff->ff_features |= CO_FUTURE_DIVISION; + } else if (strcmp(feature, FUTURE_DECORATORS) == 0) { + ff->ff_features |= CO_FUTURE_DECORATORS; } else if (strcmp(feature, "braces") == 0) { PyErr_SetString(PyExc_SyntaxError, "not a chance");