Author: Richard Plangger <planri...@gmail.com>
Branch: py3k
Changeset: r86459:281ace7115cb
Date: 2016-08-24 09:59 +0200
http://bitbucket.org/pypy/pypy/changeset/281ace7115cb/

Log:    merge default

diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -166,8 +166,8 @@
         else:
             return self.value
 
-    def __buffer__(self):
-        return memoryview(self._buffer)
+    def __buffer__(self, flags):
+        return buffer(self._buffer)
 
     def _get_b_base(self):
         try:
@@ -208,7 +208,7 @@
 
 def cdata_from_address(self, address):
     # fix the address: turn it into as unsigned, in case it's a negative number
-    address = address & (sys.maxsize * 2 + 1)
+    address = address & (sys.maxint * 2 + 1)
     instance = self.__new__(self)
     lgt = getattr(self, '_length_', 1)
     instance._buffer = self._ffiarray.fromaddress(address, lgt)
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
@@ -150,3 +150,8 @@
 
 Reduce the size of the generated C code by constant-folding ``we_are_jitted``
 in non-jitcode.
+
+.. branch: memoryview-attributes
+
+Support for memoryview attributes (format, itemsize, ...).
+Extends the cpyext emulation layer.
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -585,6 +585,11 @@
         self.sys = Module(self, w_name)
         self.sys.install()
 
+        from pypy.module.imp import Module
+        w_name = self.wrap('imp')
+        mod = Module(self, w_name)
+        mod.install()
+
         from pypy.module.__builtin__ import Module
         w_name = self.wrap('builtins')
         self.builtin = Module(self, w_name)
@@ -1993,7 +1998,7 @@
 
 ObjSpace.IrregularOpTable = [
     'wrap',
-    'bytes_w',
+    'str_w',
     'int_w',
     'float_w',
     'uint_w',
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
@@ -120,7 +120,7 @@
 Py_TPFLAGS_READY Py_TPFLAGS_READYING Py_TPFLAGS_HAVE_GETCHARBUFFER
 METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE
 METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O
-Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS
+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_CLEANUP_SUPPORTED
 """.split()
@@ -651,6 +651,7 @@
         #('smalltable', rffi.CFixedArray(Py_ssize_t, 2)),
         ('internal', rffi.VOIDP)
         ))
+Py_bufferP = lltype.Ptr(Py_buffer)
 
 @specialize.memo()
 def is_PyObject(TYPE):
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
@@ -1,8 +1,37 @@
+from pypy.interpreter.error import oefmt
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rlib import buffer
 from pypy.module.cpyext.api import (
     cpython_api, CANNOT_FAIL, Py_buffer)
-from pypy.module.cpyext.pyobject import PyObject, Py_DecRef
+from pypy.module.cpyext.pyobject import PyObject
+
+@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
+def PyObject_CheckBuffer(space, w_obj):
+    """Return 1 if obj supports the buffer interface otherwise 0."""
+    return 0  # the bf_getbuffer field is never filled by cpyext
+
+@cpython_api([PyObject, lltype.Ptr(Py_buffer), 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
+    never be NULL.  The flags argument is a bit field indicating what
+    kind of buffer the caller is prepared to deal with and therefore what
+    kind of buffer the exporter is allowed to return.  The buffer interface
+    allows for complicated memory sharing possibilities, but some caller may
+    not be able to handle all the complexity but may want to see if the
+    exporter will let them take a simpler view to its memory.
+
+    Some exporters may not be able to share memory in every possible way and
+    may need to raise errors to signal to some consumers that something is
+    just not possible. These errors should be a BufferError unless
+    there is another error that is actually causing the problem. The
+    exporter can use flags information to simplify how much of the
+    Py_buffer structure is filled in with non-default values and/or
+    raise an error if the object can't support a simpler view of its memory.
+
+    0 is returned on success and -1 on error."""
+    raise oefmt(space.w_TypeError,
+                "PyPy does not yet implement the new buffer interface")
 
 @cpython_api([lltype.Ptr(Py_buffer), lltype.Char], rffi.INT_real, 
error=CANNOT_FAIL)
 def PyBuffer_IsContiguous(space, view, fortran):
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
@@ -16,7 +16,7 @@
 @cpython_api([PyObject], PyObject)
 def PyMemoryView_GET_BASE(space, w_obj):
     # return the obj field of the Py_buffer created by PyMemoryView_GET_BUFFER
-    raise NotImplementedError
+    raise NotImplementedError('PyMemoryView_GET_BUFFER')
 
 @cpython_api([PyObject], lltype.Ptr(Py_buffer), error=CANNOT_FAIL)
 def PyMemoryView_GET_BUFFER(space, w_obj):
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
@@ -7,11 +7,11 @@
     cpython_api, generic_cpy_call, PyObject, Py_ssize_t, Py_TPFLAGS_CHECKTYPES,
     Py_buffer, mangle_name, pypy_decl)
 from pypy.module.cpyext.typeobjectdefs import (
-    unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc,
+    unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc, 
ternaryfunc,
     getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry,
     ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc,
     cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc,
-    getbufferproc, ssizessizeobjargproc)
+    getbufferproc, readbufferproc, ssizessizeobjargproc)
 from pypy.module.cpyext.pyobject import from_ref, make_ref, Py_DecRef
 from pypy.module.cpyext.pyerrors import PyErr_Occurred
 from pypy.module.cpyext.state import State
@@ -298,11 +298,23 @@
     # Similar to Py_buffer
     _immutable_ = True
 
-    def __init__(self, ptr, size, w_obj):
+    def __init__(self, ptr, size, w_obj, format='B', shape=None,
+                strides=None, ndim=1, itemsize=1, readonly=True):
         self.ptr = ptr
         self.size = size
         self.w_obj = w_obj # kept alive
-        self.readonly = True
+        self.format = format
+        if not shape:
+            self.shape = [size]
+        else:
+            self.shape = shape
+        if not strides:
+            self.strides = [1]
+        else:
+            self.strides = strides
+        self.ndim = ndim 
+        self.itemsize = itemsize
+        self.readonly = readonly
 
     def getlength(self):
         return self.size
@@ -313,14 +325,38 @@
     def get_raw_address(self):
         return rffi.cast(rffi.CCHARP, self.ptr)
 
+    def getformat(self):
+        return self.format
+
+    def getshape(self):
+        return self.shape
+
+    def getitemsize(self):
+        return self.itemsize
+
 def wrap_getbuffer(space, w_self, w_args, func):
     func_target = rffi.cast(getbufferproc, func)
-    with lltype.scoped_alloc(Py_buffer) as view:
-        flags = rffi.cast(rffi.INT_real, 0)
-        ret = generic_cpy_call(space, func_target, w_self, view, flags)
-        if rffi.cast(lltype.Signed, ret) == -1:
+    with lltype.scoped_alloc(Py_buffer) as pybuf:
+        _flags = 0
+        if space.len_w(w_args) > 0:
+            _flags = space.int_w(space.listview(w_args)[0])
+        flags = rffi.cast(rffi.INT_real,_flags)
+        size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
+        if widen(size) < 0:
             space.fromcache(State).check_and_raise_exception(always=True)
-        return space.newbuffer(CPyBuffer(view.c_buf, view.c_len, w_self))
+        ptr = pybuf.c_buf
+        size = pybuf.c_len
+        ndim = widen(pybuf.c_ndim)
+        shape =   [pybuf.c_shape[i]   for i in range(ndim)]
+        strides = [pybuf.c_strides[i] for i in range(ndim)]
+        if pybuf.c_format:
+            format = rffi.charp2str(pybuf.c_format)
+        else:
+            format = 'B'
+        return space.newbuffer(CPyBuffer(ptr, size, w_self, format=format,
+                            ndim=ndim, shape=shape, strides=strides,
+                            itemsize=pybuf.c_itemsize,
+                            readonly=widen(pybuf.c_readonly)))
 
 def get_richcmp_func(OP_CONST):
     def inner(space, w_self, w_args, func):
@@ -542,6 +578,21 @@
                              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':
+        buff_fn = w_type.getdictvalue(space, '__buffer__')
+        if buff_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, pybuf, flags):
+            # XXX this is wrong, needs a test
+            raise oefmt(space.w_NotImplemented, 
+                "calling bf_getbuffer on a builtin type not supported yet")
+            #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
@@ -673,23 +724,23 @@
                "x.__delitem__(y) <==> del x[y]"),
 
         BINSLOT("__add__", nb_add, slot_nb_add,
-            "+"),
+                "+"),
         RBINSLOT("__radd__", nb_add, slot_nb_add,
                  "+"),
         BINSLOT("__sub__", nb_subtract, slot_nb_subtract,
-            "-"),
+                "-"),
         RBINSLOT("__rsub__", nb_subtract, slot_nb_subtract,
                  "-"),
         BINSLOT("__mul__", nb_multiply, slot_nb_multiply,
-            "*"),
+                "*"),
         RBINSLOT("__rmul__", nb_multiply, slot_nb_multiply,
                  "*"),
         BINSLOT("__mod__", nb_remainder, slot_nb_remainder,
-            "%"),
+                "%"),
         RBINSLOT("__rmod__", nb_remainder, slot_nb_remainder,
                  "%"),
         BINSLOTNOTINFIX("__divmod__", nb_divmod, slot_nb_divmod,
-            "divmod(x, y)"),
+                        "divmod(x, y)"),
         RBINSLOTNOTINFIX("__rdivmod__", nb_divmod, slot_nb_divmod,
                  "divmod(y, x)"),
         NBSLOT("__pow__", nb_power, slot_nb_power, wrap_ternaryfunc,
@@ -819,11 +870,19 @@
 slotdefs = eval(slotdefs_str)
 # PyPy addition
 slotdefs += (
+    # XXX that might not be what we want!
     TPSLOT("__buffer__", "tp_as_buffer.c_bf_getbuffer", None, 
"wrap_getbuffer", ""),
 )
 
+if not PY3:
+    slotdefs += (
+        TPSLOT("__buffer__", "tp_as_buffer.c_bf_getreadbuffer", None, 
"wrap_getreadbuffer", ""),
+    )
+
+
 # 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'):
@@ -832,6 +891,10 @@
         return 2
     if slotdef.slot_name.startswith('tp_as_sequence'):
         return 3
+    if slotdef.slot_name == 'tp_as_buffer.c_bf_getbuffer':
+        return 100
+    if slotdef.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/buffer_test.c 
b/pypy/module/cpyext/test/buffer_test.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/buffer_test.c
@@ -0,0 +1,243 @@
+#include <Python.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* 
+ * Adapted from 
https://jakevdp.github.io/blog/2014/05/05/introduction-to-the-python-buffer-protocol,
+ * which is copyright Jake Vanderplas and released under the BSD license
+ */
+
+/* Structure defines a 1-dimensional strided array */
+typedef struct{
+    int* arr;
+    long length;
+} MyArray;
+
+/* initialize the array with integers 0...length */
+void initialize_MyArray(MyArray* a, long length){
+    int i;
+    a->length = length;
+    a->arr = (int*)malloc(length * sizeof(int));
+    for(i=0; i<length; i++){
+        a->arr[i] = i;
+    }
+}
+
+/* free the memory when finished */
+void deallocate_MyArray(MyArray* a){
+    free(a->arr);
+    a->arr = NULL;
+}
+
+/* tools to print the array */
+char* stringify(MyArray* a, int nmax){
+    char* output = (char*) malloc(nmax * 20);
+    int k, pos = sprintf(&output[0], "[");
+
+    for (k=0; k < a->length && k < nmax; k++){
+        pos += sprintf(&output[pos], " %d", a->arr[k]);
+    }
+    if(a->length > nmax)
+        pos += sprintf(&output[pos], "...");
+    sprintf(&output[pos], " ]");
+    return output;
+}
+
+void print_MyArray(MyArray* a, int nmax){
+    char* s = stringify(a, nmax);
+    printf("%s", s);
+    free(s);
+}
+
+/* This is where we define the PyMyArray object structure */
+typedef struct {
+    PyObject_HEAD
+    /* Type-specific fields go below. */
+    MyArray arr;
+} PyMyArray;
+
+
+/* This is the __init__ function, implemented in C */
+static int
+PyMyArray_init(PyMyArray *self, PyObject *args, PyObject *kwds)
+{
+    // init may have already been called
+    if (self->arr.arr != NULL) {
+        deallocate_MyArray(&self->arr);
+    }
+
+    int length = 0;
+    static char *kwlist[] = {"length", NULL};
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &length))
+        return -1;
+
+    if (length < 0)
+        length = 0;
+
+    initialize_MyArray(&self->arr, length);
+
+    return 0;
+}
+
+
+/* this function is called when the object is deallocated */
+static void
+PyMyArray_dealloc(PyMyArray* self)
+{
+    deallocate_MyArray(&self->arr);
+    Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+
+/* This function returns the string representation of our object */
+static PyObject *
+PyMyArray_str(PyMyArray * self)
+{
+  char* s = stringify(&self->arr, 10);
+  PyObject* ret = PyUnicode_FromString(s);
+  free(s);
+  return ret;
+}
+
+/* Here is the buffer interface function */
+static int
+PyMyArray_getbuffer(PyObject *obj, Py_buffer *view, int flags)
+{
+  if (view == NULL) {
+    PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer");
+    return -1;
+  }
+  if (flags == 0) {
+    PyErr_SetString(PyExc_ValueError, "flags == 0 in getbuffer");
+    return -1;
+  }
+
+  PyMyArray* self = (PyMyArray*)obj;
+  view->obj = (PyObject*)self;
+  view->buf = (void*)self->arr.arr;
+  view->len = self->arr.length * sizeof(int);
+  view->readonly = 0;
+  view->itemsize = sizeof(int);
+  view->format = "i";  // integer
+  view->ndim = 1;
+  view->shape = &self->arr.length;  // length-1 sequence of dimensions
+  view->strides = &view->itemsize;  // for the simple case we can do this
+  view->suboffsets = NULL;
+  view->internal = NULL;
+
+  Py_INCREF(self);  // need to increase the reference count
+  return 0;
+}
+
+static PyBufferProcs PyMyArray_as_buffer = {
+#if PY_MAJOR_VERSION < 3
+  (readbufferproc)0,
+  (writebufferproc)0,
+  (segcountproc)0,
+  (charbufferproc)0,
+#endif
+  (getbufferproc)PyMyArray_getbuffer,
+  (releasebufferproc)0,  // we do not require any special release function
+};
+
+
+/* Here is the type structure: we put the above functions in the appropriate 
place
+   in order to actually define the Python object type */
+static PyTypeObject PyMyArrayType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pymyarray.PyMyArray",        /* tp_name */
+    sizeof(PyMyArray),            /* tp_basicsize */
+    0,                            /* tp_itemsize */
+    (destructor)PyMyArray_dealloc,/* tp_dealloc */
+    0,                            /* tp_print */
+    0,                            /* tp_getattr */
+    0,                            /* tp_setattr */
+    0,                            /* tp_reserved */
+    (reprfunc)PyMyArray_str,      /* tp_repr */
+    0,                            /* tp_as_number */
+    0,                            /* tp_as_sequence */
+    0,                            /* tp_as_mapping */
+    0,                            /* tp_hash  */
+    0,                            /* tp_call */
+    (reprfunc)PyMyArray_str,      /* tp_str */
+    0,                            /* tp_getattro */
+    0,                            /* tp_setattro */
+    &PyMyArray_as_buffer,         /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */
+    "PyMyArray object",           /* tp_doc */
+    0,                            /* tp_traverse */
+    0,                            /* tp_clear */
+    0,                            /* tp_richcompare */
+    0,                            /* tp_weaklistoffset */
+    0,                            /* tp_iter */
+    0,                            /* tp_iternext */
+    0,                            /* tp_methods */
+    0,                            /* tp_members */
+    0,                            /* tp_getset */
+    0,                            /* tp_base */
+    0,                            /* tp_dict */
+    0,                            /* tp_descr_get */
+    0,                            /* tp_descr_set */
+    0,                            /* tp_dictoffset */
+    (initproc)PyMyArray_init,     /* tp_init */
+};
+
+static PyMethodDef buffer_functions[] = {
+    {NULL,        NULL}    /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    "buffer_test",
+    "Module Doc",
+    -1,
+    buffer_functions;
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
+#define INITERROR return NULL
+
+/* Initialize this module. */
+#ifdef __GNUC__
+extern __attribute__((visibility("default")))
+#else
+extern __declspec(dllexport)
+#endif
+
+PyMODINIT_FUNC
+PyInit_buffer_test(void)
+
+#else
+
+#define INITERROR return
+
+/* Initialize this module. */
+#ifdef __GNUC__
+extern __attribute__((visibility("default")))
+#else
+extern __declspec(dllexport)
+#endif
+
+PyMODINIT_FUNC
+initbuffer_test(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+    PyObject *m= PyModule_Create(&moduledef);
+#else
+    PyObject *m= Py_InitModule("buffer_test", buffer_functions);
+#endif
+    if (m == NULL)
+        INITERROR;
+    PyMyArrayType.tp_new = PyType_GenericNew;
+    if (PyType_Ready(&PyMyArrayType) < 0)
+        INITERROR;
+    Py_INCREF(&PyMyArrayType);
+    PyModule_AddObject(m, "PyMyArray", (PyObject *)&PyMyArrayType);
+#if PY_MAJOR_VERSION >=3
+    return m;
+#endif
+}
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
@@ -9,7 +9,10 @@
             py.test.skip("unsupported before Python 2.7")
 
         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 space.eq_w(w_char, space.wrap('h'))
         w_bytes = space.call_method(w_view, "tobytes")
         assert space.unwrap(w_bytes) == "hello"
 
@@ -55,3 +58,15 @@
     @pytest.mark.skipif(True, reason='write a test for this')
     def test_get_base_and_get_buffer(self, space, api):
         assert False # XXX test PyMemoryView_GET_BASE, PyMemoryView_GET_BUFFER
+
+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'
+        assert y.shape == (10,)
+        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, Py_buffer, 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)
@@ -514,6 +515,7 @@
         bytes_getbuffer.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):
@@ -681,6 +683,8 @@
             pto.c_tp_setattro = base.c_tp_setattro
         if not pto.c_tp_getattro:
             pto.c_tp_getattro = base.c_tp_getattro
+        if not pto.c_tp_as_buffer:
+            pto.c_tp_as_buffer = base.c_tp_as_buffer
     finally:
         Py_DecRef(space, base_pyo)
 
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
@@ -1,10 +1,11 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rtyper.lltypesystem.lltype import Ptr, FuncType, Void
 from pypy.module.cpyext.api import (cpython_struct, Py_ssize_t, Py_ssize_tP,
-    PyVarObjectFields, PyTypeObject, PyTypeObjectPtr, FILEP, Py_buffer,
+    PyVarObjectFields, PyTypeObject, PyTypeObjectPtr, FILEP,
     Py_TPFLAGS_READYING, Py_TPFLAGS_READY, Py_TPFLAGS_HEAPTYPE)
 from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
 from pypy.module.cpyext.modsupport import PyMethodDef
+from pypy.module.cpyext.api import Py_bufferP
 
 
 P, FT, PyO = Ptr, FuncType, PyObject
@@ -53,7 +54,11 @@
 wrapperfunc = P(FT([PyO, PyO, rffi.VOIDP], PyO))
 wrapperfunc_kwds = P(FT([PyO, PyO, rffi.VOIDP, PyO], PyO))
 
-getbufferproc = P(FT([PyO, Ptr(Py_buffer), rffi.INT_real], rffi.INT_real))
+readbufferproc = P(FT([PyO, Py_ssize_t, rffi.VOIDPP], Py_ssize_t))
+writebufferproc = P(FT([PyO, Py_ssize_t, rffi.VOIDPP], Py_ssize_t))
+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 = P(FT([PyO, Ptr(Py_buffer)], Void))
 
 
@@ -126,6 +131,10 @@
 ))
 
 PyBufferProcs = cpython_struct("PyBufferProcs", (
+    ("bf_getreadbuffer", readbufferproc),
+    ("bf_getwritebuffer", writebufferproc),
+    ("bf_getsegcount", segcountproc),
+    ("bf_getcharbuffer", charbufferproc),
     ("bf_getbuffer", getbufferproc),
     ("bf_releasebuffer", releasebufferproc),
 ))
diff --git a/pypy/module/micronumpy/compile.py 
b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -19,7 +19,6 @@
     UserDelAction)
 from pypy.interpreter.pyframe import PyFrame
 
-
 class BogusBytecode(Exception):
     pass
 
@@ -383,6 +382,9 @@
         # XXX even the hacks have hacks
         if s == 'size': # used in _array() but never called by tests
             return IntObject(0)
+        if s == '__buffer__':
+            # descr___buffer__ does not exist on W_Root
+            return self.w_None
         return getattr(w_obj, 'descr_' + s)(self, *args)
 
     @specialize.arg(1)
diff --git a/pypy/module/micronumpy/concrete.py 
b/pypy/module/micronumpy/concrete.py
--- a/pypy/module/micronumpy/concrete.py
+++ b/pypy/module/micronumpy/concrete.py
@@ -704,3 +704,20 @@
     def get_raw_address(self):
         from rpython.rtyper.lltypesystem import rffi
         return rffi.ptradd(self.impl.storage, self.impl.start)
+
+    def getformat(self):
+        return self.impl.dtype.char
+
+    def getitemsize(self):
+        return self.impl.dtype.elsize
+
+    def getndim(self):
+        return len(self.impl.shape)
+
+    def getshape(self):
+        return self.impl.shape
+
+    def getstrides(self):
+        return self.impl.strides
+
+
diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py
--- a/pypy/module/micronumpy/ctors.py
+++ b/pypy/module/micronumpy/ctors.py
@@ -468,7 +468,8 @@
     except OperationError as e:
         if not e.match(space, space.w_TypeError):
             raise
-        w_buffer = space.getattr(w_buffer, space.wrap('__buffer__'))
+        w_buffer = space.call_method(w_buffer, '__buffer__', 
+                                    space.newint(space.BUF_FULL_RO))
         buf = _getbuffer(space, w_buffer)
 
     ts = buf.getlength()
diff --git a/pypy/module/micronumpy/test/test_ndarray.py 
b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -3617,13 +3617,35 @@
         assert str(exc.value) == "assignment destination is read-only"
 
         class A(object):
-            __buffer__ = 'abc'
+            def __buffer__(self, flags):
+                return 'abc'
 
         data = A()
         a = np.frombuffer(data, 'c')
         #assert a.base is data.__buffer__
         assert a.tostring() == 'abc'
 
+    def test_memoryview(self):
+        import numpy as np
+        import sys
+        if sys.version_info[:2] > (3, 2):
+            # In Python 3.3 the representation of empty shape, strides and 
sub-offsets
+            # is an empty tuple instead of None.
+            # http://docs.python.org/dev/whatsnew/3.3.html#api-changes
+            EMPTY = ()
+        else:
+            EMPTY = None
+        x = np.array([1, 2, 3, 4, 5], dtype='i')
+        y = memoryview('abc')
+        assert y.format == 'B'
+        y = memoryview(x)
+        assert y.format == 'i'
+        assert y.shape == (5,)
+        assert y.ndim == 1
+        assert y.strides == (4,)
+        assert y.suboffsets == EMPTY
+        assert y.itemsize == 4
+
     def test_fromstring(self):
         import sys
         from numpy import fromstring, dtype
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
@@ -12,7 +12,13 @@
 from pypy.interpreter.gateway import (
     WrappedDefault, interp2app, interpindirect2app, unwrap_spec)
 from pypy.interpreter.typedef import TypeDef
+from pypy.objspace.std import newformat
+from pypy.objspace.std.basestringtype import basestring_typedef
+from pypy.objspace.std.formatting import mod_format
 from pypy.objspace.std.stringmethods import StringMethods
+from pypy.objspace.std.unicodeobject import (
+    decode_object, unicode_from_encoded_object,
+    unicode_from_string, getdefaultencoding)
 from pypy.objspace.std.util import IDTAG_SPECIAL, IDTAG_SHIFT
 
 
@@ -394,7 +400,6 @@
         of the specified width. The string S is never truncated.
         """
 
-
 class W_BytesObject(W_AbstractBytesObject):
     import_from_mixin(StringMethods)
     _immutable_fields_ = ['_value']
@@ -417,6 +422,18 @@
         space.check_buf_flags(flags, True)
         return StringBuffer(self._value)
 
+    def readbuf_w(self, space):
+        return StringBuffer(self._value)
+
+    def writebuf_w(self, space):
+        raise oefmt(space.w_TypeError,
+                    "Cannot use string as modifiable buffer")
+
+    def descr_getbuffer(self, space, w_flags):
+        #from pypy.objspace.std.bufferobject import W_Buffer
+        #return W_Buffer(StringBuffer(self._value))
+        return self
+
     def listview_int(self):
         return _create_list_from_bytes(self._value)
 
diff --git a/pypy/objspace/std/memoryobject.py 
b/pypy/objspace/std/memoryobject.py
--- a/pypy/objspace/std/memoryobject.py
+++ b/pypy/objspace/std/memoryobject.py
@@ -82,18 +82,29 @@
     def descr_getitem(self, space, w_index):
         self._check_released(space)
         start, stop, step, size = space.decode_index4(w_index, 
self.getlength())
+        itemsize = self.buf.getitemsize()
+        if itemsize > 1:
+            start *= itemsize
+            size *= itemsize
+            stop  = start + size
+            if step == 0:
+                step = 1
+            if stop > self.getlength():
+                raise oefmt(space.w_IndexError, 'index out of range')
+        if step not in (0, 1):
+            raise oefmt(space.w_NotImplementedError, "")
         if step == 0:  # index only
             # TODO: this probably isn't very fast
-            buf = SubBuffer(self.buf, start * self.itemsize, self.itemsize)
+            buf = SubBuffer(self.buf, start, self.itemsize)
             fmtiter = UnpackFormatIterator(space, buf)
             fmtiter.interpret(self.format)
             return fmtiter.result_w[0]
         elif step == 1:
-            buf = SubBuffer(self.buf, start * self.itemsize,
-                            size * self.itemsize)
+            buf = SubBuffer(self.buf, start, size)
             return W_MemoryView(buf, self.format, self.itemsize)
         else:
-            raise oefmt(space.w_NotImplementedError, "")
+            buf = SubBuffer(self.buf, start, size)
+            return W_MemoryView(buf)
 
     def descr_setitem(self, space, w_index, w_obj):
         self._check_released(space)
@@ -102,6 +113,21 @@
         if space.isinstance_w(w_index, space.w_tuple):
             raise oefmt(space.w_NotImplementedError, "")
         start, stop, step, size = space.decode_index4(w_index, 
self.getlength())
+        itemsize = self.buf.getitemsize()
+        if itemsize > 1:
+            start *= itemsize
+            size *= itemsize
+            stop  = start + size
+            if step == 0:
+                step = 1
+            if stop > self.getlength():
+                raise oefmt(space.w_IndexError, 'index out of range')
+        if step not in (0, 1):
+            raise oefmt(space.w_NotImplementedError, "")
+        value = space.buffer_w(w_obj, space.BUF_CONTIG_RO)
+        if value.getlength() != size:
+            raise oefmt(space.w_ValueError,
+                        "cannot modify size of memoryview object")
         if step == 0:  # index only
             # TODO: this probably isn't very fast
             fmtiter = PackFormatIterator(space, [w_obj], self.itemsize)
@@ -111,43 +137,43 @@
                 raise oefmt(space.w_TypeError,
                             "memoryview: invalid type for format '%s'",
                             self.format)
-            self.buf.setslice(start * self.itemsize, fmtiter.result.build())
+            self.buf.setslice(start, fmtiter.result.build())
         elif step == 1:
             value = space.buffer_w(w_obj, space.BUF_CONTIG_RO)
             if value.getlength() != size * self.itemsize:
                 raise oefmt(space.w_ValueError,
                             "cannot modify size of memoryview object")
-            self.buf.setslice(start * self.itemsize, value.as_str())
+            self.buf.setslice(start, value.as_str())
         else:
             raise oefmt(space.w_NotImplementedError, "")
 
     def descr_len(self, space):
         self._check_released(space)
-        return space.wrap(self.getlength())
+        return space.wrap(self.buf.getlength())
 
     def w_get_format(self, space):
         self._check_released(space)
-        return space.wrap(self.format)
+        return space.wrap(self.buf.getformat())
 
     def w_get_itemsize(self, space):
         self._check_released(space)
-        return space.wrap(self.itemsize)
+        return space.wrap(self.buf.getitemsize())
 
     def w_get_ndim(self, space):
         self._check_released(space)
-        return space.wrap(1)
+        return space.wrap(self.buf.getndim())
 
     def w_is_readonly(self, space):
         self._check_released(space)
-        return space.wrap(self.buf.readonly)
+        return space.newbool(bool(self.buf.readonly))
 
     def w_get_shape(self, space):
         self._check_released(space)
-        return space.newtuple([space.wrap(self.getlength())])
+        return space.newtuple([space.wrap(x) for x in self.buf.getshape()])
 
     def w_get_strides(self, space):
         self._check_released(space)
-        return space.newtuple([space.wrap(self.itemsize)])
+        return space.newtuple([space.wrap(x) for x in self.buf.getstrides()])
 
     def w_get_suboffsets(self, space):
         self._check_released(space)
@@ -196,7 +222,6 @@
             raise OperationError(space.w_ValueError, space.wrap(msg))
         return space.wrap(rffi.cast(lltype.Signed, ptr))
 
-
 W_MemoryView.typedef = TypeDef(
     "memoryview",
     __doc__ = """\
diff --git a/pypy/tool/pytest/objspace.py b/pypy/tool/pytest/objspace.py
--- a/pypy/tool/pytest/objspace.py
+++ b/pypy/tool/pytest/objspace.py
@@ -122,6 +122,9 @@
     def newlist(self, iterable):
         return list(iterable)
 
+    def newbytes(self, obj):
+        return bytes(obj)
+
     def call_function(self, func, *args, **kwds):
         return func(*args, **kwds)
 
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -59,6 +59,20 @@
     def get_raw_address(self):
         raise ValueError("no raw buffer")
 
+    def getformat(self):
+        return 'B'
+
+    def getitemsize(self):
+        return 1
+
+    def getndim(self):
+        return 1
+
+    def getshape(self):
+        return [self.getlength()]
+
+    def getstrides(self):
+        return [1]
 
 class StringBuffer(Buffer):
     __slots__ = ['value']
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to