reverted last change, based on extended test case
authorStefan Behnel <scoder@users.berlios.de>
Fri, 23 May 2008 20:11:00 +0000 (22:11 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 23 May 2008 20:11:00 +0000 (22:11 +0200)
Cython/Compiler/Symtab.py
tests/run/classmethod.pyx

index 02ad826a5e70158d228fe9fa222c7203c671807c..ba2e9d8cc2e4b47e2e8d1a26b62431e489bb30bc 100644 (file)
@@ -1120,6 +1120,24 @@ class ClassScope(Scope):
 
     def add_string_const(self, value):
         return self.outer_scope.add_string_const(value)
+
+    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", 
+                PyrexTypes.CFuncType(
+                    py_object_type,
+                    [PyrexTypes.CFuncTypeArg("", py_object_type, None)], 0, 0))
+            entry.is_cfunction = 1
+            return entry
+        else:
+            return Scope.lookup(self, name)
     
 
 class PyClassScope(ClassScope):
@@ -1371,3 +1389,28 @@ 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(Py_TYPE(method)->tp_name, "method_descriptor") == 0) { /* cdef classes */
+        PyMethodDescrObject *descr = (PyMethodDescrObject *)method;
+        return PyDescr_NewClassMethod(descr->d_type, descr->d_method);
+    }
+    else if (PyMethod_Check(method)) {                                /* python classes */
+        return PyClassMethod_New(PyMethod_GET_FUNCTION(method));
+    }
+    PyErr_Format(PyExc_TypeError, "Class-level classmethod() can only be called on a method_descriptor or instance method.");
+    return NULL;
+}
+"""
+]
index 2577934e067d61d44a457c23dd9f31c426ec7916..863d18b8f45a0150b6de8b3297534a6a50c7d774 100644 (file)
@@ -1,8 +1,14 @@
 __doc__ = u"""
+>>> class1.view()
+class1
 >>> class1.plus(1)
 6
+>>> class2.view()
+class2
 >>> class2.plus(1)
 7
+>>> class3.view()
+class3
 >>> class3.plus(1)
 8
 """
@@ -13,11 +19,20 @@ def f_plus(cls, a):
 class class1:
     a = 5
     plus = classmethod(f_plus)
+    def view(cls):
+        print cls.__name__
+    view = classmethod(view)
 
 class class2(object):
     a = 6
     plus = classmethod(f_plus)
+    def view(cls):
+        print cls.__name__
+    view = classmethod(view)
 
 cdef class class3:
     a = 7
     plus = classmethod(f_plus)
+    def view(cls):
+        print cls.__name__
+    view = classmethod(view)