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