Author: Ronan Lamy <[email protected]>
Branch: 
Changeset: r90506:37e26597bd07
Date: 2017-03-03 11:53 +0100
http://bitbucket.org/pypy/pypy/changeset/37e26597bd07/

Log:    Merged fix-cpyext-releasebuffer into default

diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py
--- a/pypy/module/cpyext/buffer.py
+++ b/pypy/module/cpyext/buffer.py
@@ -2,7 +2,7 @@
 from pypy.interpreter.error import oefmt
 from pypy.module.cpyext.api import (
     cpython_api, CANNOT_FAIL, Py_TPFLAGS_HAVE_NEWBUFFER, cts, Py_buffer,
-    Py_ssize_t, Py_ssize_tP,
+    Py_ssize_t, Py_ssize_tP, generic_cpy_call,
     PyBUF_WRITABLE, PyBUF_FORMAT, PyBUF_ND, PyBUF_STRIDES)
 from pypy.module.cpyext.pyobject import PyObject, Py_IncRef, Py_DecRef
 
@@ -13,10 +13,6 @@
     flags = pyobj.c_ob_type.c_tp_flags
     if (flags & Py_TPFLAGS_HAVE_NEWBUFFER and as_buffer.c_bf_getbuffer):
         return 1
-    name = rffi.charp2str(cts.cast('char*', pyobj.c_ob_type.c_tp_name))
-    if name in ('str', 'bytes'):
-        # XXX remove once wrapper of __buffer__ -> bf_getbuffer works
-        return 1
     return 0
 
 @cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t,
@@ -60,6 +56,15 @@
     Release the buffer view. This should be called when the buffer is
     no longer being used as it may free memory from it
     """
-    Py_DecRef(space, view.c_obj)
+    obj = view.c_obj
+    if not obj:
+        return
+    assert obj.c_ob_type
+    as_buffer = obj.c_ob_type.c_tp_as_buffer
+    if as_buffer:
+        func = as_buffer.c_bf_releasebuffer
+        if func:
+            generic_cpy_call(space, func, obj, view)
+    Py_DecRef(space, obj)
     view.c_obj = lltype.nullptr(PyObject.TO)
     # XXX do other fields leak memory?
diff --git a/pypy/module/cpyext/memoryobject.py 
b/pypy/module/cpyext/memoryobject.py
--- a/pypy/module/cpyext/memoryobject.py
+++ b/pypy/module/cpyext/memoryobject.py
@@ -1,15 +1,18 @@
+from rpython.rlib.objectmodel import keepalive_until_here
+from pypy.interpreter.error import oefmt
 from pypy.module.cpyext.api import (
     cpython_api, Py_buffer, CANNOT_FAIL, Py_MAX_FMT, Py_MAX_NDIMS,
     build_type_checkers, Py_ssize_tP, PyObjectFields, cpython_struct,
-    bootstrap_function, Py_bufferP, slot_function)
+    bootstrap_function, Py_bufferP, slot_function, generic_cpy_call)
 from pypy.module.cpyext.pyobject import (
-    PyObject, make_ref, as_pyobj, incref, decref, from_ref, make_typedescr,
+    PyObject, make_ref, as_pyobj, decref, from_ref, make_typedescr,
     get_typedescr, track_reference)
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rlib.rarithmetic import widen
 from pypy.objspace.std.memoryobject import W_MemoryView
 from pypy.module.cpyext.object import _dealloc
 from pypy.module.cpyext.import_ import PyImport_Import
+from pypy.module.cpyext.buffer import PyObject_CheckBuffer
 
 PyMemoryView_Check, PyMemoryView_CheckExact = build_type_checkers("MemoryView")
 
@@ -114,18 +117,13 @@
     raise an error if the object can't support a simpler view of its memory.
 
     0 is returned on success and -1 on error."""
-    flags = widen(flags)
-    buf = space.buffer_w(w_obj, flags)
-    try:
-        view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
-    except ValueError:
-        if not space.isinstance_w(w_obj, space.w_bytes):
-            # XXX Python 3?
-            raise BufferError("could not create buffer from object")
-        view.c_buf = rffi.cast(rffi.VOIDP, 
rffi.str2charp(space.bytes_w(w_obj), track_allocation=False))
-        rffi.setintfield(view, 'c_readonly', 1)
-    ret = fill_Py_buffer(space, buf, view)
-    view.c_obj = make_ref(space, w_obj)
+    if not PyObject_CheckBuffer(space, w_obj):
+        raise oefmt(space.w_TypeError,
+                    "'%T' does not have the buffer interface", w_obj)
+    py_obj = as_pyobj(space, w_obj)
+    func = py_obj.c_ob_type.c_tp_as_buffer.c_bf_getbuffer
+    ret = generic_cpy_call(space, func, py_obj, view, flags)
+    keepalive_until_here(w_obj)
     return ret
 
 def fill_Py_buffer(space, buf, view):
@@ -259,4 +257,3 @@
         py_mem.c_view.c_shape = view.c_shape
     # XXX ignore suboffsets?
     return py_obj
-
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -7,14 +7,14 @@
 from rpython.rlib import rgc # Force registration of gc.collect
 from pypy.module.cpyext.api import (
     slot_function, generic_cpy_call, PyObject, Py_ssize_t, 
Py_TPFLAGS_CHECKTYPES,
-    pypy_decl, Py_buffer, Py_bufferP, PyTypeObjectPtr)
+    pypy_decl, Py_buffer, Py_bufferP, PyTypeObjectPtr, cts)
 from pypy.module.cpyext.typeobjectdefs import (
     unaryfunc, ternaryfunc, binaryfunc,
     getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry,
     ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc,
     cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc,
     readbufferproc, getbufferproc, releasebufferproc, ssizessizeobjargproc)
-from pypy.module.cpyext.pyobject import make_ref, decref
+from pypy.module.cpyext.pyobject import make_ref, decref, as_pyobj
 from pypy.module.cpyext.pyerrors import PyErr_Occurred
 from pypy.module.cpyext.memoryobject import fill_Py_buffer
 from pypy.module.cpyext.state import State
@@ -327,13 +327,14 @@
     _immutable_ = True
 
     def __init__(self, space, ptr, size, w_obj, format='B', shape=None,
-                strides=None, ndim=1, itemsize=1, readonly=True,
-                releasebufferproc=rffi.cast(rffi.VOIDP, 0)):
+                 strides=None, ndim=1, itemsize=1, readonly=True,
+                 needs_decref=False,
+                 releasebufferproc=rffi.cast(rffi.VOIDP, 0)):
         self.space = space
         self.ptr = ptr
         self.size = size
         self.w_obj = w_obj # kept alive
-        self.pyobj = make_ref(space, w_obj)
+        self.pyobj = as_pyobj(space, w_obj)
         self.format = format
         if not shape:
             self.shape = [size]
@@ -346,30 +347,33 @@
         self.ndim = ndim
         self.itemsize = itemsize
         self.readonly = readonly
+        self.needs_decref = needs_decref
         self.releasebufferproc = releasebufferproc
 
     def releasebuffer(self):
         if self.pyobj:
-            decref(self.space, self.pyobj)
+            if self.needs_decref:
+                if self.releasebufferproc:
+                    func_target = rffi.cast(releasebufferproc, 
self.releasebufferproc)
+                    with lltype.scoped_alloc(Py_buffer) as pybuf:
+                        pybuf.c_buf = self.ptr
+                        pybuf.c_len = self.size
+                        pybuf.c_ndim = cts.cast('int', self.ndim)
+                        pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape)
+                        pybuf.c_strides = cts.cast('Py_ssize_t*', 
pybuf.c__strides)
+                        for i in range(self.ndim):
+                            pybuf.c_shape[i] = self.shape[i]
+                            pybuf.c_strides[i] = self.strides[i]
+                        if self.format:
+                            pybuf.c_format = rffi.str2charp(self.format)
+                        else:
+                            pybuf.c_format = rffi.str2charp("B")
+                        generic_cpy_call(self.space, func_target, self.pyobj, 
pybuf)
+                decref(self.space, self.pyobj)
             self.pyobj = lltype.nullptr(PyObject.TO)
         else:
             #do not call twice
             return
-        if self.releasebufferproc:
-            func_target = rffi.cast(releasebufferproc, self.releasebufferproc)
-            with lltype.scoped_alloc(Py_buffer) as pybuf:
-                pybuf.c_buf = self.ptr
-                pybuf.c_len = self.size
-                pybuf.c_ndim = rffi.cast(rffi.INT_real, self.ndim)
-                for i in range(self.ndim):
-                    pybuf.c_shape[i] = self.shape[i]
-                    pybuf.c_strides[i] = self.strides[i]
-                if self.format:
-                    pybuf.c_format = rffi.str2charp(self.format)
-                else:
-                    pybuf.c_format = rffi.str2charp("B")
-                generic_cpy_call(self.space, func_target, self.pyobj, pybuf)
-            self.releasebufferproc = rffi.cast(rffi.VOIDP, 0)
 
     def getlength(self):
         return self.size
@@ -465,19 +469,25 @@
         ptr = pybuf.c_buf
         size = pybuf.c_len
         ndim = widen(pybuf.c_ndim)
-        shape =   [pybuf.c_shape[i]   for i in range(ndim)]
+        shape = None
+        if pybuf.c_shape:
+            shape = [pybuf.c_shape[i]   for i in range(ndim)]
+        strides = None
         if pybuf.c_strides:
             strides = [pybuf.c_strides[i] for i in range(ndim)]
-        else:
-            strides = [1]
         if pybuf.c_format:
             format = rffi.charp2str(pybuf.c_format)
         else:
             format = 'B'
+        # the CPython docs mandates that you do an incref whenever you call
+        # bf_getbuffer; so, we pass needs_decref=True to ensure that we don't
+        # leak we release the buffer:
+        # 
https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer
         buf = CPyBuffer(space, ptr, size, w_self, format=format,
                             ndim=ndim, shape=shape, strides=strides,
                             itemsize=pybuf.c_itemsize,
                             readonly=widen(pybuf.c_readonly),
+                            needs_decref=True,
                             releasebufferproc = rbp)
         fq.register_finalizer(buf)
         return space.newbuffer(buf)
@@ -713,33 +723,12 @@
         slot_func = slot_tp_new
     elif name == 'tp_as_buffer.c_bf_getbuffer':
         buff_fn = w_type.getdictvalue(space, '__buffer__')
-        if buff_fn is None:
+        if buff_fn is not None:
+            buff_w = slot_from___buffer__(space, typedef, buff_fn)
+        elif typedef.buffer:
+            buff_w = slot_from_buffer_w(space, typedef, buff_fn)
+        else:
             return
-        @slot_function([PyObject, Py_bufferP, rffi.INT_real],
-                rffi.INT_real, error=-1)
-        @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name))
-        def buff_w(space, w_self, view, flags):
-            args = Arguments(space, [space.newint(flags)])
-            w_obj = space.call_args(space.get(buff_fn, w_self), args)
-            if view:
-                #like PyObject_GetBuffer
-                flags = widen(flags)
-                buf = space.buffer_w(w_obj, flags)
-                try:
-                    view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
-                    view.c_obj = make_ref(space, w_obj)
-                except ValueError:
-                    s = buf.as_str()
-                    w_s = space.newbytes(s)
-                    view.c_obj = make_ref(space, w_s)
-                    view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp(
-                                           s, track_allocation=False))
-                    rffi.setintfield(view, 'c_readonly', 1)
-                ret = fill_Py_buffer(space, buf, view)
-                return ret
-            return 0
-        # XXX remove this when it no longer crashes a translated PyPy
-        return
         slot_func = buff_w
     else:
         # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce
@@ -749,6 +738,60 @@
 
     return slot_func
 
+
+def slot_from___buffer__(space, typedef, buff_fn):
+    name = 'bf_getbuffer'
+    @slot_function([PyObject, Py_bufferP, rffi.INT_real],
+            rffi.INT_real, error=-1)
+    @func_renamer("cpyext_%s_%s" % (name, typedef.name))
+    def buff_w(space, w_self, view, flags):
+        args = Arguments(space, [space.newint(flags)])
+        w_obj = space.call_args(space.get(buff_fn, w_self), args)
+        if view:
+            #like PyObject_GetBuffer
+            flags = widen(flags)
+            buf = space.buffer_w(w_obj, flags)
+            try:
+                view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
+                view.c_obj = make_ref(space, w_obj)
+            except ValueError:
+                s = buf.as_str()
+                w_s = space.newbytes(s)
+                view.c_obj = make_ref(space, w_s)
+                view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp(
+                                        s, track_allocation=False))
+                rffi.setintfield(view, 'c_readonly', 1)
+            ret = fill_Py_buffer(space, buf, view)
+            return ret
+        return 0
+    return buff_w
+
+def slot_from_buffer_w(space, typedef, buff_fn):
+    name = 'bf_getbuffer'
+    @slot_function([PyObject, Py_bufferP, rffi.INT_real],
+            rffi.INT_real, error=-1)
+    @func_renamer("cpyext_%s_%s" % (name, typedef.name))
+    def buff_w(space, w_self, view, flags):
+        w_obj = w_self
+        if view:
+            #like PyObject_GetBuffer
+            flags = widen(flags)
+            buf = space.buffer_w(w_obj, flags)
+            try:
+                view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
+                view.c_obj = make_ref(space, w_obj)
+            except ValueError:
+                s = buf.as_str()
+                w_s = space.newbytes(s)
+                view.c_obj = make_ref(space, w_s)
+                view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp(
+                                        s, track_allocation=False))
+                rffi.setintfield(view, 'c_readonly', 1)
+            ret = fill_Py_buffer(space, buf, view)
+            return ret
+        return 0
+    return buff_w
+
 PyWrapperFlag_KEYWORDS = 1
 
 class TypeSlot:
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
@@ -1893,10 +1893,10 @@
     }
     else if(obj1->ob_type == &Arraytype)
         fprintf(stderr, "\nCannot multiply array of type %c and %s\n",
-            ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name); 
+            ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name);
     else if(obj2->ob_type == &Arraytype)
         fprintf(stderr, "\nCannot multiply array of type %c and %s\n",
-            ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name); 
+            ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name);
     Py_INCREF(Py_NotImplemented);
     return Py_NotImplemented;
 }
@@ -1947,10 +1947,10 @@
     }
     else if(obj1->ob_type == &Arraytype)
         fprintf(stderr, "\nCannot multiply array of type %c and %s\n",
-            ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name); 
+            ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name);
     else if(obj2->ob_type == &Arraytype)
         fprintf(stderr, "\nCannot multiply array of type %c and %s\n",
-            ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name); 
+            ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name);
     Py_INCREF(Py_NotImplemented);
     return Py_NotImplemented;
 }
@@ -2006,6 +2006,29 @@
     return 1;
 }
 
+static int
+array_getbuffer(PyObject* obj, Py_buffer* view, int flags)
+{
+    arrayobject* self = (arrayobject*)obj;
+    return PyBuffer_FillInfo(view, obj, self->ob_item,
+            Py_SIZE(self)*self->ob_descr->itemsize, 0, flags);
+}
+
+static long releasebuffer_cnt = 0;
+
+static PyObject *
+get_releasebuffer_cnt(void)
+{
+    return PyLong_FromLong(releasebuffer_cnt);
+}
+
+static void
+array_releasebuffer(arrayobject* self, Py_buffer* view)
+{
+    releasebuffer_cnt++;
+    return;
+}
+
 static PySequenceMethods array_as_sequence = {
     (lenfunc)array_length,                      /*sq_length*/
     (binaryfunc)array_concat,               /*sq_concat*/
@@ -2024,6 +2047,8 @@
     (writebufferproc)array_buffer_getwritebuf,
     (segcountproc)array_buffer_getsegcount,
     NULL,
+    (getbufferproc)array_getbuffer,
+    (releasebufferproc)array_releasebuffer
 };
 
 static PyObject *
@@ -2237,7 +2262,7 @@
     PyObject_GenericGetAttr,                    /* tp_getattro */
     0,                                          /* tp_setattro */
     &array_as_buffer,                           /* tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | 
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
     Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_CHECKTYPES,  /* tp_flags */
     arraytype_doc,                              /* tp_doc */
     0,                                          /* tp_traverse */
@@ -2280,8 +2305,9 @@
     PyObject_GenericGetAttr,                    /* tp_getattro */
     0,                                          /* tp_setattro */
     &array_as_buffer,                           /* tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | 
-    Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_CHECKTYPES,  /* tp_flags */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+    Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_CHECKTYPES |
+    Py_TPFLAGS_HAVE_NEWBUFFER,                  /* tp_flags */
     arraytype_doc,                              /* tp_doc */
     0,                                          /* tp_traverse */
     0,                                          /* tp_clear */
@@ -2409,6 +2435,16 @@
     return PyString_FromStringAndSize((char*)ptr, size);
 }
 
+static PyObject *
+create_and_release_buffer(PyObject *self, PyObject *obj)
+{
+    Py_buffer view;
+    int res = PyObject_GetBuffer(obj, &view, 0);
+    if (res < 0)
+        return NULL;
+    PyBuffer_Release(&view);
+    Py_RETURN_NONE;
+}
 
 
 /*********************** Install Module **************************/
@@ -2417,6 +2453,8 @@
     {"_reconstruct",   (PyCFunction)_reconstruct, METH_VARARGS, NULL},
     {"switch_multiply",   (PyCFunction)switch_multiply, METH_NOARGS, NULL},
     {"readbuffer_as_string",   (PyCFunction)readbuffer_as_string, 
METH_VARARGS, NULL},
+    {"get_releasebuffer_cnt",   (PyCFunction)get_releasebuffer_cnt, 
METH_NOARGS, NULL},
+    {"create_and_release_buffer",   (PyCFunction)create_and_release_buffer, 
METH_O, 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
@@ -78,6 +78,23 @@
                                 '\x03\0\0\0'
                                 '\x04\0\0\0')
 
+    def test_releasebuffer(self):
+        module = self.import_module(name='array')
+        arr = module.array('i', [1,2,3,4])
+        assert module.get_releasebuffer_cnt() == 0
+        module.create_and_release_buffer(arr)
+        assert module.get_releasebuffer_cnt() == 1
+
+    def test_Py_buffer(self):
+        module = self.import_module(name='array')
+        arr = module.array('i', [1,2,3,4])
+        assert module.get_releasebuffer_cnt() == 0
+        m = memoryview(arr)
+        assert module.get_releasebuffer_cnt() == 0
+        del m
+        self.debug_collect()
+        assert module.get_releasebuffer_cnt() == 1
+
     def test_pickle(self):
         import pickle
         module = self.import_module(name='array')
@@ -107,7 +124,7 @@
         arr = Sub('i', [2])
         res = [1, 2, 3] * arr
         assert res == [1, 2, 3, 1, 2, 3]
-        
+
         val = module.readbuffer_as_string(arr)
         assert val == struct.pack('i', 2)
 
diff --git a/pypy/module/cpyext/test/test_bufferobject.py 
b/pypy/module/cpyext/test/test_bufferobject.py
--- a/pypy/module/cpyext/test/test_bufferobject.py
+++ b/pypy/module/cpyext/test/test_bufferobject.py
@@ -63,59 +63,3 @@
         a = array.array('c', 'text')
         b = buffer(a)
         assert module.roundtrip(b) == 'text'
-        
-    def test_releasebuffer(self):
-        module = self.import_extension('foo', [
-            ("create_test", "METH_NOARGS",
-             """
-                PyObject *obj;
-                obj = PyObject_New(PyObject, (PyTypeObject*)type);
-                return obj;
-             """),
-            ("get_cnt", "METH_NOARGS",
-             'return PyLong_FromLong(cnt);')], prologue="""
-                static float test_data = 42.f;
-                static int cnt=0;
-                static PyHeapTypeObject * type=NULL;
-
-                int getbuffer(PyObject *obj, Py_buffer *view, int flags) {
-
-                    cnt ++;
-                    memset(view, 0, sizeof(Py_buffer));
-                    view->obj = obj;
-                    view->ndim = 0;
-                    view->buf = (void *) &test_data;
-                    view->itemsize = sizeof(float);
-                    view->len = 1;
-                    view->strides = NULL;
-                    view->shape = NULL;
-                    view->format = "f";
-                    return 0;
-                }
-
-                void releasebuffer(PyObject *obj, Py_buffer *view) { 
-                    cnt --;
-                }
-            """, more_init="""
-                type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 
0);
-
-                type->ht_type.tp_name = "Test";
-                type->ht_type.tp_basicsize = sizeof(PyObject);
-                type->ht_name = PyString_FromString("Test");
-                type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | 
Py_TPFLAGS_BASETYPE |
-                                          Py_TPFLAGS_HEAPTYPE | 
Py_TPFLAGS_HAVE_NEWBUFFER;
-                type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC;
-
-                type->ht_type.tp_as_buffer = &type->as_buffer;
-                type->as_buffer.bf_getbuffer = getbuffer;
-                type->as_buffer.bf_releasebuffer = releasebuffer;
-
-                if (PyType_Ready(&type->ht_type) < 0) INITERROR;
-            """, )
-        import gc
-        assert module.get_cnt() == 0
-        a = memoryview(module.create_test())
-        assert module.get_cnt() == 1
-        del a
-        gc.collect(); gc.collect(); gc.collect()
-        assert module.get_cnt() == 0
diff --git a/pypy/module/cpyext/test/test_memoryobject.py 
b/pypy/module/cpyext/test/test_memoryobject.py
--- a/pypy/module/cpyext/test/test_memoryobject.py
+++ b/pypy/module/cpyext/test/test_memoryobject.py
@@ -7,7 +7,7 @@
 from pypy.module.cpyext.pyobject import from_ref
 from pypy.module.cpyext.memoryobject import PyMemoryViewObject
 
-only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names" 
+only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names"
 
 class TestMemoryViewObject(BaseApiTest):
     def test_fromobject(self, space, api):
@@ -35,10 +35,10 @@
         assert o == 'hello'
         ref = api.PyMemoryView_FromBuffer(view)
         w_mv = from_ref(space, ref)
-        for f in ('format', 'itemsize', 'ndim', 'readonly', 
+        for f in ('format', 'itemsize', 'ndim', 'readonly',
                   'shape', 'strides', 'suboffsets'):
             w_f = space.wrap(f)
-            assert space.eq_w(space.getattr(w_mv, w_f), 
+            assert space.eq_w(space.getattr(w_mv, w_f),
                               space.getattr(w_memoryview, w_f))
         api.Py_DecRef(ref)
 
@@ -137,15 +137,15 @@
         shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS'])
         assert strides[-1] == 8
         dt1 = np.dtype(
-             [('a', 'b'), ('b', 'i'), 
-              ('sub0', np.dtype('b,i')), 
-              ('sub1', np.dtype('b,i')), 
-              ('sub2', np.dtype('b,i')), 
-              ('sub3', np.dtype('b,i')), 
-              ('sub4', np.dtype('b,i')), 
-              ('sub5', np.dtype('b,i')), 
-              ('sub6', np.dtype('b,i')), 
-              ('sub7', np.dtype('b,i')), 
+             [('a', 'b'), ('b', 'i'),
+              ('sub0', np.dtype('b,i')),
+              ('sub1', np.dtype('b,i')),
+              ('sub2', np.dtype('b,i')),
+              ('sub3', np.dtype('b,i')),
+              ('sub4', np.dtype('b,i')),
+              ('sub5', np.dtype('b,i')),
+              ('sub6', np.dtype('b,i')),
+              ('sub7', np.dtype('b,i')),
               ('c', 'i')],
              )
         x = np.arange(dt1.itemsize, dtype='int8').view(dt1)
@@ -162,3 +162,75 @@
                           " on too long format string"
         finally:
             warnings.resetwarnings()
+
+    def test_releasebuffer(self):
+        if not self.runappdirect:
+            skip("Fails due to ll2ctypes nonsense")
+        module = self.import_extension('foo', [
+            ("create_test", "METH_NOARGS",
+             """
+                PyObject *obj;
+                obj = PyObject_New(PyObject, (PyTypeObject*)type);
+                return obj;
+             """),
+            ("get_cnt", "METH_NOARGS",
+             'return PyLong_FromLong(cnt);'),
+            ("get_dealloc_cnt", "METH_NOARGS",
+             'return PyLong_FromLong(dealloc_cnt);'),
+        ],
+        prologue="""
+                static float test_data = 42.f;
+                static int cnt=0;
+                static int dealloc_cnt=0;
+                static PyHeapTypeObject * type=NULL;
+
+                void dealloc(PyObject *self) {
+                    dealloc_cnt++;
+                }
+                int getbuffer(PyObject *obj, Py_buffer *view, int flags) {
+
+                    cnt ++;
+                    memset(view, 0, sizeof(Py_buffer));
+                    view->obj = obj;
+                    /* see the CPython docs for why we need this incref:
+                       
https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer */
+                    Py_INCREF(obj);
+                    view->ndim = 0;
+                    view->buf = (void *) &test_data;
+                    view->itemsize = sizeof(float);
+                    view->len = 1;
+                    view->strides = NULL;
+                    view->shape = NULL;
+                    view->format = "f";
+                    return 0;
+                }
+
+                void releasebuffer(PyObject *obj, Py_buffer *view) {
+                    cnt --;
+                }
+            """, more_init="""
+                type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 
0);
+
+                type->ht_type.tp_name = "Test";
+                type->ht_type.tp_basicsize = sizeof(PyObject);
+                type->ht_name = PyString_FromString("Test");
+                type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | 
Py_TPFLAGS_BASETYPE |
+                                          Py_TPFLAGS_HEAPTYPE | 
Py_TPFLAGS_HAVE_NEWBUFFER;
+                type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC;
+
+                type->ht_type.tp_dealloc = dealloc;
+                type->ht_type.tp_as_buffer = &type->as_buffer;
+                type->as_buffer.bf_getbuffer = getbuffer;
+                type->as_buffer.bf_releasebuffer = releasebuffer;
+
+                if (PyType_Ready(&type->ht_type) < 0) INITERROR;
+            """, )
+        import gc
+        assert module.get_cnt() == 0
+        a = memoryview(module.create_test())
+        assert module.get_cnt() == 1
+        assert module.get_dealloc_cnt() == 0
+        del a
+        self.debug_collect()
+        assert module.get_cnt() == 0
+        assert module.get_dealloc_cnt() == 1
diff --git a/pypy/module/micronumpy/ndarray.py 
b/pypy/module/micronumpy/ndarray.py
--- a/pypy/module/micronumpy/ndarray.py
+++ b/pypy/module/micronumpy/ndarray.py
@@ -1532,7 +1532,7 @@
         return res
 """, filename=__file__).interphook('ptp')
 
-W_NDimArray.typedef = TypeDef("numpy.ndarray",
+W_NDimArray.typedef = TypeDef("numpy.ndarray", None, None, 'read-write',
     __new__ = interp2app(descr_new_array),
 
     __len__ = interp2app(W_NDimArray.descr_len),
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to