Author: Matti Picus <matti.pi...@gmail.com> Branch: Changeset: r94262:be473ba66a18 Date: 2018-04-08 00:42 +0300 http://bitbucket.org/pypy/pypy/changeset/be473ba66a18/
Log: merge cpyext-subclass-setattr which fixes cpyext pyobjects "losing" a w_obj 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 @@ -640,7 +640,7 @@ 'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 'Py_NoUserSiteDirectory', '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', '_Py_PackageContext', '_PyTraceMalloc_Track', '_PyTraceMalloc_Untrack', 'PyMem_Malloc', - 'Py_IncRef', 'Py_DecRef', 'PyObject_Free', 'PyObject_GC_Del', 'PyType_GenericAlloc', + 'PyObject_Free', 'PyObject_GC_Del', 'PyType_GenericAlloc', '_PyObject_New', '_PyObject_NewVar', '_PyObject_GC_New', '_PyObject_GC_NewVar', 'PyObject_Init', 'PyObject_InitVar', 'PyInt_FromLong', diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -60,10 +60,10 @@ def _cpyext_attach_pyobj(self, space, py_obj): self._cpy_ref = py_obj - rawrefcount.create_link_pyobj(self, py_obj) + rawrefcount.create_link_pypy(self, py_obj) cls._cpyext_attach_pyobj = _cpyext_attach_pyobj -add_direct_pyobj_storage(W_BaseCPyObject) +add_direct_pyobj_storage(W_BaseCPyObject) add_direct_pyobj_storage(W_TypeObject) add_direct_pyobj_storage(W_NoneObject) add_direct_pyobj_storage(W_BoolObject) @@ -414,3 +414,14 @@ @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL) def _Py_HashPointer(space, ptr): return rffi.cast(lltype.Signed, ptr) + +@cpython_api([PyObject], lltype.Void) +def Py_IncRef(space, obj): + # used only ifdef PYPY_DEBUG_REFCOUNT + if obj: + incref(space, obj) + +@cpython_api([PyObject], lltype.Void) +def Py_DecRef(space, obj): + # used only ifdef PYPY_DEBUG_REFCOUNT + decref(space, obj) diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -5,18 +5,6 @@ extern void _PyPy_Free(void *ptr); extern void *_PyPy_Malloc(Py_ssize_t size); -void -Py_IncRef(PyObject *o) -{ - Py_XINCREF(o); -} - -void -Py_DecRef(PyObject *o) -{ - Py_XDECREF(o); -} - /* * The actual value of this variable will be the address of * pyobject.w_marker_deallocating, and will be set by diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c --- a/pypy/module/cpyext/test/array.c +++ b/pypy/module/cpyext/test/array.c @@ -2492,6 +2492,87 @@ return PyLong_FromLong(obj1->ob_type->tp_dealloc == obj2->ob_type->tp_dealloc); } +static PyObject * +subclass_with_attribute(PyObject *self, PyObject* args) { + /* what happens when we use tp_alloc to create the subclass, then + * assign to the w_obj via python, then get the GC to collect? + * The w_obj should not be collected!! + */ + PyObject * obj, *sub, *attrib, *funcname, *attribname, *collect, *res, *tup; + PyTypeObject * subtype; + int i; + if (!PyArg_ParseTuple(args, "OOOO", &obj, &funcname, &attribname, &collect)) { + return NULL; + } + if (!PyType_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "expected type object"); + return NULL; + } + subtype = (PyTypeObject*)obj; + sub = subtype->tp_alloc(subtype, 0); + if (!sub) { + return NULL; + } + attrib = PyObject_GetAttr(sub, funcname); + if (!attrib || (attrib == Py_None) ) { + PyErr_SetString(PyExc_ValueError, + "could not find function to call"); + Py_XDECREF(attrib); + Py_DECREF(sub); + return NULL; + } + tup = PyTuple_New(0); + /* + #ifdef PYPY_VERSION + printf("calling addattrib pypylink %lu \n", sub->ob_pypy_link); + #endif + */ + res = PyObject_Call(attrib, tup, NULL); + /* + #ifdef PYPY_VERSION + printf("after addattrib pypylink %lu \n", sub->ob_pypy_link); + #endif + */ + Py_DECREF(attrib); + if (res == NULL) { + Py_DECREF(tup); + Py_DECREF(sub); + return NULL; + } + Py_DECREF(res); + for(i=0; i<10; i++) { + /* + #ifdef PYPY_VERSION + printf("starting loop iteration %d refcnt %lu pypylink %lu \n", i, + sub->ob_refcnt, sub->ob_pypy_link); + #else + printf("starting loop iteration %d refcnt %lu\n", i, sub->ob_refcnt); + #endif + */ + attrib = PyObject_GetAttr(sub, attribname); + if (!attrib || (attrib == Py_None)) { + PyErr_SetString(PyExc_ValueError, + "could not find attrib on object"); + Py_XDECREF(attrib); + Py_DECREF(tup); + Py_DECREF(sub); + return NULL; + } + Py_XDECREF(attrib); + res = PyObject_Call(collect, tup, NULL); + if (res == NULL) { + Py_DECREF(tup); + Py_DECREF(sub); + return NULL; + } + Py_DECREF(res); + } + Py_DECREF(tup); + Py_DECREF(sub); + Py_RETURN_NONE; +} + /*********************** Install Module **************************/ static PyMethodDef a_methods[] = { @@ -2502,6 +2583,7 @@ {"create_and_release_buffer", (PyCFunction)create_and_release_buffer, METH_O, NULL}, {"same_dealloc", (PyCFunction)same_dealloc, METH_VARARGS, NULL}, {"getitem", (PyCFunction)getitem, METH_VARARGS, NULL}, + {"subclass_with_attribute", (PyCFunction)subclass_with_attribute, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py --- a/pypy/module/cpyext/test/test_arraymodule.py +++ b/pypy/module/cpyext/test/test_arraymodule.py @@ -186,3 +186,15 @@ # array_subscr does) raises(IndexError, module.getitem, a, -5) + def test_subclass_with_attribute(self): + module = self.import_module(name='array') + class Sub(module.array): + def addattrib(self): + print('called addattrib') + self.attrib = True + import gc + module.subclass_with_attribute(Sub, "addattrib", "attrib", gc.collect) + if self.runappdirect: + assert Sub.__module__ == 'pypy.module.cpyext.test.test_arraymodule' + assert str(Sub) == "<class 'pypy.module.cpyext.test.test_arraymodule.Sub'>" + 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 @@ -5,7 +5,7 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.api import generic_cpy_call from pypy.module.cpyext.pyobject import make_ref, from_ref, decref, as_pyobj -from pypy.module.cpyext.typeobject import PyTypeObjectPtr +from pypy.module.cpyext.typeobject import PyTypeObjectPtr, W_PyCTypeObject class AppTestTypeObject(AppTestCpythonExtensionBase): @@ -412,33 +412,42 @@ def test_type_dict(self): foo = self.import_module("foo") module = self.import_extension('test', [ - ("hack_tp_dict", "METH_O", + ("hack_tp_dict", "METH_VARARGS", ''' - PyTypeObject *type = args->ob_type; + PyTypeObject *type, *obj; PyObject *a1 = PyLong_FromLong(1); PyObject *a2 = PyLong_FromLong(2); PyObject *value; + PyObject * key; + if (!PyArg_ParseTuple(args, "OO", &obj, &key)) + return NULL; + type = obj->ob_type; - if (PyDict_SetItemString(type->tp_dict, "a", + if (PyDict_SetItem(type->tp_dict, key, a1) < 0) return NULL; Py_DECREF(a1); PyType_Modified(type); - value = PyObject_GetAttrString((PyObject *)type, "a"); + value = PyObject_GetAttr((PyObject *)type, key); Py_DECREF(value); - if (PyDict_SetItemString(type->tp_dict, "a", + if (PyDict_SetItem(type->tp_dict, key, a2) < 0) return NULL; Py_DECREF(a2); PyType_Modified(type); - value = PyObject_GetAttrString((PyObject *)type, "a"); + value = PyObject_GetAttr((PyObject *)type, key); return value; ''' ) ]) obj = foo.new() - assert module.hack_tp_dict(obj) == 2 + assert module.hack_tp_dict(obj, "a") == 2 + class Sub(foo.fooType): + pass + obj = Sub() + assert module.hack_tp_dict(obj, "b") == 2 + def test_tp_descr_get(self): module = self.import_extension('foo', [ @@ -560,6 +569,23 @@ assert w_obj is None assert api.PyErr_Occurred() is None + def test_subclass_not_PyCTypeObject(self, space, api): + pyobj = make_ref(space, api.PyLong_Type) + py_type = rffi.cast(PyTypeObjectPtr, pyobj) + w_pyclass = W_PyCTypeObject(space, py_type) + w_class = space.appexec([w_pyclass], """(base): + class Sub(base): + def addattrib(self, value): + self.attrib = value + return Sub + """) + assert w_pyclass in w_class.mro_w + assert isinstance(w_pyclass, W_PyCTypeObject) + assert not isinstance(w_class, W_PyCTypeObject) + assert w_pyclass.is_cpytype() + # XXX document the current status, not clear if this is desirable + assert w_class.is_cpytype() + class AppTestSlots(AppTestCpythonExtensionBase): def setup_class(cls): diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -1,6 +1,6 @@ import os -from rpython.rlib import jit +from rpython.rlib import jit, rawrefcount from rpython.rlib.objectmodel import specialize, we_are_translated from rpython.rtyper.lltypesystem import rffi, lltype @@ -517,6 +517,10 @@ self.w_doc = space.newtext( rffi.charp2str(cts.cast('char*', pto.c_tp_doc))) + def _cpyext_attach_pyobj(self, space, py_obj): + self._cpy_ref = py_obj + rawrefcount.create_link_pyobj(self, py_obj) + @bootstrap_function def init_typeobject(space): make_typedescr(space.w_type.layout.typedef, @@ -777,7 +781,6 @@ try: w_obj = _type_realize(space, py_obj) finally: - name = rffi.charp2str(cts.cast('char*', pto.c_tp_name)) pto.c_tp_flags &= ~Py_TPFLAGS_READYING pto.c_tp_flags |= Py_TPFLAGS_READY return w_obj @@ -884,7 +887,6 @@ base = pto.c_tp_base base_pyo = rffi.cast(PyObject, pto.c_tp_base) if base and not base.c_tp_flags & Py_TPFLAGS_READY: - name = rffi.charp2str(cts.cast('char*', base.c_tp_name)) type_realize(space, base_pyo) if base and not pto.c_ob_type: # will be filled later pto.c_ob_type = base.c_ob_type _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit