Author: Ronan Lamy <ronan.l...@gmail.com>
Branch: 
Changeset: r92502:484db8c81ec1
Date: 2017-09-29 12:17 +0200
http://bitbucket.org/pypy/pypy/changeset/484db8c81ec1/

Log:    fix Cython issue with classmethods in cdef classes

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -654,7 +654,7 @@
         'PyClass_Type': 'space.gettypeobject(W_ClassObject.typedef)',
         'PyStaticMethod_Type': 'space.gettypeobject(StaticMethod.typedef)',
         'PyCFunction_Type': 
'space.gettypeobject(cpyext.methodobject.W_PyCFunctionObject.typedef)',
-        'PyWrapperDescr_Type': 
'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)'
+        'PyWrapperDescr_Type': 
'space.gettypeobject(cpyext.methodobject.W_PyCWrapperObject.typedef)',
         }.items():
         register_global(cpyname, 'PyTypeObject*', pypyexpr, header=pypy_decl)
 
diff --git a/pypy/module/cpyext/methodobject.py 
b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -1,4 +1,4 @@
-from rpython.rtyper.lltypesystem import lltype, rffi, llmemory
+from rpython.rtyper.lltypesystem import lltype, rffi
 
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
@@ -10,8 +10,7 @@
 from pypy.module.cpyext.api import (
     CONST_STRING, METH_CLASS, METH_COEXIST, METH_KEYWORDS, METH_NOARGS, METH_O,
     METH_STATIC, METH_VARARGS, PyObject, bootstrap_function,
-    cpython_api, generic_cpy_call, CANNOT_FAIL,
-    PyTypeObjectPtr, slot_function, cts)
+    cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts)
 from pypy.module.cpyext.pyobject import (
     Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr)
 
@@ -109,7 +108,7 @@
         return self.space.unwrap(self.descr_method_repr())
 
     def descr_method_repr(self):
-        w_objclass = self.w_objclass 
+        w_objclass = self.w_objclass
         assert isinstance(w_objclass, W_TypeObject)
         return self.space.newtext("<method '%s' of '%s' objects>" % (
             self.name, w_objclass.name))
@@ -117,7 +116,7 @@
     def descr_call(self, space, __args__):
         args_w, kw_w = __args__.unpack()
         if len(args_w) < 1:
-            w_objclass = self.w_objclass 
+            w_objclass = self.w_objclass
             assert isinstance(w_objclass, W_TypeObject)
             raise oefmt(space.w_TypeError,
                 "descriptor '%s' of '%s' object needs an argument",
@@ -125,7 +124,7 @@
         w_instance = args_w[0]
         # XXX: needs a stricter test
         if not space.isinstance_w(w_instance, self.w_objclass):
-            w_objclass = self.w_objclass 
+            w_objclass = self.w_objclass
             assert isinstance(w_objclass, W_TypeObject)
             raise oefmt(space.w_TypeError,
                 "descriptor '%s' requires a '%s' object but received a '%T'",
@@ -333,11 +332,15 @@
 def PyClassMethod_New(space, w_func):
     return ClassMethod(w_func)
 
-@cpython_api([PyTypeObjectPtr, lltype.Ptr(PyMethodDef)], PyObject)
+@cts.decl("""
+    PyObject *
+    PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""")
 def PyDescr_NewMethod(space, w_type, method):
     return W_PyCMethodObject(space, method, w_type)
 
-@cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject)
+@cts.decl("""
+    PyObject *
+    PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""")
 def PyDescr_NewClassMethod(space, w_type, method):
     return W_PyCClassMethodObject(space, method, w_type)
 
diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -83,6 +83,22 @@
     return cls;
 }
 
+PyObject* make_classmethod(PyObject* method)
+{
+    // adapted from __Pyx_Method_ClassMethod
+    if (PyObject_TypeCheck(method, &PyWrapperDescr_Type)) {
+        return PyClassMethod_New(method);
+    }
+    else if (PyMethod_Check(method)) {
+        return PyClassMethod_New(PyMethod_GET_FUNCTION(method));
+    }
+    else {
+        PyMethodDescrObject *descr = (PyMethodDescrObject *)method;
+        PyTypeObject *d_type = descr->d_type;
+        return PyDescr_NewClassMethod(d_type, descr->d_method);
+    }
+}
+
 static PyObject *
 foo_unset(fooobject *self)
 {
@@ -95,6 +111,7 @@
     {"copy",      (PyCFunction)foo_copy,      METH_NOARGS,  NULL},
     {"create",    (PyCFunction)foo_create,    METH_NOARGS|METH_STATIC,  NULL},
     {"classmeth", (PyCFunction)foo_classmeth, METH_NOARGS|METH_CLASS,  NULL},
+    {"fake_classmeth", (PyCFunction)foo_classmeth, METH_NOARGS,  NULL},
     {"unset_string_member", (PyCFunction)foo_unset, METH_NOARGS, NULL},
     {NULL, NULL}                 /* sentinel */
 };
@@ -167,19 +184,19 @@
     /* copied from numpy scalartypes.c for inherited classes */
     if (t->tp_bases && (PyTuple_GET_SIZE(t->tp_bases) > 1))
     {
-        PyTypeObject *sup; 
-        /* We are inheriting from a Python type as well so 
+        PyTypeObject *sup;
+        /* We are inheriting from a Python type as well so
            give it first dibs on conversion */
         sup = (PyTypeObject *)PyTuple_GET_ITEM(t->tp_bases, 1);
-        /* Prevent recursion */ 
-        if (new_fooType != sup->tp_new) 
+        /* Prevent recursion */
+        if (new_fooType != sup->tp_new)
         {
             o = sup->tp_new(t, args, kwds);
             return o;
         }
     }
     o = t->tp_alloc(t, 0);
-    return o;   
+    return o;
 };
 
 static PyMemberDef foo_members[] = {
@@ -714,7 +731,7 @@
     "foo",
     "Module Doc",
     -1,
-    foo_functions, 
+    foo_functions,
     NULL,
     NULL,
     NULL,
@@ -748,6 +765,7 @@
 #endif
 {
     PyObject *d;
+    PyObject *fake_classmeth, *classmeth;
 #if PY_MAJOR_VERSION >= 3
     PyObject *module = PyModule_Create(&moduledef);
 #else
@@ -805,6 +823,10 @@
         INITERROR;
     gettype2 = PyObject_New(PyObject, &GetType2);
 
+    fake_classmeth = PyDict_GetItemString((PyObject *)fooType.tp_dict, 
"fake_classmeth");
+    classmeth = make_classmethod(fake_classmeth);
+    if (PyDict_SetItemString((PyObject *)fooType.tp_dict, "fake_classmeth", 
classmeth) < 0)
+        INITERROR;
 
     d = PyModule_GetDict(module);
     if (d == NULL)
diff --git a/pypy/module/cpyext/test/test_typeobject.py 
b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -130,6 +130,12 @@
         assert repr(descr) == "<method 'copy' of 'foo.foo' objects>"
         raises(TypeError, descr, None)
 
+    def test_cython_fake_classmethod(self):
+        module = self.import_module(name='foo')
+        print(module.fooType.fake_classmeth)
+        print(type(module.fooType.fake_classmeth))
+        assert module.fooType.fake_classmeth() is module.fooType
+
     def test_new(self):
         # XXX cpython segfaults but if run singly (with -k test_new) this 
passes
         module = self.import_module(name='foo')
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to