Commits

Amaury Forgeot d'Arc committed 6216fbf

cpyext: Add support for user-allocated PyObjects, when the initialization only calls _Py_NewReference(obj).

Comments (0)

Files changed (4)

pypy/module/cpyext/object.py

     Py_GE, CONST_STRING, FILEP, fwrite)
 from pypy.module.cpyext.pyobject import (
     PyObject, PyObjectP, create_ref, from_ref, Py_IncRef, Py_DecRef,
-    track_reference, get_typedescr, RefcountState)
+    track_reference, get_typedescr, _Py_NewReference, RefcountState)
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 from pypy.module.cpyext.pyerrors import PyErr_NoMemory, PyErr_BadInternalCall
-from pypy.objspace.std.objectobject import W_ObjectObject
 from pypy.objspace.std.typeobject import W_TypeObject
 from pypy.interpreter.error import OperationError
 import pypy.module.__builtin__.operation as operation
     return 0
 
 @cpython_api([PyObject, PyTypeObjectPtr], PyObject)
-def PyObject_Init(space, py_obj, type):
+def PyObject_Init(space, obj, type):
     """Initialize a newly-allocated object op with its type and initial
     reference.  Returns the initialized object.  If type indicates that the
     object participates in the cyclic garbage detector, it is added to the
     detector's set of observed objects. Other fields of the object are not
     affected."""
-    if not py_obj:
+    if not obj:
         PyErr_NoMemory(space)
-    py_obj.c_ob_type = type
-    py_obj.c_ob_refcnt = 1
-    w_type = from_ref(space, rffi.cast(PyObject, type))
-    assert isinstance(w_type, W_TypeObject)
-    if w_type.is_cpytype():
-        w_obj = space.allocate_instance(W_ObjectObject, w_type)
-        track_reference(space, py_obj, w_obj)
-        state = space.fromcache(RefcountState)
-        state.set_lifeline(w_obj, py_obj)
-    else:
-        assert False, "Please add more cases in PyObject_Init"
-    return py_obj
+    obj.c_ob_type = type
+    _Py_NewReference(space, obj)
+    return obj
 
 @cpython_api([PyVarObject, PyTypeObjectPtr, Py_ssize_t], PyObject)
 def PyObject_InitVar(space, py_obj, type, size):

pypy/module/cpyext/pyobject.py

     CANNOT_FAIL, Py_TPFLAGS_HEAPTYPE, PyTypeObjectPtr)
 from pypy.module.cpyext.state import State
 from pypy.objspace.std.typeobject import W_TypeObject
+from pypy.objspace.std.objectobject import W_ObjectObject
 from pypy.rlib.objectmodel import specialize, we_are_translated
 from pypy.rlib.rweakref import RWeakKeyDictionary
 from pypy.rpython.annlowlevel import llhelper
 @cpython_api([PyObject], lltype.Void)
 def _Py_NewReference(space, obj):
     obj.c_ob_refcnt = 1
+    w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
+    assert isinstance(w_type, W_TypeObject)
+    if w_type.is_cpytype():
+        w_obj = space.allocate_instance(W_ObjectObject, w_type)
+        track_reference(space, obj, w_obj)
+        state = space.fromcache(RefcountState)
+        state.set_lifeline(w_obj, obj)
+    else:
+        assert False, "Please add more cases in _Py_NewReference()"
 
 def _Py_Dealloc(space, obj):
     from pypy.module.cpyext.api import generic_cpy_call_dont_decref

pypy/module/cpyext/test/foo.c

     return (PyObject *)foop;
 }
 
-/* List of functions exported by this module */
-
-static PyMethodDef foo_functions[] = {
-    {"new",        (PyCFunction)foo_new, METH_NOARGS, NULL},
-    {NULL,        NULL}    /* Sentinel */
-};
-
-
 static int initerrtype_init(PyObject *self, PyObject *args, PyObject *kwargs) {
     PyErr_SetString(PyExc_ValueError, "init raised an error!");
     return -1;
     0           /*tp_weaklist*/
 };
 
+/* A type with a custom allocator */
+static void custom_dealloc(PyObject *ob)
+{
+    free(ob);
+}
+
+static PyTypeObject CustomType;
+
+static PyObject *newCustom(PyObject *self, PyObject *args)
+{
+    PyObject *obj = calloc(1, sizeof(PyObject));
+    obj->ob_type = &CustomType;
+    _Py_NewReference(obj);
+    return obj;
+}
+
+static PyTypeObject CustomType = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "foo.Custom",            /*tp_name*/
+    sizeof(PyObject),        /*tp_size*/
+    0,                       /*tp_itemsize*/
+    /* methods */
+    (destructor)custom_dealloc, /*tp_dealloc*/
+};
+
+
+/* List of functions exported by this module */
+
+static PyMethodDef foo_functions[] = {
+    {"new",        (PyCFunction)foo_new, METH_NOARGS, NULL},
+    {"newCustom",  (PyCFunction)newCustom, METH_NOARGS, NULL},
+    {NULL,        NULL}    /* Sentinel */
+};
+
 
 /* Initialize this module. */
 
     if (PyType_Ready(&InitErrType) < 0)
         return;
     if (PyType_Ready(&SimplePropertyType) < 0)
-	return;
+        return;
+    if (PyType_Ready(&CustomType) < 0)
+        return;
     m = Py_InitModule("foo", foo_functions);
     if (m == NULL)
         return;
         return;
     if (PyDict_SetItemString(d, "Property", (PyObject *) &SimplePropertyType) < 0)
         return;
+    if (PyDict_SetItemString(d, "Custom", (PyObject *) &CustomType) < 0)
+        return;
 }

pypy/module/cpyext/test/test_typeobject.py

         obj = foo.new()
         assert module.read_tp_dict(obj) == foo.fooType.copy
 
+    def test_custom_allocation(self):
+        foo = self.import_module("foo")
+        obj = foo.newCustom()
+        assert type(obj) is foo.Custom
 
 class TestTypes(BaseApiTest):
     def test_type_attributes(self, space, api):