Author: Matti Picus <[email protected]>
Branch:
Changeset: r89484:28d4f9418183
Date: 2017-01-10 23:40 +0200
http://bitbucket.org/pypy/pypy/changeset/28d4f9418183/
Log: merge cpyext-FromBuffer which fixes ref leak in
PyMemoryView_FromBuffer
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
@@ -124,6 +124,7 @@
METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O Py_TPFLAGS_HAVE_INPLACEOPS
Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_HAVE_NEWBUFFER
Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES Py_MAX_NDIMS
+PyBUF_FORMAT PyBUF_ND PyBUF_STRIDES
""".split()
for name in constant_names:
setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name))
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
@@ -3,7 +3,8 @@
build_type_checkers, Py_ssize_tP, PyObjectFields, cpython_struct,
bootstrap_function, Py_bufferP, slot_function)
from pypy.module.cpyext.pyobject import (
- PyObject, make_ref, as_pyobj, incref, decref, from_ref, make_typedescr)
+ PyObject, make_ref, as_pyobj, incref, 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
@@ -28,7 +29,7 @@
basestruct=PyMemoryViewObject.TO,
attach=memory_attach,
dealloc=memory_dealloc,
- #realize=memory_realize,
+ realize=memory_realize,
)
def memory_attach(space, py_obj, w_obj, w_userdata=None):
@@ -54,11 +55,35 @@
track_allocation=False))
rffi.setintfield(view, 'c_readonly', 1)
-def memory_realize(space, py_obj):
+def memory_realize(space, obj):
"""
Creates the memory object in the interpreter
"""
- raise oefmt(space.w_NotImplementedError, "cannot call this yet")
+ from pypy.module.cpyext.slotdefs import CPyBuffer, fq
+ py_mem = rffi.cast(PyMemoryViewObject, obj)
+ view = py_mem.c_view
+ ndim = widen(view.c_ndim)
+ shape = None
+ if view.c_shape:
+ shape = [view.c_shape[i] for i in range(ndim)]
+ strides = None
+ if view.c_strides:
+ strides = [view.c_strides[i] for i in range(ndim)]
+ format = 'B'
+ if view.c_format:
+ format = rffi.charp2str(view.c_format)
+ buf = CPyBuffer(space, view.c_buf, view.c_len, from_ref(space, view.c_obj),
+ format=format, shape=shape, strides=strides,
+ ndim=ndim, itemsize=view.c_itemsize,
+ readonly=widen(view.c_readonly))
+ # Ensure view.c_buf is released upon object finalization
+ fq.register_finalizer(buf)
+ # Allow subclassing W_MemeoryView
+ w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
+ w_obj = space.allocate_instance(W_MemoryView, w_type)
+ w_obj.__init__(buf)
+ track_reference(space, obj, w_obj)
+ return w_obj
@slot_function([PyObject], lltype.Void)
def memory_dealloc(space, py_obj):
@@ -208,17 +233,41 @@
py_memview = make_ref(space, w_memview, w_obj)
return py_memview
-@cpython_api([Py_bufferP], PyObject)
+@cpython_api([Py_bufferP], PyObject, result_is_ll=True)
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
try to release it yourself: it will be released on deallocation of the
memoryview object."""
- assert view.c_obj
- w_obj = from_ref(space, view.c_obj)
- if isinstance(w_obj, W_MemoryView):
- return w_obj
- return space.call_method(space.builtin, "memoryview", w_obj)
+ # XXX this should allocate a PyMemoryViewObject and
+ # copy view into obj.c_view, without creating a new view.c_obj
+ typedescr = get_typedescr(W_MemoryView.typedef)
+ py_obj = typedescr.allocate(space, space.w_memoryview)
+ py_mem = rffi.cast(PyMemoryViewObject, py_obj)
+ mview = py_mem.c_view
+ mview.c_buf = view.c_buf
+ mview.c_obj = view.c_obj
+ mview.c_len = view.c_len
+ mview.c_itemsize = view.c_itemsize
+ mview.c_readonly = view.c_readonly
+ mview.c_ndim = view.c_ndim
+ mview.c_format = view.c_format
+ if view.c_strides == rffi.cast(Py_ssize_tP, view.c__strides):
+ py_mem.c_view.c_strides = rffi.cast(Py_ssize_tP,
py_mem.c_view.c__strides)
+ for i in range(view.c_ndim):
+ py_mem.c_view.c_strides[i] = view.c_strides[i]
+ else:
+ # some externally allocated memory chunk
+ py_mem.c_view.c_strides = view.c_strides
+ if view.c_shape == rffi.cast(Py_ssize_tP, view.c__shape):
+ py_mem.c_view.c_shape = rffi.cast(Py_ssize_tP, py_mem.c_view.c__shape)
+ for i in range(view.c_ndim):
+ py_mem.c_view.c_shape[i] = view.c_shape[i]
+ else:
+ # some externally allocated memory chunk
+ py_mem.c_view.c_shape = view.c_shape
+ # XXX ignore suboffsets?
+ return py_obj
@cpython_api([PyObject], PyObject)
def PyMemoryView_GET_BASE(space, w_obj):
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -2,6 +2,7 @@
from pypy.module.cpyext.api import (
cpython_api, generic_cpy_call, CANNOT_FAIL, Py_ssize_t, Py_ssize_tP,
PyVarObject, Py_buffer, size_t, slot_function,
+ PyBUF_FORMAT, PyBUF_ND, PyBUF_STRIDES,
Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT,
Py_GE, CONST_STRING, CONST_STRINGP, FILEP, fwrite)
from pypy.module.cpyext.pyobject import (
@@ -486,22 +487,28 @@
Fills in a buffer-info structure correctly for an exporter that can only
share a contiguous chunk of memory of "unsigned bytes" of the given
length. Returns 0 on success and -1 (with raising an error) on error.
-
- This is not a complete re-implementation of the CPython API; it only
- provides a subset of CPython's behavior.
"""
if flags & PyBUF_WRITABLE and readonly:
raise oefmt(space.w_ValueError, "Object is not writable")
view.c_buf = buf
view.c_len = length
view.c_obj = obj
- Py_IncRef(space, obj)
+ if obj:
+ Py_IncRef(space, obj)
view.c_itemsize = 1
rffi.setintfield(view, 'c_readonly', readonly)
- rffi.setintfield(view, 'c_ndim', 0)
+ rffi.setintfield(view, 'c_ndim', 1)
view.c_format = lltype.nullptr(rffi.CCHARP.TO)
+ if (flags & PyBUF_FORMAT) == PyBUF_FORMAT:
+ view.c_format = rffi.str2charp("B")
view.c_shape = lltype.nullptr(Py_ssize_tP.TO)
+ if (flags & PyBUF_ND) == PyBUF_ND:
+ view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape)
+ view.c_shape[0] = view.c_len
view.c_strides = lltype.nullptr(Py_ssize_tP.TO)
+ if (flags & PyBUF_STRIDES) == PyBUF_STRIDES:
+ view.c_strides = rffi.cast(Py_ssize_tP, view.c__strides)
+ view.c_strides[0] = view.c_itemsize
view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
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
@@ -324,7 +324,7 @@
def __init__(self, space, ptr, size, w_obj, format='B', shape=None,
strides=None, ndim=1, itemsize=1, readonly=True,
- releasebuffer=None):
+ releasebufferproc=rffi.cast(rffi.VOIDP, 0)):
self.space = space
self.ptr = ptr
self.size = size
@@ -342,7 +342,7 @@
self.ndim = ndim
self.itemsize = itemsize
self.readonly = readonly
- self.releasebufferproc = releasebuffer
+ self.releasebufferproc = releasebufferproc
def releasebuffer(self):
if self.pyobj:
@@ -360,7 +360,10 @@
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)
+ 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)
@@ -407,9 +410,9 @@
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)
+ rbp = 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)
+ rbp = 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)
@@ -417,7 +420,7 @@
if size < 0:
space.fromcache(State).check_and_raise_exception(always=True)
buf = CPyBuffer(space, ptr[0], size, w_self,
- releasebuffer=releasebuffer)
+ releasebufferproc=rbp)
fq.register_finalizer(buf)
return space.newbuffer(buf)
@@ -426,16 +429,16 @@
py_obj = make_ref(space, w_self)
py_type = py_obj.c_ob_type
decref(space, py_obj)
- releasebuffer = rffi.cast(rffi.VOIDP, 0)
+ rbp = 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)
+ rbp = 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)
buf = CPyBuffer(space, ptr[0], size, w_self, readonly=False,
- releasebuffer=releasebuffer)
+ releasebufferproc=rbp)
fq.register_finalizer(buf)
return space.newbuffer(buf)
@@ -443,9 +446,9 @@
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)
+ rbp = 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)
+ rbp = 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
@@ -471,7 +474,7 @@
ndim=ndim, shape=shape, strides=strides,
itemsize=pybuf.c_itemsize,
readonly=widen(pybuf.c_readonly),
- releasebuffer = releasebuffer)
+ releasebufferproc = rbp)
fq.register_finalizer(buf)
return space.newbuffer(buf)
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
@@ -30,7 +30,7 @@
assert view.c_len == 5
o = rffi.charp2str(view.c_buf)
assert o == 'hello'
- w_mv = api.PyMemoryView_FromBuffer(view)
+ w_mv = from_ref(space, api.PyMemoryView_FromBuffer(view))
for f in ('format', 'itemsize', 'ndim', 'readonly',
'shape', 'strides', 'suboffsets'):
w_f = space.wrap(f)
@@ -44,6 +44,7 @@
("fillinfo", "METH_VARARGS",
"""
Py_buffer buf;
+ PyObject * ret = NULL;
PyObject *str = PyBytes_FromString("hello, world.");
if (PyBuffer_FillInfo(&buf, str, PyBytes_AsString(str), 13,
0, 0)) {
@@ -55,7 +56,14 @@
*/
Py_DECREF(str);
- return PyMemoryView_FromBuffer(&buf);
+ ret = PyMemoryView_FromBuffer(&buf);
+ if (((PyMemoryViewObject*)ret)->view.obj != buf.obj)
+ {
+ PyErr_SetString(PyExc_ValueError, "leaked ref");
+ Py_DECREF(ret);
+ return NULL;
+ }
+ return ret;
""")])
result = module.fillinfo()
assert b"hello, world." == result
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit