diff -r 1e79ca2bc494 Doc/reference/compound_stmts.rst --- a/Doc/reference/compound_stmts.rst Wed Nov 20 11:53:31 2013 -0800 +++ b/Doc/reference/compound_stmts.rst Wed Nov 20 20:56:18 2013 -0500 @@ -442,7 +442,7 @@ .. productionlist:: funcdef: [`decorators`] "def" `funcname` "(" [`parameter_list`] ")" ["->" `expression`] ":" `suite` decorators: `decorator`+ - decorator: "@" `dotted_name` ["(" [`parameter_list` [","]] ")"] NEWLINE + decorator: "@" `expression` NEWLINE dotted_name: `identifier` ("." `identifier`)* parameter_list: (`defparameter` ",")* : ( "*" [`parameter`] ("," `defparameter`)* ["," "**" `parameter`] diff -r 1e79ca2bc494 Grammar/Grammar --- a/Grammar/Grammar Wed Nov 20 11:53:31 2013 -0800 +++ b/Grammar/Grammar Wed Nov 20 20:56:18 2013 -0500 @@ -19,7 +19,7 @@ file_input: (NEWLINE | stmt)* ENDMARKER eval_input: testlist NEWLINE* ENDMARKER -decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE +decorator: '@' testlist NEWLINE decorators: decorator+ decorated: decorators (classdef | funcdef) funcdef: 'def' NAME parameters ['->' test] ':' suite diff -r 1e79ca2bc494 Lib/test/test_decorators.py --- a/Lib/test/test_decorators.py Wed Nov 20 11:53:31 2013 -0800 +++ b/Lib/test/test_decorators.py Wed Nov 20 20:56:18 2013 -0500 @@ -90,9 +90,30 @@ self.assertEqual(foo(), 42) self.assertEqual(foo.author, 'Cleese') + def test_chained(self): + class d1: + class d2: + def d3(self, f): + return f + @d1().d2().d3 + def foo(): return 42 + self.assertEqual(foo(), 42) + + def test_conditional(self): + def d1(f): return f + def d2(f): return None + @(d1 if True else d2) + def foo(): return 42 + self.assertEqual(foo(), 42) + + def test_lambda(self): + @(lambda f: f) + def foo(): return 42 + self.assertEqual(foo(), 42) + def test_argforms(self): - # A few tests of argument passing, as we use restricted form - # of expressions for decorators. + # A few tests of argument passing; note that we no longer + # restrict decorators to just this form. def noteargs(*args, **kwds): def decorate(func): @@ -152,15 +173,6 @@ self.assertEqual(counts['double'], 4) def test_errors(self): - # 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, diff -r 1e79ca2bc494 Modules/parsermodule.c --- a/Modules/parsermodule.c Wed Nov 20 11:53:31 2013 -0800 +++ b/Modules/parsermodule.c Wed Nov 20 20:56:18 2013 -0500 @@ -2541,7 +2541,7 @@ ok = (validate_ntype(tree, decorator) && (nch == 3 || nch == 5 || nch == 6) && validate_at(CHILD(tree, 0)) && - validate_dotted_name(CHILD(tree, 1)) && + validate_testlist(CHILD(tree, 1)) && validate_newline(RCHILD(tree, -1))); if (ok && nch != 3) { diff -r 1e79ca2bc494 Python/ast.c --- a/Python/ast.c Wed Nov 20 11:53:31 2013 -0800 +++ b/Python/ast.c Wed Nov 20 20:56:18 2013 -0500 @@ -1429,7 +1429,7 @@ REQ(CHILD(n, 0), AT); REQ(RCHILD(n, -1), NEWLINE); - name_expr = ast_for_dotted_name(c, CHILD(n, 1)); + name_expr = ast_for_testlist(c, CHILD(n, 1)); if (!name_expr) return NULL;