[pypy-commit] pypy memoryview-attributes: add test of getitem, fix CPyBuffer object so they pass. Add __buffer__ slot to str.

mattip pypy.commits at gmail.com
Fri Aug 19 18:27:44 EDT 2016


Author: Matti Picus <matti.picus at gmail.com>
Branch: memoryview-attributes
Changeset: r86333:0a85b1b3bb7f
Date: 2016-08-20 10:26 +1200
http://bitbucket.org/pypy/pypy/changeset/0a85b1b3bb7f/

Log:	add test of getitem, fix CPyBuffer object so they pass. Add
	__buffer__ slot to str.

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
@@ -9,7 +9,7 @@
     """Return 1 if obj supports the buffer interface otherwise 0."""
     as_buffer = pyobj.c_ob_type.c_tp_as_buffer
     flags = pyobj.c_ob_type.c_tp_flags
-    if (flags & Py_TPFLAGS_HAVE_NEWBUFFER and as_buffer.bf_getbuffer):
+    if (flags & Py_TPFLAGS_HAVE_NEWBUFFER and as_buffer.c_bf_getbuffer):
         return 1
     return 0  
 
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
@@ -5,7 +5,7 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (
     cpython_api, generic_cpy_call, PyObject, Py_ssize_t, Py_TPFLAGS_CHECKTYPES,
-    mangle_name, pypy_decl, Py_buffer)
+    mangle_name, pypy_decl, Py_buffer, Py_bufferP)
 from pypy.module.cpyext.typeobjectdefs import (
     unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc, ternaryfunc,
     getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry,
@@ -301,24 +301,42 @@
     # Similar to Py_buffer
     _immutable_ = True
 
-    def __init__(self, ptr, size, w_obj):
-        # XXX leak of ptr
+    def __init__(self, ptr, size, w_obj, pybuf=None):
         self.ptr = ptr
         self.size = size
         self.w_obj = w_obj # kept alive
-        self.readonly = True
+        if pybuf is None:
+            self.format = 'B'
+            self.shape = [size]
+            self.strides = [1]
+            self.ndim = 1
+            self.itemsize = 1
+            self.readonly = True
+        else:
+            self.format = rffi.charp2str(pybuf.c_format)
+            self.ndim = pybuf.c_ndim
+            self.shape = [pybuf.c_shape[i] for i in range(self.ndim)]
+            self.strides = [pybuf.c_strides[i] for i in range(self.ndim)]
+            self.itemsize = pybuf.c_itemsize 
+            self.readonly = pybuf.c_readonly
 
     def getlength(self):
         return self.size
 
     def getitem(self, index):
-        return self.ptr[index]
+        start = index * self.itemsize
+        stop = (index + 1) * self.itemsize
+        return ''.join([self.ptr[i] for i in range(start, stop)])
 
     def get_raw_address(self):
         return rffi.cast(rffi.CCHARP, self.ptr)
 
     def getformat(self):
-        return rffi.charp2str(self.ptr.c_format)
+        return self.format
+
+    def getshape(self):
+        return self.shape
+
 
 def wrap_getreadbuffer(space, w_self, w_args, func):
     func_target = rffi.cast(readbufferproc, func)
@@ -332,13 +350,15 @@
 def wrap_getbuffer(space, w_self, w_args, func):
     func_target = rffi.cast(getbufferproc, func)
     # XXX leak
-    pybuf = lltype.malloc(Py_buffer, flavor='raw', track_allocation=False)
-    # XXX flags are not in w_args?
-    flags = rffi.cast(rffi.INT_real,0)
-    size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
-    if size < 0:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return space.newbuffer(CPyBuffer(pybuf, size, w_self))
+    with lltype.scoped_alloc(Py_buffer) as pybuf:
+        # XXX flags are not in w_args?
+        flags = rffi.cast(rffi.INT_real,0)
+        size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
+        if size < 0:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        ptr = pybuf.c_buf
+        size = pybuf.c_len
+        return space.newbuffer(CPyBuffer(ptr, size, w_self, pybuf))
 
 def get_richcmp_func(OP_CONST):
     def inner(space, w_self, w_args, func):
@@ -504,8 +524,6 @@
         def slot_tp_getattro(space, w_self, w_name):
             return space.call_function(getattr_fn, w_self, w_name)
         api_func = slot_tp_getattro.api_func
-    elif name == 'tp_as_buffer':
-        raise NotImplementedError
     elif name == 'tp_call':
         call_fn = w_type.getdictvalue(space, '__call__')
         if call_fn is None:
@@ -561,6 +579,19 @@
                              w_stararg=w_args, w_starstararg=w_kwds)
             return space.call_args(space.get(new_fn, w_self), args)
         api_func = slot_tp_new.api_func
+    elif name == 'tp_as_buffer.c_bf_getbuffer':
+        buf_fn = w_type.getdictvalue(space, '__buffer__')
+        if buf_fn is None:
+            return
+        @cpython_api([PyObject, Py_bufferP, rffi.INT_real], 
+                rffi.INT_real, header=None, error=-1)
+        @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name))
+        def buff_w(space, w_self, w_args, w_kwds):
+            # XXX this is wrong, needs a test
+            args = Arguments(space, [w_self],
+                             w_stararg=w_args, w_starstararg=w_kwds)
+            return space.call_args(space.get(buff_fn, w_self), args)
+        api_func = buff_w.api_func
     else:
         # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce
         # tp_as_sequence.c_sq_contains, tp_as_sequence.c_sq_length
@@ -881,6 +912,7 @@
 
 # partial sort to solve some slot conflicts:
 # Number slots before Mapping slots before Sequence slots.
+# also prefer the new buffer interface
 # These are the only conflicts between __name__ methods
 def slotdef_sort_key(slotdef):
     if slotdef.slot_name.startswith('tp_as_number'):
@@ -889,6 +921,10 @@
         return 2
     if slotdef.slot_name.startswith('tp_as_sequence'):
         return 3
+    if slodef.slot_name == 'tp_as_buffer.c_bf_getbuffer':
+        return 100
+    if slodef.slot_name == 'tp_as_buffer.c_bf_getreadbuffer':
+        return 101
     return 0
 slotdefs = sorted(slotdefs, key=slotdef_sort_key)
 
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
@@ -3,7 +3,6 @@
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 
 
-
 class TestMemoryViewObject(BaseApiTest):
     def test_fromobject(self, space, api):
         if space.is_true(space.lt(space.sys.get('version_info'),
@@ -13,14 +12,20 @@
         w_hello = space.newbytes("hello")
         assert api.PyObject_CheckBuffer(w_hello)
         w_view = api.PyMemoryView_FromObject(w_hello)
+        w_char = space.call_method(w_view, '__getitem__', space.wrap(0))
+        assert w_char == space.wrap('h')
         w_bytes = space.call_method(w_view, "tobytes")
         assert space.unwrap(w_bytes) == "hello"
 
 
 class AppTestBufferProtocol(AppTestCpythonExtensionBase):
     def test_buffer_protocol(self):
+        import struct
         module = self.import_module(name='buffer_test')
         arr = module.PyMyArray(10)
         y = memoryview(arr)
         assert y.format == 'i'
+        s = y[3]
+        assert len(s) == struct.calcsize('i')
+        assert s == struct.pack('i', 3)
 
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -17,7 +17,8 @@
     generic_cpy_call, Py_TPFLAGS_READY, Py_TPFLAGS_READYING,
     Py_TPFLAGS_HEAPTYPE, METH_VARARGS, METH_KEYWORDS, CANNOT_FAIL,
     Py_TPFLAGS_HAVE_GETCHARBUFFER, build_type_checkers, StaticObjectBuilder,
-    PyObjectFields, Py_TPFLAGS_BASETYPE, PyTypeObject, PyTypeObjectPtr)
+    PyObjectFields, Py_TPFLAGS_BASETYPE, PyTypeObject, PyTypeObjectPtr,
+    Py_TPFLAGS_HAVE_NEWBUFFER)
 from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject,
     W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction_typedef, PyMethodDef,
     W_PyCMethodObject, W_PyCFunctionObject)
@@ -608,6 +609,7 @@
                 bf_getwritebuffer.api_func.get_wrapper(space))
     pto.c_tp_as_buffer = c_buf
     pto.c_tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER
+    pto.c_tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER
 
 @cpython_api([PyObject], lltype.Void, header=None)
 def type_dealloc(space, obj):
diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py
--- a/pypy/objspace/std/bytesobject.py
+++ b/pypy/objspace/std/bytesobject.py
@@ -464,6 +464,9 @@
         raise oefmt(space.w_TypeError,
                     "Cannot use string as modifiable buffer")
 
+    def descr_getbuffer(self, space):
+        return self
+
     charbuf_w = str_w
 
     def listview_bytes(self):
@@ -925,6 +928,7 @@
     translate = interpindirect2app(W_AbstractBytesObject.descr_translate),
     upper = interpindirect2app(W_AbstractBytesObject.descr_upper),
     zfill = interpindirect2app(W_AbstractBytesObject.descr_zfill),
+    __buffer__ = interp2app(W_BytesObject.descr_getbuffer),
 
     format = interpindirect2app(W_BytesObject.descr_format),
     __format__ = interpindirect2app(W_BytesObject.descr__format__),


More information about the pypy-commit mailing list