Commits

Lenard Lindstrom committed a68a90c

Add PyBUF_* flag to the PgObject_GetBuffer C api function for dict buffers

The PgObject_GetBuffer Pygame C api function, in pygame.base, now properly
handles the buffer request flags passed to it for an exporting object with a
Python level array interface. The changes are made in PgDict_AsBuffer, another
C api function. These changes effect all Pygame types and functions that import
a buffer, since they all use these api functions.

Comments (0)

Files changed (2)

 #define PAI_OTHER_ENDIAN '<'
 #endif
 
+#if PY3
+#define INT_CHECK(o) PyLong_Check(o)
+#else
+#define INT_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
+#endif
+
+#define PyBUF_HAS_FLAG(f, F) (((f) & (F)) == (F))
+
 /* Extended array struct */
 typedef struct capsule_interface_s {
     PyArrayInterface inter;
     Py_intptr_t imem[1];
 } CapsuleInterface;
 
-/* Py_buffer internal data for an array struct */
+/* Py_buffer internal data for an array interface/struct */
 typedef struct view_internals_s {
     PyObject* cobj;
     char format[4];      /* make 4 byte word sized */
 static int PgObject_GetBuffer (PyObject*, Pg_buffer*, int);
 static int GetArrayInterface (PyObject**, PyObject*);
 static int PgDict_AsBuffer (Pg_buffer*, PyObject*, int);
-static int _shape_arg_convert (PyObject *, Py_buffer*);
-static int _typestr_arg_convert (PyObject *, Py_buffer*);
-static int _data_arg_convert (PyObject*, Py_buffer*);
-static int _strides_arg_convert (PyObject*, Py_buffer*);
+static int _pyshape_check (PyObject*);
+static int _pytypestr_check (PyObject*);
+static int _pystrides_check (PyObject*);
+static int _pydata_check (PyObject*);
+static int _is_inttuple (PyObject*);
+static int _pyvalues_as_buffer(Py_buffer*,
+                               int,
+                               PyObject*,
+                               PyObject*,
+                               PyObject*,
+                               PyObject*,
+                               PyObject*);
+static int _pyinttuple_as_ssize_arr (PyObject*, Py_ssize_t*);
+static int _pytypestr_as_format (PyObject*, char*, Py_ssize_t*);
 static PyObject* view_get_typestr_obj (Py_buffer*);
 static PyObject* view_get_shape_obj (Py_buffer*);
 static PyObject* view_get_strides_obj (Py_buffer*);
     }
     if (!PyDict_Check (inter)) {
         PyErr_Format (PyExc_ValueError,
-                      "expected __array_interface__ to return a dict: got a %s",
+                      "expected '__array_interface__' to return a dict: got %s",
                       Py_TYPE (dict)->tp_name);
         Py_DECREF (inter);
         return -1;
 static int
 PgDict_AsBuffer (Pg_buffer* pg_view_p, PyObject* dict, int flags)
 {
-    Py_buffer* view_p = (Py_buffer*)pg_view_p;
     PyObject* pyshape = PyDict_GetItemString (dict, "shape");
     PyObject* pytypestr = PyDict_GetItemString (dict, "typestr");
     PyObject* pydata = PyDict_GetItemString (dict, "data");
     PyObject* pystrides = PyDict_GetItemString (dict, "strides");
-    int i;
 
-    if (!pyshape) {
-        PyErr_SetString (PyExc_ValueError,
-                         "required \"shape\" item is missing");
+    if (_pyshape_check (pyshape)) {
         return -1;
     }
-    if (!pytypestr) {
-        PyErr_SetString (PyExc_ValueError,
-                         "required \"typestr\" item is missing");
+    if (_pytypestr_check (pytypestr)) {
         return -1;
     }
-    if (!pydata) {
-        PyErr_SetString (PyExc_ValueError,
-                         "required \"data\" item is missing");
+    if (_pydata_check (pydata)) {
         return -1;
     }
-    /* The item processing order is important:
-       "strides" and "typestr" must follow "shape". */
-    view_p->internal = 0;
-    view_p->obj = 0;
+    if (_pystrides_check (pystrides)) {
+        return -1;
+    }
     pg_view_p->release_buffer = _release_buffer_array;
-    if (_shape_arg_convert (pyshape, view_p)) {
+    if (_pyvalues_as_buffer ((Py_buffer*)pg_view_p, flags, dict,
+                             pytypestr, pyshape, pydata, pystrides)) {
         PgBuffer_Release (pg_view_p);
         return -1;
     }
-    if (_typestr_arg_convert (pytypestr, view_p)) {
-        PgBuffer_Release (pg_view_p);
-        return -1;
-    }
-    if (_data_arg_convert (pydata, view_p)) {
-        PgBuffer_Release (pg_view_p);
-        return -1;
-    }
-    if (_strides_arg_convert (pystrides, view_p)) {
-        PgBuffer_Release (pg_view_p);
-        return -1;
-    }
-    view_p->len = view_p->itemsize;
-    for (i = 0; i < view_p->ndim; ++i) {
-        view_p->len *= view_p->shape[i];
-    }
     return 0;
 }
 
 static int
-_shape_arg_convert (PyObject* o, Py_buffer* view)
+_pyshape_check(PyObject* op)
 {
-    /* Convert o as a C array of integers and return 1, otherwise
-     * raise a Python exception and return 0.
-     */
-    ViewInternals* internal_p;
-    Py_ssize_t i, n;
-    Py_ssize_t* a;
-    size_t sz;
-
-    view->obj = 0;
-    view->internal = 0;
-    if (!PyTuple_Check (o)) {
-        PyErr_Format (PyExc_TypeError,
-                      "Expected a tuple for shape: found %s",
-                      Py_TYPE (o)->tp_name);
+    if (!op) {
+        PyErr_SetString (PyExc_ValueError,
+                         "required 'shape' item is missing");
         return -1;
     }
-    n = PyTuple_GET_SIZE (o);
-    sz = sizeof (ViewInternals) + (2 * n - 1) * sizeof (Py_ssize_t);
-    internal_p = (ViewInternals*)PyMem_Malloc (sz);
-    if (!internal_p) {
-        PyErr_NoMemory ();
+    if (!_is_inttuple (op)) {
+        PyErr_SetString (PyExc_ValueError,
+                         "expected a tuple of ints for 'shape'");
         return -1;
     }
-    internal_p->cobj = 0;
-    a = internal_p->imem;
-    for (i = 0; i < n; ++i) {
-        a[i] = PyInt_AsSsize_t (PyTuple_GET_ITEM (o, i));
-        if (a[i] == -1 && PyErr_Occurred () /* conditional && */) {
-            PyMem_Free (internal_p);
-            PyErr_Format (PyExc_TypeError,
-                          "shape tuple has a non-integer at position %d",
-                          (int)i);
-            return -1;
-        }
+    if (PyTuple_GET_SIZE (op) == 0) {
+        PyErr_SetString (PyExc_ValueError,
+                         "expected 'shape' to be at least one-dimensional");
+        return -1;
     }
-    view->ndim = n;
-    view->internal = internal_p;
-    view->shape = a;
-    view->strides = a + n;
-    view->format = internal_p->format;
     return 0;
 }
 
 static int
-_typestr_arg_convert (PyObject* o, Py_buffer* view)
+_pytypestr_check(PyObject *op)
 {
-    /* Due to incompatibilities between the array and new buffer interfaces,
-     * as well as significant unicode changes in Python 3.3, this will
-     * only handle integer types. Must call _shape_arg_convert first.
-     */
-    char *fchar_p;
-    int is_swapped;
-    int itemsize = 0;
-    PyObject* s;
-    const char* typestr;
-
-    if (PyUnicode_Check (o)) {
-        s = PyUnicode_AsASCIIString (o);
-        if (!s) {
+    if (!op) {
+        PyErr_SetString (PyExc_ValueError,
+                         "required 'typestr' item is missing");
+        return -1;
+    }
+    if (PyUnicode_Check (op)) {
+        if (PyUnicode_GET_SIZE (op) != 3) {
+            PyErr_SetString (PyExc_ValueError,
+                             "expected 'typestr' to be length 3");
+            return -1;
+        }
+    }
+    else if (Bytes_Check (op)) {
+        if (Bytes_GET_SIZE (op) != 3) {
+            PyErr_SetString (PyExc_ValueError,
+                             "expected 'typestr' to be length 3");
             return -1;
         }
     }
     else {
-        Py_INCREF (o);
-        s = o;
-    }
-    if (!Bytes_Check (s)) {
-        PyErr_Format( PyExc_TypeError, "Expected a string for typestr: got %s",
-                     Py_TYPE (s)->tp_name);
-        Py_DECREF (s);
+        PyErr_SetString (PyExc_ValueError,
+                         "expected a string for 'typestr'");
         return -1;
     }
-    if (Bytes_GET_SIZE (s) != 3) {
-        PyErr_SetString (PyExc_TypeError, "Expected typestr to be length 3");
-        Py_DECREF (s);
+    return 0;
+}
+
+static int
+_pydata_check(PyObject *op)
+{
+    PyObject* item;
+
+    if (!op) {
+        PyErr_SetString (PyExc_ValueError,
+                         "required 'data' item is missing");
         return -1;
     }
-    typestr = Bytes_AsString (s);
-    fchar_p = view->format;
+    if (!PyTuple_Check (op)) {
+        PyErr_SetString (PyExc_ValueError,
+                         "expected a tuple for 'data'");
+        return -1;
+    }
+    if (PyTuple_GET_SIZE (op) != 2) {
+        PyErr_SetString (PyExc_ValueError,
+                         "expected a length 2 tuple for 'data'");
+        return -1;
+    }
+    item = PyTuple_GET_ITEM (op, 0);
+    if (!INT_CHECK (item)) {
+        PyErr_SetString (PyExc_ValueError,
+                         "expected an int for item 0 of 'data'");
+        return -1;
+    }
+    return 0;
+}
+
+static int
+_pystrides_check (PyObject *op)
+{
+    if (op && !_is_inttuple (op) /* Conditional && */) {
+        PyErr_SetString (PyExc_ValueError,
+                         "expected a tuple of ints for 'strides'");
+        return -1;
+    }
+    return 0;
+}
+
+static int
+_is_inttuple (PyObject* op)
+{
+    Py_ssize_t i;
+    Py_ssize_t n;
+    PyObject* ip;
+
+    if (!PyTuple_Check (op)) {
+        return 0;
+    }
+    n = PyTuple_GET_SIZE (op);
+    for (i = 0; i != n; ++i) {
+        ip = PyTuple_GET_ITEM (op, i);
+        if (!INT_CHECK (ip)) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static int
+_pyvalues_as_buffer(Py_buffer* view_p, int flags, PyObject* obj,
+                    PyObject* pytypestr,
+                    PyObject* pyshape,
+                    PyObject* pydata,
+                    PyObject* pystrides)
+{
+    Py_ssize_t ndim = PyTuple_GET_SIZE (pyshape);
+    ViewInternals* internal_p;
+    ssize_t sz;
+    int i;
+
+    assert (ndim > 0);
+    view_p->obj = 0;
+    view_p->internal = 0;
+    if (pystrides && PyTuple_GET_SIZE (pystrides) != ndim /* Cond. && */) {
+        PyErr_SetString (PyExc_ValueError,
+                         "'shape' and 'strides' are not the same length");
+        return -1;
+    }
+    view_p->ndim = ndim;
+    view_p->buf = PyLong_AsVoidPtr (PyTuple_GET_ITEM (pydata, 0));
+    if (!view_p->buf && PyErr_Occurred ()) {
+        return -1;
+    }
+    view_p->readonly = PyObject_IsTrue (PyTuple_GET_ITEM (pydata, 1));
+    if (view_p->readonly == -1) {
+        return -1;
+    }
+    if (PyBUF_HAS_FLAG (flags, PyBUF_WRITABLE) && view_p->readonly) {
+        PyErr_SetString (PgExc_BufferError,
+                         "require writable buffer, but it is read-only");
+        return -1;
+    }
+    sz = sizeof (ViewInternals) + (2 * ndim - 1) * sizeof (Py_ssize_t);
+    internal_p = (ViewInternals*)PyMem_Malloc (sz);
+    internal_p->cobj = 0;
+    view_p->internal = internal_p;
+    view_p->format = internal_p->format;
+    view_p->shape = internal_p->imem;
+    view_p->strides = internal_p->imem + ndim;
+    if (_pytypestr_as_format (pytypestr, view_p->format, &view_p->itemsize)) {
+        return -1;
+    }
+    if (_pyinttuple_as_ssize_arr (pyshape, view_p->shape)) {
+        return -1;
+    }
+    if (pystrides) {
+        if (_pyinttuple_as_ssize_arr (pystrides, view_p->strides)) {
+            return -1;
+        }
+    }
+    else if (PyBUF_HAS_FLAG (flags, PyBUF_STRIDES)) {
+        view_p->strides[ndim - 1] = view_p->itemsize;
+        for (i = ndim - 1; i != 0; --i) {
+            view_p->strides[i - 1] = view_p->shape[i] * view_p->strides[i];
+        }
+    }
+    else {
+        view_p->strides = 0;
+    }
+    view_p->suboffsets = 0;
+    view_p->len = view_p->itemsize;
+    for (i = 0; i < view_p->ndim; ++i) {
+        view_p->len *= view_p->shape[i];
+    }
+    if (PyBUF_HAS_FLAG (flags, PyBUF_ANY_CONTIGUOUS)) {
+        if (!PyBuffer_IsContiguous (view_p, 'A')) {
+            PyErr_SetString (PgExc_BufferError,
+                             "buffer data is not contiguous");
+            return -1;
+        }
+    }
+    else if (PyBUF_HAS_FLAG (flags, PyBUF_C_CONTIGUOUS)) {
+        if (!PyBuffer_IsContiguous (view_p, 'C')) {
+            PyErr_SetString (PgExc_BufferError,
+                             "buffer data is not C contiguous");
+            return -1;
+        }
+    }
+    else if (PyBUF_HAS_FLAG (flags, PyBUF_F_CONTIGUOUS)) {
+        if (!PyBuffer_IsContiguous (view_p, 'F')) {
+            PyErr_SetString (PgExc_BufferError,
+                             "buffer data is not F contiguous");
+            return -1;
+        }
+    }
+    if (!PyBUF_HAS_FLAG (flags, PyBUF_STRIDES)) {
+        if (PyBuffer_IsContiguous (view_p, 'C')) {
+            view_p->strides = 0;
+        }
+        else {
+            PyErr_SetString (PgExc_BufferError,
+                             "buffer data is not C contiguous, strides needed");
+            return -1;
+        }
+    }
+    if (!PyBUF_HAS_FLAG (flags, PyBUF_ND)) {
+        if (ndim == 1) {
+            view_p->shape = 0;
+        }
+        else {
+            PyErr_SetString (PgExc_BufferError,
+                             "buffer data is multi-dimensional, shape needed");
+            return -1;
+        }
+    }
+    if (!PyBUF_HAS_FLAG (flags, PyBUF_FORMAT)) {
+        view_p->format = 0;
+    }
+    if (flags == PyBUF_SIMPLE) {
+        view_p->ndim = 0;
+    }
+    Py_XINCREF (obj);
+    view_p->obj = obj;
+    return 0;
+}
+
+static int
+_pyinttuple_as_ssize_arr (PyObject *tp, Py_ssize_t* arr)
+{
+    Py_ssize_t i;
+    Py_ssize_t n = PyTuple_GET_SIZE (tp);
+
+    for (i = 0; i != n; ++i) {
+        arr[i] = PyInt_AsSsize_t (PyTuple_GET_ITEM (tp, i));
+        if (arr[i] == -1 && PyErr_Occurred ()) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int
+_pytypestr_as_format (PyObject* sp, char* format, Py_ssize_t* itemsize_p)
+{
+    const char* typestr;
+    char *fchar_p = format;
+    int is_swapped = 0;
+    Py_ssize_t itemsize = 0;
+
+    if (PyUnicode_Check (sp)) {
+        sp = PyUnicode_AsASCIIString (sp);
+        if (!sp) {
+            return -1;
+        }
+    }
+    else {
+        Py_INCREF (sp);
+    }
+    typestr = Bytes_AsString (sp);
     switch (typestr[0]) {
 
     case PAI_MY_ENDIAN:
-        is_swapped = 0;
+    case '|':
         break;
     case PAI_OTHER_ENDIAN:
         is_swapped = 1;
         break;
-    case '|':
-        is_swapped = 0;
-        break;
     default:
         PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
-        Py_DECREF (s);
+        Py_DECREF (sp);
         return -1;
     }
     switch (typestr[1]) {
         switch (typestr[2]) {
 
         case '1':
-            *fchar_p = '=';
-            ++fchar_p;
             *fchar_p = 'B';
             itemsize = 1;
             break;
             break;
         default:
             PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
-            Py_DECREF (s);
+            Py_DECREF (sp);
             return -1;
         }
         if (typestr[1] == 'i') {
             break;
         default:
             PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
-            Py_DECREF (s);
+            Py_DECREF (sp);
             return -1;
         }
         break;
             break;
         default:
             PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
-            Py_DECREF (s);
+            Py_DECREF (sp);
             return -1;
         }
         ++fchar_p;
         break;
     default:
         PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
-        Py_DECREF (s);
+        Py_DECREF (sp);
         return -1;
     }
+    Py_DECREF (sp);
     ++fchar_p;
     *fchar_p = '\0';
-    view->itemsize = itemsize;
+    *itemsize_p = itemsize;
     return 0;
 }
 
-static int
-_strides_arg_convert (PyObject* o, Py_buffer* view)
-{
-    /* Must called _shape_arg_convert first.
-     */
-    int n;
-    Py_ssize_t* a;
-    size_t i;
-
-    if (o == Py_None) {
-        /* no strides (optional) given */
-        view->strides = 0;
-        return 0;
-    }
-    a = view->strides;
-    
-    if (!PyTuple_Check (o)) {
-        PyErr_Format (PyExc_TypeError,
-                      "Expected a tuple for strides: found %s",
-                      Py_TYPE (o)->tp_name);
-        return -1;
-    }
-    n = PyTuple_GET_SIZE (o);
-    if (n != view->ndim) {
-        PyErr_SetString (PyExc_TypeError,
-                         "Missmatch in strides and shape length");
-        return -1;
-    }
-    for (i = 0; i < n; ++i) {
-        a[i] = PyInt_AsSsize_t (PyTuple_GET_ITEM (o, i));
-        if (a[i] == -1 && PyErr_Occurred () /* conditional && */) {
-            PyErr_Format (PyExc_TypeError,
-                          "strides tuple has a non-integer at position %d",
-                          (int)i);
-            return -1;
-        }
-    }
-    if (n != view->ndim) {
-        PyErr_SetString (PyExc_TypeError,
-                         "strides and shape tuple lengths differ");
-        return -1;
-    }
-    return 0;
-}
-
-static int
-_data_arg_convert(PyObject *o, Py_buffer *view)
-{
-    void* address;
-    int readonly;
-
-    if (!PyTuple_Check (o)) {
-        PyErr_Format (PyExc_TypeError, "expected a tuple for data: got %s",
-                      Py_TYPE (o)->tp_name);
-        return -1;
-    }
-    if (PyTuple_GET_SIZE (o) != 2) {
-        PyErr_SetString (PyExc_TypeError, "expected a length 2 tuple for data");
-        return -1;
-    }
-    address = PyLong_AsVoidPtr (PyTuple_GET_ITEM (o, 0));
-    if (!address && PyErr_Occurred ()) {
-        PyErr_Clear ();
-        PyErr_Format (PyExc_TypeError,
-                      "expected an integer address for data item 0: got %s",
-                      Py_TYPE (PyTuple_GET_ITEM (o, 0))->tp_name);
-        return -1;
-    }
-    readonly = PyObject_IsTrue (PyTuple_GET_ITEM (o, 1));
-    if (readonly == -1) {
-        PyErr_Clear ();
-        PyErr_Format (PyExc_TypeError,
-                      "expected a boolean flag for data item 1: got %s",
-                      Py_TYPE (PyTuple_GET_ITEM (o, 0))->tp_name);
-        return -1;
-    }
-    view->buf = address;
-    view->readonly = readonly;
-    return 0;
-}
 
 /*error signal handlers (replacing SDL parachute)*/
 static void

test/base_test.py

             self.assertSame(v, o)
 
     if (pygame.HAVE_NEWBUF):
-        def test_GetView_newbuf(self):
-            self.NEWBUF_test_GetView_newbuf()
+        def test_newbuf(self):
+            self.NEWBUF_test_newbuf()
+        def test_newbuf_PyBUF_flags(self):
+            self.NEWBUF_test_newbuf_PyBUF_flags()
+        if is_pygame_pkg:
+            from pygame.tests.test_utils import array as NEWBUF_array
+        else:
+            from test.test_utils import array as NEWBUF_array
 
-    def NEWBUF_test_GetView_newbuf(self):
+    def NEWBUF_assertSame(self, proxy, exp):
+        array = self.NEWBUF_array
+        self.assertEqual(proxy.length, exp.len)
+        imp = array.BufferImporter(proxy, array.PyBUF_RECORDS_RO)
+        try:
+            self.assertEqual(imp.readonly, exp.readonly)
+            self.assertEqual(imp.format, exp.format)
+            self.assertEqual(imp.itemsize, exp.itemsize)
+            self.assertEqual(imp.ndim, exp.ndim)
+            self.assertEqual(imp.shape, exp.shape)
+            self.assertEqual(imp.strides, exp.strides)
+            self.assertTrue(imp.suboffsets is None)
+        finally:
+            imp = None
+
+    def NEWBUF_test_newbuf(self):
         from pygame.bufferproxy import BufferProxy
 
-        def exporter(shape, typechar, itemsize):
-            # Use a BufferProxy object as the new buffer exporter, as none
-            # of the Python standard types are multidimensional arrays.
-            e = self.ExporterBase(shape, typechar, itemsize)
-            bp = BufferProxy(e.__dict__)
-            bp.size = e.size
-            bp.typestr = e.typestr
-            bp.shape = e.shape
-            bp.strides = e.strides
-            bp.data = e.data
-            return bp
-
+        Exporter = self.NEWBUF_array.BufferExporter
         _shape = [2, 3, 5, 7, 11]  # Some prime numbers
         for ndim in range(1, len(_shape)):
-            o = exporter(_shape[0:ndim], 'i', 2)
+            o = Exporter(_shape[0:ndim], '=h')
             v = BufferProxy(o)
-            self.assertSame(v, o)
+            self.NEWBUF_assertSame(v, o)
         ndim = 2
         shape = _shape[0:ndim]
-        for typechar in ('i', 'u'):
-            for itemsize in (1, 2, 4, 8):
-                o = exporter(shape, typechar, itemsize)
-                v = BufferProxy(o)
-                self.assertSame(v, o)
-        for itemsize in (4, 8):
-            o = exporter(shape, 'f', itemsize)
+        for format in ['b', 'B', '=h', '=H', '=i', '=I', '=q', '=Q', 'f', 'd']:
+            o = Exporter(shape, format)
             v = BufferProxy(o)
-            self.assertSame(v, o)
+            self.NEWBUF_assertSame(v, o)
+
+    def NEWBUF_test_newbuf_PyBUF_flags(self):
+        from pygame.bufferproxy import BufferProxy
+
+        is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN
+        fsys, frev = ('<', '>') if is_lil_endian else ('>', '<')
+        array = self.NEWBUF_array
+        BufferImporter = array.BufferImporter
+        a = BufferProxy({'typestr': '|u1',
+                         'shape': (10,),
+                         'data': (9, False)}) # 9? No data accesses.
+        b = BufferImporter(a, array.PyBUF_SIMPLE)
+        self.assertTrue(b.format is None)
+        self.assertEqual(b.len, 10)
+        self.assertEqual(b.itemsize, 1)
+        self.assertTrue(b.shape is None)
+        self.assertTrue(b.strides is None)
+        self.assertTrue(b.suboffsets is None)
+        self.assertFalse(b.readonly)
+        self.assertEqual(b.buf, 9)
+        b = BufferImporter(a, array.PyBUF_ND)
+        self.assertTrue(b.format is None)
+        self.assertEqual(b.len, a.length)
+        self.assertEqual(b.itemsize, 1)
+        self.assertEqual(b.shape, (10,))
+        self.assertTrue(b.strides is None)
+        self.assertTrue(b.suboffsets is None)
+        self.assertFalse(b.readonly)
+        self.assertEqual(b.buf, 9)
+        a = BufferProxy({'typestr': fsys + 'i2',
+                         'shape': (5, 10),
+                         'strides': (24, 2),
+                         'data': (42, False)}) # 42? No data accesses.
+        b = BufferImporter(a, array.PyBUF_STRIDES)
+        self.assertTrue(b.format is None)
+        self.assertEqual(b.len, 100)
+        self.assertEqual(b.itemsize, 2)
+        self.assertEqual(b.shape, (5, 10))
+        self.assertEqual(b.strides, (24, 2))
+        self.assertTrue(b.suboffsets is None)
+        self.assertFalse(b.readonly)
+        self.assertEqual(b.buf, 42)
+        b = BufferImporter(a, array.PyBUF_FULL_RO)
+        self.assertEqual(b.format, '=h')
+        self.assertEqual(b.len, 100)
+        self.assertEqual(b.itemsize, 2)
+        self.assertEqual(b.shape, (5, 10))
+        self.assertEqual(b.strides, (24, 2))
+        self.assertTrue(b.suboffsets is None)
+        self.assertFalse(b.readonly)
+        self.assertEqual(b.buf, 42)
+        self.assertRaises(BufferError, BufferImporter, a, array.PyBUF_SIMPLE)
+        self.assertRaises(BufferError, BufferImporter, a, array.PyBUF_ND)
+        self.assertRaises(BufferError, BufferImporter, a,
+                          array.PyBUF_C_CONTIGUOUS)
+        self.assertRaises(BufferError, BufferImporter, a,
+                          array.PyBUF_F_CONTIGUOUS)
+        self.assertRaises(BufferError, BufferImporter, a,
+                          array.PyBUF_ANY_CONTIGUOUS)
+        self.assertRaises(BufferError, BufferImporter, a, array.PyBUF_CONTIG)
+        self.assertRaises(BufferError, BufferImporter, a, array.PyBUF_SIMPLE)
+        self.assertRaises(BufferError, BufferImporter, a, array.PyBUF_ND)
+        self.assertRaises(BufferError, BufferImporter, a,
+                          array.PyBUF_C_CONTIGUOUS)
+        self.assertRaises(BufferError, BufferImporter, a,
+                          array.PyBUF_F_CONTIGUOUS)
+        self.assertRaises(BufferError, BufferImporter, a,
+                          array.PyBUF_ANY_CONTIGUOUS)
+        self.assertRaises(BufferError, BufferImporter, a, array.PyBUF_CONTIG)
+        a = BufferProxy({'typestr': frev + 'i2',
+                         'shape': (5, 10),
+                         'strides': (24, 2),
+                         'data': (1000000, True)}) # 1000000? No data accesses.
+        b = BufferImporter(a, array.PyBUF_FULL_RO)
+        self.assertEqual(b.format, frev + 'h')
+        self.assertEqual(b.len, 100)
+        self.assertEqual(b.itemsize, 2)
+        self.assertEqual(b.shape, (5, 10))
+        self.assertEqual(b.strides, (24, 2))
+        self.assertTrue(b.suboffsets is None)
+        self.assertTrue(b.readonly)
+        self.assertEqual(b.buf, 1000000)
+        self.assertRaises(BufferError, BufferImporter, a, array.PyBUF_FULL)
 
     def test_PgObject_GetBuffer_exception(self):
         # For consistency with surfarray