Fix classmethod for use in cdef classes.
authorRobert Bradshaw <robertwb@math.washington.edu>
Wed, 19 Sep 2007 20:26:34 +0000 (13:26 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Wed, 19 Sep 2007 20:26:34 +0000 (13:26 -0700)
The issue here (as pointed out by Thomas Hunger) is that once the CClass
body gets executed the class is already constructed, and its methods
are actual methods not functions (as in Python).

The CClassScope now returns a utility function on lookup of "classmethod"
that creates a class method out of a method (rather than out of a function).
When added to the type dictionary, the result is exactly the same as setting
the METH_CLASS flag.

Cython/Compiler/Nodes.py
Cython/Compiler/Symtab.py

index 2c0cebc723b3fbe08aa28e09ef713874f8207965..8f6b729f155b6d0f424a72892a610fb20b6e2f37 100644 (file)
@@ -1372,9 +1372,6 @@ class CClassDefNode(StatNode):
     def analyse_expressions(self, env):
         if self.body:
             scope = self.entry.type.scope
-            print "env", env
-            print "scope", scope
-            print scope.outer_scope
             self.body.analyse_expressions(scope)
     
     def generate_function_definitions(self, env, code):
index 9279282e8fca622f045f2d97473d5a7b1c4878b0..2674cd0b9ae8d5800fa206f3da8427cc74d5599f 100644 (file)
@@ -344,7 +344,6 @@ class Scope:
             error(pos, "'%s' is not declared" % name)
     
     def lookup(self, name):
-        print "looking up", name, "in", self
         # Look up name in this scope or an enclosing one.
         # Return None if not found.
         return (self.lookup_here(name)
@@ -980,10 +979,6 @@ class ClassScope(Scope):
     def add_string_const(self, value):
         return self.outer_scope.add_string_const(value)
 
-    def lookup(self, name):
-        res = Scope.lookup(self, name)
-        return res
-    
 
 class PyClassScope(ClassScope):
     #  Namespace of a Python class.
@@ -1194,6 +1189,21 @@ class CClassScope(ClassScope):
     def release_temp(self, cname):
         return Scope.release_temp(self.global_scope(), cname)
         
+    def lookup(self, name):
+        if name == "classmethod":
+            # We don't want to use the builtin classmethod here 'cause it won't do the 
+            # right thing in this scope (as the class memebers aren't still functions). 
+            # Don't want to add a cfunction to this scope 'cause that would mess with 
+            # the type definition, so we just return the right entry. 
+            self.use_utility_code(classmethod_utility_code)
+            entry = Entry("classmethod", 
+                          "__Pyx_Method_ClassMethod", 
+                          CFuncType(py_object_type, [CFuncTypeArg("", py_object_type, None)], 0, 0))
+            entry.is_cfunction = 1
+            return entry
+        else:
+            return Scope.lookup(self, name)
+    
         
 class PropertyScope(Scope):
     #  Scope holding the __get__, __set__ and __del__ methods for
@@ -1213,3 +1223,25 @@ class PropertyScope(Scope):
             error(pos, "Only __get__, __set__ and __del__ methods allowed "
                 "in a property declaration")
             return None
+
+
+# Should this go elsewhere (and then get imported)?
+#------------------------------------------------------------------------------------
+
+classmethod_utility_code = [
+"""
+#include "descrobject.h"
+static PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
+""","""
+static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
+    /* It appears that PyMethodDescr_Type is not anywhere exposed in the Python/C API */
+    /* if (!PyObject_TypeCheck(method, &PyMethodDescr_Type)) { */ 
+    if (strcmp(method->ob_type->tp_name, "method_descriptor") != 0) {
+        PyErr_Format(PyExc_TypeError, "Extension type classmethod() can only be called on a method_descriptor.");
+        return NULL;
+    }
+    PyMethodDescrObject *descr = (PyMethodDescrObject *)method;
+    return PyDescr_NewClassMethod(descr->d_type, descr->d_method);
+}
+"""
+]