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