Author: Matti Picus <matti.pi...@gmail.com> 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 pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit