[pypy-commit] pypy missing-tp_new: merge default into branch
mattip
pypy.commits at gmail.com
Tue Dec 27 16:33:52 EST 2016
Author: Matti Picus <matti.picus at gmail.com>
Branch: missing-tp_new
Changeset: r89256:d8febc18447e
Date: 2016-12-27 23:09 +0200
http://bitbucket.org/pypy/pypy/changeset/d8febc18447e/
Log: merge default into branch
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -68,3 +68,11 @@
.. branch: stdlib-2.7.13
Updated the implementation to match CPython 2.7.13 instead of 2.7.13.
+
+.. branch: issue2444
+
+Fix ``PyObject_GetBuffer`` and ``PyMemoryView_GET_BUFFER``, which leaked
+memory and held references. Add a finalizer to CPyBuffer, add a
+PyMemoryViewObject with a PyBuffer attached so that the call to
+``PyMemoryView_GET_BUFFER`` does not leak a PyBuffer-sized piece of memory.
+Properly call ``bf_releasebuffer`` when not ``NULL``.
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
@@ -637,18 +637,18 @@
('len', Py_ssize_t),
('itemsize', Py_ssize_t),
- ('readonly', lltype.Signed),
- ('ndim', lltype.Signed),
+ ('readonly', rffi.INT_real),
+ ('ndim', rffi.INT_real),
('format', rffi.CCHARP),
('shape', Py_ssize_tP),
('strides', Py_ssize_tP),
+ ('suboffsets', Py_ssize_tP),
('_format', rffi.CFixedArray(rffi.UCHAR, Py_MAX_FMT)),
('_shape', rffi.CFixedArray(Py_ssize_t, Py_MAX_NDIMS)),
('_strides', rffi.CFixedArray(Py_ssize_t, Py_MAX_NDIMS)),
- ('suboffsets', Py_ssize_tP),
#('smalltable', rffi.CFixedArray(Py_ssize_t, 2)),
- ('internal', rffi.VOIDP)
- ))
+ ('internal', rffi.VOIDP),
+))
Py_bufferP = lltype.Ptr(Py_buffer)
@specialize.memo()
diff --git a/pypy/module/cpyext/classobject.py b/pypy/module/cpyext/classobject.py
--- a/pypy/module/cpyext/classobject.py
+++ b/pypy/module/cpyext/classobject.py
@@ -1,7 +1,7 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (
PyObjectFields, CANNOT_FAIL,
- cpython_api, bootstrap_function, cpython_struct, build_type_checkers)
+ cpython_api, bootstrap_function, build_type_checkers)
from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref, Py_DecRef, make_typedescr
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
from pypy.module.__builtin__.interp_classobj import W_ClassObject, W_InstanceObject
diff --git a/pypy/module/cpyext/include/memoryobject.h b/pypy/module/cpyext/include/memoryobject.h
--- a/pypy/module/cpyext/include/memoryobject.h
+++ b/pypy/module/cpyext/include/memoryobject.h
@@ -5,6 +5,14 @@
extern "C" {
#endif
+/* The struct is declared here but it shouldn't
+ be considered public. Don't access those fields directly,
+ use the functions instead! */
+typedef struct {
+ PyObject_HEAD
+ Py_buffer view;
+} PyMemoryViewObject;
+
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,14 +1,59 @@
from pypy.module.cpyext.api import (cpython_api, Py_buffer, CANNOT_FAIL,
- Py_MAX_FMT, Py_MAX_NDIMS, build_type_checkers, Py_ssize_tP)
-from pypy.module.cpyext.pyobject import PyObject, make_ref, incref, from_ref
+ Py_MAX_FMT, Py_MAX_NDIMS, build_type_checkers,
+ Py_ssize_tP, PyObjectFields, cpython_struct,
+ bootstrap_function, Py_bufferP)
+from pypy.module.cpyext.pyobject import (PyObject, make_ref, as_pyobj, incref,
+ decref, from_ref, make_typedescr)
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
-PyMemoryView_Check, PyMemoryView_CheckExact = build_type_checkers("MemoryView", "w_memoryview")
+PyMemoryView_Check, PyMemoryView_CheckExact = build_type_checkers("MemoryView")
- at cpython_api([PyObject, lltype.Ptr(Py_buffer), rffi.INT_real],
+
+PyMemoryViewObjectStruct = lltype.ForwardReference()
+PyMemoryViewObject = lltype.Ptr(PyMemoryViewObjectStruct)
+PyMemoryViewObjectFields = PyObjectFields + \
+ (("view", Py_buffer),)
+cpython_struct(
+ "PyMemoryViewObject", PyMemoryViewObjectFields, PyMemoryViewObjectStruct,
+ level=2)
+
+ at bootstrap_function
+def init_memoryobject(space):
+ "Type description of PyDictObject"
+ make_typedescr(W_MemoryView.typedef,
+ basestruct=PyMemoryViewObject.TO,
+ attach=memory_attach,
+ dealloc=memory_dealloc,
+ #realize=memory_realize,
+ )
+
+def memory_attach(space, py_obj, w_obj, w_userdata=None):
+ """
+ Fills a newly allocated PyMemoryViewObject with the given W_MemoryView object.
+ """
+ py_obj = rffi.cast(PyMemoryViewObject, py_obj)
+ py_obj.c_view.c_obj = rffi.cast(PyObject, 0)
+
+def memory_realize(space, py_obj):
+ """
+ Creates the memory object in the interpreter
+ """
+ raise oefmt(space.w_NotImplementedError, "cannot call this yet")
+
+ at cpython_api([PyObject], lltype.Void, header=None)
+def memory_dealloc(space, py_obj):
+ mem_obj = rffi.cast(PyMemoryViewObject, py_obj)
+ if mem_obj.c_view.c_obj:
+ decref(space, mem_obj.c_view.c_obj)
+ mem_obj.c_view.c_obj = rffi.cast(PyObject, 0)
+ _dealloc(space, py_obj)
+
+
+ at cpython_api([PyObject, Py_bufferP, rffi.INT_real],
rffi.INT_real, error=-1)
def PyObject_GetBuffer(space, w_obj, view, flags):
"""Export obj into a Py_buffer, view. These arguments must
@@ -33,11 +78,40 @@
try:
view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
except ValueError:
- raise BufferError("could not create buffer from object")
+ if not space.isinstance_w(w_obj, space.w_str):
+ # XXX Python 3?
+ raise BufferError("could not create buffer from object")
+ view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp(space.str_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)
return ret
+ at cpython_api([PyObject], Py_bufferP, error=CANNOT_FAIL)
+def PyMemoryView_GET_BUFFER(space, w_obj):
+ """Return a pointer to the buffer-info structure wrapped by the given
+ object. The object must be a memoryview instance; this macro doesn't
+ check its type, you must do it yourself or you will risk crashes."""
+ if not isinstance(w_obj, W_MemoryView):
+ return lltype.nullptr(Py_buffer)
+ py_memobj = rffi.cast(PyMemoryViewObject, as_pyobj(space, w_obj)) # no inc_ref
+ view = py_memobj.c_view
+ ndim = w_obj.buf.getndim()
+ if ndim >= Py_MAX_NDIMS:
+ # XXX warn?
+ return view
+ fill_Py_buffer(space, w_obj.buf, view)
+ try:
+ view.c_buf = rffi.cast(rffi.VOIDP, w_obj.buf.get_raw_address())
+ #view.c_obj = make_ref(space, w_obj) # NO - this creates a ref cycle!
+ rffi.setintfield(view, 'c_readonly', w_obj.buf.readonly)
+ except ValueError:
+ w_s = w_obj.descr_tobytes(space)
+ view.c_obj = make_ref(space, w_s)
+ view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp(space.str_w(w_s), track_allocation=False))
+ rffi.setintfield(view, 'c_readonly', 1)
+ return view
+
def fill_Py_buffer(space, buf, view):
# c_buf, c_obj have been filled in
ndim = buf.getndim()
@@ -111,7 +185,7 @@
sd *= dim
return 1
- at cpython_api([lltype.Ptr(Py_buffer), lltype.Char], rffi.INT_real, error=CANNOT_FAIL)
+ at cpython_api([Py_bufferP, lltype.Char], rffi.INT_real, error=CANNOT_FAIL)
def PyBuffer_IsContiguous(space, view, fort):
"""Return 1 if the memory defined by the view is C-style (fort is
'C') or Fortran-style (fort is 'F') contiguous or either one
@@ -132,7 +206,7 @@
def PyMemoryView_FromObject(space, w_obj):
return space.call_method(space.builtin, "memoryview", w_obj)
- at cpython_api([lltype.Ptr(Py_buffer)], PyObject)
+ at cpython_api([Py_bufferP], PyObject)
def PyMemoryView_FromBuffer(space, view):
"""Create a memoryview object wrapping the given buffer-info structure view.
The memoryview object then owns the buffer, which means you shouldn't
@@ -149,29 +223,3 @@
# XXX needed for numpy on py3k
raise NotImplementedError('PyMemoryView_GET_BASE')
- at cpython_api([PyObject], lltype.Ptr(Py_buffer), error=CANNOT_FAIL)
-def PyMemoryView_GET_BUFFER(space, w_obj):
- """Return a pointer to the buffer-info structure wrapped by the given
- object. The object must be a memoryview instance; this macro doesn't
- check its type, you must do it yourself or you will risk crashes."""
- view = lltype.malloc(Py_buffer, flavor='raw', zero=True)
- if not isinstance(w_obj, W_MemoryView):
- return view
- ndim = w_obj.buf.getndim()
- if ndim >= Py_MAX_NDIMS:
- # XXX warn?
- return view
- fill_Py_buffer(space, w_obj.buf, view)
- try:
- view.c_buf = rffi.cast(rffi.VOIDP, w_obj.buf.get_raw_address())
- view.c_obj = make_ref(space, w_obj)
- rffi.setintfield(view, 'c_readonly', w_obj.buf.readonly)
- isstr = False
- except ValueError:
- w_s = w_obj.descr_tobytes(space)
- view.c_obj = make_ref(space, w_s)
- view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp(space.str_w(w_s), track_allocation=False))
- rffi.setintfield(view, 'c_readonly', 1)
- isstr = True
- return view
-
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
@@ -4,6 +4,7 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.rarithmetic import widen
+from rpython.rlib import rgc # Force registration of gc.collect
from pypy.module.cpyext.api import (
cpython_api, generic_cpy_call, PyObject, Py_ssize_t, Py_TPFLAGS_CHECKTYPES,
mangle_name, pypy_decl, Py_buffer, Py_bufferP)
@@ -12,8 +13,8 @@
getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry,
ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc,
cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc,
- readbufferproc, getbufferproc, ssizessizeobjargproc)
-from pypy.module.cpyext.pyobject import make_ref, Py_DecRef
+ readbufferproc, getbufferproc, releasebufferproc, ssizessizeobjargproc)
+from pypy.module.cpyext.pyobject import make_ref, decref
from pypy.module.cpyext.pyerrors import PyErr_Occurred
from pypy.module.cpyext.memoryobject import fill_Py_buffer
from pypy.module.cpyext.state import State
@@ -99,7 +100,7 @@
if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
return space.w_NotImplemented
- Py_DecRef(space, ref)
+ decref(space, ref)
return generic_cpy_call(space, func_binary, w_self, args_w[0])
def wrap_binaryfunc_r(space, w_self, w_args, func):
@@ -110,7 +111,7 @@
if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
return space.w_NotImplemented
- Py_DecRef(space, ref)
+ decref(space, ref)
return generic_cpy_call(space, func_binary, args_w[0], w_self)
def wrap_ternaryfunc(space, w_self, w_args, func):
@@ -132,7 +133,7 @@
if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
return space.w_NotImplemented
- Py_DecRef(space, ref)
+ decref(space, ref)
arg3 = space.w_None
if len(args_w) > 1:
arg3 = args_w[1]
@@ -322,11 +323,14 @@
# Similar to Py_buffer
_immutable_ = True
- def __init__(self, ptr, size, w_obj, format='B', shape=None,
- strides=None, ndim=1, itemsize=1, readonly=True):
+ def __init__(self, space, ptr, size, w_obj, format='B', shape=None,
+ strides=None, ndim=1, itemsize=1, readonly=True,
+ releasebuffer=None):
+ self.space = space
self.ptr = ptr
self.size = size
self.w_obj = w_obj # kept alive
+ self.pyobj = make_ref(space, w_obj)
self.format = format
if not shape:
self.shape = [size]
@@ -339,6 +343,27 @@
self.ndim = ndim
self.itemsize = itemsize
self.readonly = readonly
+ self.releasebufferproc = releasebuffer
+
+ def releasebuffer(self):
+ if self.pyobj:
+ 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]
+ pybuf.c_format = rffi.str2charp(self.format)
+ generic_cpy_call(self.space, func_target, self.pyobj, pybuf)
+ self.releasebufferproc = rffi.cast(rffi.VOIDP, 0)
def getlength(self):
return self.size
@@ -368,26 +393,61 @@
# absolutely no safety checks, what could go wrong?
self.ptr[index] = char
+class FQ(rgc.FinalizerQueue):
+ Class = CPyBuffer
+ def finalizer_trigger(self):
+ while 1:
+ buf = self.next_dead()
+ if not buf:
+ break
+ buf.releasebuffer()
+
+fq = FQ()
+
def wrap_getreadbuffer(space, w_self, w_args, func):
func_target = rffi.cast(readbufferproc, func)
+ py_obj = make_ref(space, w_self)
+ py_type = py_obj.c_ob_type
+ releasebuffer = rffi.cast(rffi.VOIDP, 0)
+ if py_type.c_tp_as_buffer:
+ releasebuffer = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+ decref(space, py_obj)
with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
index = rffi.cast(Py_ssize_t, 0)
size = generic_cpy_call(space, func_target, w_self, index, ptr)
if size < 0:
space.fromcache(State).check_and_raise_exception(always=True)
- return space.newbuffer(CPyBuffer(ptr[0], size, w_self))
+ buf = CPyBuffer(space, ptr[0], size, w_self,
+ releasebuffer=releasebuffer)
+ fq.register_finalizer(buf)
+ return space.newbuffer(buf)
def wrap_getwritebuffer(space, w_self, w_args, func):
func_target = rffi.cast(readbufferproc, func)
+ py_obj = make_ref(space, w_self)
+ py_type = py_obj.c_ob_type
+ decref(space, py_obj)
+ releasebuffer = rffi.cast(rffi.VOIDP, 0)
+ if py_type.c_tp_as_buffer:
+ releasebuffer = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
index = rffi.cast(Py_ssize_t, 0)
size = generic_cpy_call(space, func_target, w_self, index, ptr)
if size < 0:
space.fromcache(State).check_and_raise_exception(always=True)
- return space.newbuffer(CPyBuffer(ptr[0], size, w_self, readonly=False))
+ buf = CPyBuffer(space, ptr[0], size, w_self, readonly=False,
+ releasebuffer=releasebuffer)
+ fq.register_finalizer(buf)
+ return space.newbuffer(buf)
def wrap_getbuffer(space, w_self, w_args, func):
func_target = rffi.cast(getbufferproc, func)
+ py_obj = make_ref(space, w_self)
+ py_type = py_obj.c_ob_type
+ releasebuffer = rffi.cast(rffi.VOIDP, 0)
+ if py_type.c_tp_as_buffer:
+ releasebuffer = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+ decref(space, py_obj)
with lltype.scoped_alloc(Py_buffer) as pybuf:
_flags = 0
if space.len_w(w_args) > 0:
@@ -408,10 +468,13 @@
format = rffi.charp2str(pybuf.c_format)
else:
format = 'B'
- return space.newbuffer(CPyBuffer(ptr, size, w_self, format=format,
+ buf = CPyBuffer(space, ptr, size, w_self, format=format,
ndim=ndim, shape=shape, strides=strides,
itemsize=pybuf.c_itemsize,
- readonly=widen(pybuf.c_readonly)))
+ readonly=widen(pybuf.c_readonly),
+ releasebuffer = releasebuffer)
+ fq.register_finalizer(buf)
+ return space.newbuffer(buf)
def get_richcmp_func(OP_CONST):
def inner(space, w_self, w_args, func):
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -10,11 +10,7 @@
PyMethodDef = rffi.VOIDP
PyGetSetDef = rffi.VOIDP
PyMemberDef = rffi.VOIDP
-Py_buffer = rffi.VOIDP
va_list = rffi.VOIDP
-PyDateTime_Date = rffi.VOIDP
-PyDateTime_DateTime = rffi.VOIDP
-PyDateTime_Time = rffi.VOIDP
wrapperbase = rffi.VOIDP
FILE = rffi.VOIDP
PyFileObject = rffi.VOIDP
diff --git a/pypy/module/cpyext/test/buffer_test.c b/pypy/module/cpyext/test/buffer_test.c
--- a/pypy/module/cpyext/test/buffer_test.c
+++ b/pypy/module/cpyext/test/buffer_test.c
@@ -185,19 +185,6 @@
(initproc)PyMyArray_init, /* tp_init */
};
-static PyObject*
-test_buffer(PyObject* self, PyObject* args)
-{
- Py_buffer* view = NULL;
- PyObject* obj = PyTuple_GetItem(args, 0);
- PyObject* memoryview = PyMemoryView_FromObject(obj);
- if (memoryview == NULL)
- return PyInt_FromLong(-1);
- view = PyMemoryView_GET_BUFFER(memoryview);
- Py_DECREF(memoryview);
- return PyInt_FromLong(view->len);
-}
-
/* Copied from numpy tests */
/*
* Create python string from a FLAG and or the corresponding PyBuf flag
@@ -308,7 +295,6 @@
static PyMethodDef buffer_functions[] = {
- {"test_buffer", (PyCFunction)test_buffer, METH_VARARGS, NULL},
{"get_buffer_info", (PyCFunction)get_buffer_info, METH_VARARGS, NULL},
{NULL, NULL} /* Sentinel */
};
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
@@ -64,3 +64,58 @@
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
@@ -20,16 +20,16 @@
def test_frombuffer(self, space, api):
w_buf = space.newbuffer(StringBuffer("hello"))
w_memoryview = api.PyMemoryView_FromObject(w_buf)
- w_view = api.PyMemoryView_GET_BUFFER(w_memoryview)
- assert w_view.c_ndim == 1
- f = rffi.charp2str(w_view.c_format)
+ view = api.PyMemoryView_GET_BUFFER(w_memoryview)
+ assert view.c_ndim == 1
+ f = rffi.charp2str(view.c_format)
assert f == 'B'
- assert w_view.c_shape[0] == 5
- assert w_view.c_strides[0] == 1
- assert w_view.c_len == 5
- o = rffi.charp2str(w_view.c_buf)
+ assert view.c_shape[0] == 5
+ assert view.c_strides[0] == 1
+ assert view.c_len == 5
+ o = rffi.charp2str(view.c_buf)
assert o == 'hello'
- w_mv = api.PyMemoryView_FromBuffer(w_view)
+ w_mv = api.PyMemoryView_FromBuffer(view)
for f in ('format', 'itemsize', 'ndim', 'readonly',
'shape', 'strides', 'suboffsets'):
w_f = space.wrap(f)
@@ -37,7 +37,7 @@
space.getattr(w_memoryview, w_f))
class AppTestBufferProtocol(AppTestCpythonExtensionBase):
- def test_buffer_protocol(self):
+ def test_buffer_protocol_app(self):
import struct
module = self.import_module(name='buffer_test')
arr = module.PyMyArray(10)
@@ -48,8 +48,41 @@
s = y[3]
assert len(s) == struct.calcsize('i')
assert s == struct.pack('i', 3)
- viewlen = module.test_buffer(arr)
- assert viewlen == y.itemsize * len(y)
+
+ def test_buffer_protocol_capi(self):
+ foo = self.import_extension('foo', [
+ ("get_len", "METH_VARARGS",
+ """
+ Py_buffer view;
+ PyObject* obj = PyTuple_GetItem(args, 0);
+ long ret, vlen;
+ memset(&view, 0, sizeof(Py_buffer));
+ ret = PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO);
+ if (ret != 0)
+ return NULL;
+ vlen = view.len / view.itemsize;
+ PyBuffer_Release(&view);
+ return PyInt_FromLong(vlen);
+ """),
+ ("test_buffer", "METH_VARARGS",
+ """
+ Py_buffer* view = NULL;
+ PyObject* obj = PyTuple_GetItem(args, 0);
+ PyObject* memoryview = PyMemoryView_FromObject(obj);
+ if (memoryview == NULL)
+ return PyInt_FromLong(-1);
+ view = PyMemoryView_GET_BUFFER(memoryview);
+ Py_DECREF(memoryview);
+ return PyInt_FromLong(view->len / view->itemsize);
+ """)])
+ module = self.import_module(name='buffer_test')
+ arr = module.PyMyArray(10)
+ ten = foo.get_len(arr)
+ assert ten == 10
+ ten = foo.get_len('1234567890')
+ assert ten == 10
+ ten = foo.test_buffer(arr)
+ assert ten == 10
@pytest.mark.skipif(only_pypy, reason='pypy only test')
def test_buffer_info(self):
diff --git a/pypy/module/cpyext/typeobjectdefs.py b/pypy/module/cpyext/typeobjectdefs.py
--- a/pypy/module/cpyext/typeobjectdefs.py
+++ b/pypy/module/cpyext/typeobjectdefs.py
@@ -60,7 +60,7 @@
segcountproc = P(FT([PyO, Py_ssize_tP], Py_ssize_t))
charbufferproc = P(FT([PyO, Py_ssize_t, rffi.CCHARPP], Py_ssize_t))
getbufferproc = P(FT([PyO, Py_bufferP, rffi.INT_real], rffi.INT_real))
-releasebufferproc = rffi.VOIDP
+releasebufferproc = P(FT([PyO, Py_bufferP], Void))
PyGetSetDef = cpython_struct("PyGetSetDef", (
diff --git a/pypy/objspace/std/bufferobject.py b/pypy/objspace/std/bufferobject.py
--- a/pypy/objspace/std/bufferobject.py
+++ b/pypy/objspace/std/bufferobject.py
@@ -17,6 +17,9 @@
assert isinstance(buf, Buffer)
self.buf = buf
+ def _finalize_(self):
+ return self.buf.releasebuffer()
+
def buffer_w(self, space, flags):
space.check_buf_flags(flags, self.buf.readonly)
return self.buf
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -321,8 +321,9 @@
def newseqiter(self, w_obj):
return W_SeqIterObject(w_obj)
- def newbuffer(self, w_obj):
- return W_Buffer(w_obj)
+ def newbuffer(self, obj):
+ ret = W_Buffer(obj)
+ return ret
def newbytes(self, s):
return W_BytesObject(s)
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -6,7 +6,7 @@
class Buffer(object):
"""Abstract base class for buffers."""
- __slots__ = ['readonly']
+ _attrs_ = ['readonly']
_immutable_ = True
def getlength(self):
@@ -75,8 +75,11 @@
def getstrides(self):
return [1]
+ def releasebuffer(self):
+ pass
+
class StringBuffer(Buffer):
- __slots__ = ['value']
+ _attrs_ = ['readonly', 'value']
_immutable_ = True
def __init__(self, value):
@@ -107,7 +110,7 @@
class SubBuffer(Buffer):
- __slots__ = ['buffer', 'offset', 'size']
+ _attrs_ = ['buffer', 'offset', 'size', 'readonly']
_immutable_ = True
def __init__(self, buffer, offset, size):
More information about the pypy-commit
mailing list