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

Reply via email to