Index: Python/compile.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.301 diff -c -u -r2.301 compile.c --- Python/compile.c 22 Mar 2004 17:52:53 -0000 2.301 +++ Python/compile.c 31 Mar 2004 18:08:24 -0000 @@ -749,6 +749,9 @@ static int com_argdefs(struct compiling *, node *); static void com_assign(struct compiling *, node *, int, node *); static void com_assign_name(struct compiling *, node *, int); +static void com_funcdef(struct compiling *, node *, int); +static int com_listmaker(struct compiling *, node *); + static PyCodeObject *icompile(node *, struct compiling *); static PyCodeObject *jcompile(node *, const char *, struct compiling *, PyCompilerFlags *); @@ -1672,12 +1675,14 @@ --c->c_tmpname; } -static void +static int com_listmaker(struct compiling *c, node *n) { /* listmaker: test ( list_for | (',' test)* [','] ) */ - if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) + if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) { com_list_comprehension(c, n); + return -1; + } else { int len = 0; int i; @@ -1685,6 +1690,7 @@ com_node(c, CHILD(n, i)); com_addoparg(c, BUILD_LIST, len); com_pop(c, len-1); + return len; } } @@ -3688,11 +3694,98 @@ return parsestrplus(c, n); } +static node * +is_list_display(node *n) +{ + do { + switch (TYPE(n)) { + + case simple_stmt: + if (NCH(n) > 2) + break; + case stmt: + case small_stmt: + n = CHILD(n, 0); + continue; + n = CHILD(n, 0); + continue; + + case expr_stmt: + case testlist: + case test: + case and_test: + case not_test: + case comparison: + case expr: + case xor_expr: + case and_expr: + case shift_expr: + case arith_expr: + case term: + case factor: + case power: + if (NCH(n) > 1) + break; + n = CHILD(n, 0); + continue; + + case atom: + if (TYPE(CHILD(n, 0)) == LSQB && + TYPE(CHILD(n, 1)) == listmaker) { + n = CHILD(n, 1); + continue; + } + break; + + case listmaker: + if (NCH(n) <= 1 || TYPE(CHILD(n, 1)) == COMMA) + return n; + break; + + } + return NULL; + } while (1); +} + +static node * +is_funcdef(node *n) +{ + do { + switch (TYPE(n)) { + + case stmt: + case compound_stmt: + n = CHILD(n, 0); + continue; + + case funcdef: + return n; + + } + return NULL; + } while (1); +} + +static void +com_decorated_funcdef(struct compiling *c, node *n0, node *n1) +{ + int len; + + REQ(n0, listmaker); + REQ(n1, funcdef); + len = com_listmaker(c, n0); + assert(len > 0); + com_addoparg(c, UNPACK_SEQUENCE, len); + com_push(c, len-1); + /* Now there are len decorators on the stack, the first on top */ + com_funcdef(c, n1, len); +} + static void com_suite(struct compiling *c, node *n) { REQ(n, suite); - /* simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT */ + /* simple_stmt | NEWLINE INDENT stmt+ DEDENT */ if (NCH(n) == 1) { com_node(c, CHILD(n, 0)); } @@ -3700,6 +3793,15 @@ int i; for (i = 0; i < NCH(n) && c->c_errors == 0; i++) { node *ch = CHILD(n, i); + node *ch0; + if (i+1 < NCH(n) && (ch0 = is_list_display(ch))) { + node *ch1; + if ((ch1 = is_funcdef(CHILD(n, i+1)))) { + com_decorated_funcdef(c, ch0, ch1); + i += 1; + continue; + } + } if (TYPE(ch) == stmt) com_node(c, ch); } @@ -3809,7 +3911,7 @@ } static void -com_funcdef(struct compiling *c, node *n) +com_funcdef(struct compiling *c, node *n, int ndecorators) { PyObject *co; int ndefs; @@ -3833,6 +3935,11 @@ else com_addoparg(c, MAKE_FUNCTION, ndefs); com_pop(c, ndefs); + while (ndecorators > 0) { + com_addoparg(c, CALL_FUNCTION, 1); + com_pop(c, 1); + ndecorators--; + } com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1))); com_pop(c, 1); Py_DECREF(co); @@ -3914,7 +4021,7 @@ /* Definition nodes */ case funcdef: - com_funcdef(c, n); + com_funcdef(c, n, 0); break; case classdef: com_classdef(c, n); Index: Lib/test/test_decorators.py =================================================================== RCS file: Lib/test/test_decorators.py diff -N Lib/test/test_decorators.py --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Lib/test/test_decorators.py 31 Mar 2004 18:08:24 -0000 @@ -0,0 +1,47 @@ +import unittest +from test import test_support + +def funcattrs(**kwds): + def decorate(func): + func.__dict__.update(kwds) + return func + return decorate + +class TestDecorators(unittest.TestCase): + + def test_none(self): + class C(object): + [] + def foo(self): return 42 + self.assertEqual(C().foo(), 42) + + def test_single(self): + class C(object): + [staticmethod] + def foo(): return 42 + self.assertEqual(C.foo(), 42) + self.assertEqual(C().foo(), 42) + + 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) + self.assertEqual(C.foo.xyz, "haha") + self.assertEqual(C.foo.booh, 42) + + def test_order(self): + class C(object): + [funcattrs(abc=1), staticmethod] + 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_main(): + test_support.run_unittest(TestDecorators) + +if __name__=="__main__": + test_main()