support @cython.final decorator in override .pxd files
authorStefan Behnel <stefan_ml@behnel.de>
Sat, 22 Dec 2012 10:06:17 +0000 (11:06 +0100)
committerStefan Behnel <stefan_ml@behnel.de>
Sat, 22 Dec 2012 10:06:17 +0000 (11:06 +0100)
Cython/Compiler/ParseTreeTransforms.py
tests/run/final_in_pxd.srctree [new file with mode: 0644]

index d1c0b34..4bcfb30 100644 (file)
@@ -909,16 +909,17 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
         return self.visit_with_directives(body, directives)
 
     def visit_CVarDefNode(self, node):
-        if not node.decorators:
+        directives = self._extract_directives(node, 'function')
+        if not directives:
             return node
-        for dec in node.decorators:
-            for directive in self.try_to_parse_directives(dec.decorator) or ():
-                if directive is not None and directive[0] == u'locals':
-                    node.directive_locals = directive[1]
-                else:
-                    self.context.nonfatal_error(PostParseError(dec.pos,
-                        "Cdef functions can only take cython.locals() decorator."))
-        return node
+        for name, value in directives.iteritems():
+            if name == 'locals':
+                node.directive_locals = value
+            elif name != 'final':
+                self.context.nonfatal_error(PostParseError(dec.pos,
+                    "Cdef functions can only take cython.locals() or final decorators, got %s." % name))
+        body = Nodes.StatListNode(node.pos, stats=[node])
+        return self.visit_with_directives(body, directives)
 
     def visit_CClassDefNode(self, node):
         directives = self._extract_directives(node, 'cclass')
@@ -948,7 +949,7 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
                         directives.append(directive)
             else:
                 realdecs.append(dec)
-        if realdecs and isinstance(node, (Nodes.CFuncDefNode, Nodes.CClassDefNode)):
+        if realdecs and isinstance(node, (Nodes.CFuncDefNode, Nodes.CClassDefNode, Nodes.CVarDefNode)):
             raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.")
         else:
             node.decorators = realdecs
diff --git a/tests/run/final_in_pxd.srctree b/tests/run/final_in_pxd.srctree
new file mode 100644 (file)
index 0000000..e0fe876
--- /dev/null
@@ -0,0 +1,68 @@
+PYTHON -c "import a; assert a.__file__.rstrip('co').endswith('.py'), a.__file__; a.test()"
+PYTHON setup.py build_ext --inplace
+PYTHON -c "import a; assert not a.__file__.rstrip('co').endswith('.py'), a.__file__; a.test()"
+
+######## setup.py ########
+
+from Cython.Build.Dependencies import cythonize
+
+from distutils.core import setup
+
+setup(
+    ext_modules = cythonize("a.py"),
+    )
+
+######## a.pxd ########
+
+cimport cython
+
+cdef class ExtType:
+    @cython.final
+    cdef int final_func(self)
+
+@cython.final
+cdef class FinalExtType:
+    cdef int func(self)
+
+@cython.final
+cdef class FinalExtSubType(ExtType):
+    cdef int func(self)
+
+cdef class NonFinalSubType(ExtType):
+    cdef int func(self)
+
+
+######## a.py ########
+
+import cython
+
+class ExtType(object):
+    @cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
+    def final_func(self):
+        return 1
+
+class FinalExtType(object):
+    @cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
+    def func(self):
+        return 2
+
+class FinalExtSubType(ExtType):
+    @cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
+    def func(self):
+        return 3
+
+class NonFinalSubType(ExtType):
+    @cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
+    @cython.final
+    def func(self):
+        return 4
+
+
+def test():
+    assert ExtType().final_func() == 1
+    assert FinalExtSubType().final_func() == 1
+    assert NonFinalSubType().final_func() == 1
+
+    assert FinalExtType().func() == 2
+    assert FinalExtSubType().func() == 3
+    assert NonFinalSubType().func() == 4