class decorators for Python classes - disabled for cdef classes (see transform)
authorStefan Behnel <scoder@users.berlios.de>
Fri, 26 Jun 2009 20:30:11 +0000 (22:30 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 26 Jun 2009 20:30:11 +0000 (22:30 +0200)
Cython/Compiler/Nodes.py
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/Parsing.pxd
Cython/Compiler/Parsing.py

index 998d705421f2c933e6f9e8d16468f667062d859e..df2029109b9e9f484be25d5042121a878ddcb1e8 100644 (file)
@@ -2479,6 +2479,7 @@ class PyClassDefNode(ClassDefNode):
     #  body     StatNode        Attribute definition code
     #  entry    Symtab.Entry
     #  scope    PyClassScope
+    #  decorators    [DecoratorNode]        list of decorators or None
     #
     #  The following subnodes are constructed internally:
     #
@@ -2487,12 +2488,14 @@ class PyClassDefNode(ClassDefNode):
     #  target   NameNode   Variable to assign class object to
 
     child_attrs = ["body", "dict", "classobj", "target"]
+    decorators = None
     
-    def __init__(self, pos, name, bases, doc, body):
+    def __init__(self, pos, name, bases, doc, body, decorators = None):
         StatNode.__init__(self, pos)
         self.name = name
         self.doc = doc
         self.body = body
+        self.decorators = decorators
         import ExprNodes
         self.dict = ExprNodes.DictNode(pos, key_value_pairs = [])
         if self.doc and Options.docstrings:
@@ -2538,6 +2541,7 @@ class PyClassDefNode(ClassDefNode):
                              class_name = self.name,
                              base_class_module = base_class_module,
                              base_class_name = base_class_name,
+                             decorators = self.decorators,
                              body = self.body,
                              in_pxd = False,
                              doc = self.doc)
@@ -2594,6 +2598,7 @@ class CClassDefNode(ClassDefNode):
     #  objstruct_name     string or None    Specified C name of object struct
     #  typeobj_name       string or None    Specified C name of type object
     #  in_pxd             boolean           Is in a .pxd file
+    #  decorators         [DecoratorNode]   list of decorators or None
     #  doc                string or None
     #  body               StatNode or None
     #  entry              Symtab.Entry
@@ -2608,6 +2613,7 @@ class CClassDefNode(ClassDefNode):
     api = False
     objstruct_name = None
     typeobj_name = None
+    decorators = None
 
     def analyse_declarations(self, env):
         #print "CClassDefNode.analyse_declarations:", self.class_name
index 9e159f75c856652ae00c5b180684dea568cc1b9b..c1fb5288dcb620a0a5c7e37a797d110d3aef2f95 100644 (file)
@@ -597,22 +597,48 @@ class WithTransform(CythonTransform, SkipDeclarations):
 class DecoratorTransform(CythonTransform, SkipDeclarations):
 
     def visit_DefNode(self, func_node):
+        self.visitchildren(func_node)
         if not func_node.decorators:
             return func_node
-
-        decorator_result = NameNode(func_node.pos, name = func_node.name)
-        for decorator in func_node.decorators[::-1]:
+        return self._handle_decorators(
+            func_node, func_node.name)
+
+    def _visit_CClassDefNode(self, class_node):
+        # This doesn't currently work, so it's disabled (also in the
+        # parser).
+        #
+        # Problem: assignments to cdef class names do not work.  They
+        # would require an additional check anyway, as the extension
+        # type must not change its C type, so decorators cannot
+        # replace an extension type, just alter it and return it.
+
+        self.visitchildren(class_node)
+        if not class_node.decorators:
+            return class_node
+        return self._handle_decorators(
+            class_node, class_node.class_name)
+
+    def visit_ClassDefNode(self, class_node):
+        self.visitchildren(class_node)
+        if not class_node.decorators:
+            return class_node
+        return self._handle_decorators(
+            class_node, class_node.name)
+
+    def _handle_decorators(self, node, name):
+        decorator_result = NameNode(node.pos, name = name)
+        for decorator in node.decorators[::-1]:
             decorator_result = SimpleCallNode(
                 decorator.pos,
                 function = decorator.decorator,
                 args = [decorator_result])
 
-        func_name_node = NameNode(func_node.pos, name = func_node.name)
+        name_node = NameNode(node.pos, name = name)
         reassignment = SingleAssignmentNode(
-            func_node.pos,
-            lhs = func_name_node,
+            node.pos,
+            lhs = name_node,
             rhs = decorator_result)
-        return [func_node, reassignment]
+        return [node, reassignment]
 
 
 class AnalyseDeclarationsTransform(CythonTransform):
index c2e68c2230236aa9f348ae2472dcb74e073fb26f..2d271c14bc42a4e2b0ce3f7d8d5124c11cfebb1e 100644 (file)
@@ -141,7 +141,7 @@ cpdef p_ctypedef_statement(PyrexScanner s, ctx)
 cpdef p_decorators(PyrexScanner s)
 cpdef p_def_statement(PyrexScanner s, decorators = *)
 cpdef p_py_arg_decl(PyrexScanner s)
-cpdef p_class_statement(PyrexScanner s)
+cpdef p_class_statement(PyrexScanner s, decorators = *)
 cpdef p_c_class_definition(PyrexScanner s, pos,  ctx)
 cpdef p_c_class_options(PyrexScanner s)
 cpdef p_property_decl(PyrexScanner s)
index 3d04b841b83c66afeb3d132d2117cc22e0b50541..3566347c9947429947ba7f4769aeccd9a03627a6 100644 (file)
@@ -1574,8 +1574,8 @@ def p_statement(s, ctx, first_statement = 0):
             s.error('decorator not allowed here')
         s.level = ctx.level
         decorators = p_decorators(s)
-        if s.sy not in ('def', 'cdef', 'cpdef'):
-            s.error("Decorators can only be followed by functions ")
+        if s.sy not in ('def', 'cdef', 'cpdef', 'class'):
+            s.error("Decorators can only be followed by functions or classes")
     elif s.sy == 'pass' and cdef_flag:
         # empty cdef block
         return p_pass_statement(s, with_newline = 1)
@@ -1595,7 +1595,7 @@ def p_statement(s, ctx, first_statement = 0):
         node = p_cdef_statement(s, ctx(overridable = overridable))
         if decorators is not None:
             if not isinstance(node, (Nodes.CFuncDefNode, Nodes.CVarDefNode)):
-                s.error("Decorators can only be followed by functions ")
+                s.error("Decorators can only be followed by functions or Python classes")
             node.decorators = decorators
         return node
     else:
@@ -1609,7 +1609,7 @@ def p_statement(s, ctx, first_statement = 0):
         elif s.sy == 'class':
             if ctx.level != 'module':
                 s.error("class definition not allowed here")
-            return p_class_statement(s)
+            return p_class_statement(s, decorators)
         elif s.sy == 'include':
             if ctx.level not in ('module', 'module_pxd'):
                 s.error("include statement not allowed here")
@@ -2426,7 +2426,7 @@ def p_py_arg_decl(s):
     name = p_ident(s)
     return Nodes.PyArgDeclNode(pos, name = name)
 
-def p_class_statement(s):
+def p_class_statement(s, decorators):
     # s.sy == 'class'
     pos = s.position()
     s.next()
@@ -2442,7 +2442,7 @@ def p_class_statement(s):
     return Nodes.PyClassDefNode(pos,
         name = class_name,
         bases = ExprNodes.TupleNode(pos, args = base_list),
-        doc = doc, body = body)
+        doc = doc, body = body, decorators = decorators)
 
 def p_c_class_definition(s, pos,  ctx):
     # s.sy == 'class'