[pypy-commit] cffi default: Test and fix for from_buffer() receiving read-only buffer objects as

arigo noreply at buildbot.pypy.org
Fri Mar 13 09:44:46 CET 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1665:18a20248b9f7
Date: 2015-03-13 09:02 +0100
http://bitbucket.org/cffi/cffi/changeset/18a20248b9f7/

Log:	Test and fix for from_buffer() receiving read-only buffer objects as
	argument.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -5142,10 +5142,13 @@
        'view->obj'. */
     PyBufferProcs *pb = x->ob_type->tp_as_buffer;
     if (pb && !pb->bf_releasebuffer) {
-        /* try all three in some vaguely sensible order */
-        readbufferproc proc = (readbufferproc)pb->bf_getwritebuffer;
+        /* we used to try all three in some vaguely sensible order,
+           i.e. first the write.  But trying to call the write on a
+           read-only buffer fails with TypeError.  So we use a less-
+           sensible order now.  See test_from_buffer_more_cases. */
+        readbufferproc proc = (readbufferproc)pb->bf_getreadbuffer;
         if (!proc)     proc = (readbufferproc)pb->bf_getcharbuffer;
-        if (!proc)     proc = (readbufferproc)pb->bf_getreadbuffer;
+        if (!proc)     proc = (readbufferproc)pb->bf_getwritebuffer;
         if (proc && pb->bf_getsegcount) {
             if ((*pb->bf_getsegcount)(x, NULL) != 1) {
                 PyErr_SetString(PyExc_TypeError,
@@ -5441,6 +5444,63 @@
     return PyLong_FromVoidPtr(f);
 }
 
+static Py_ssize_t _test_segcountproc(PyObject *o, Py_ssize_t *ignored)
+{
+    return 1;
+}
+static Py_ssize_t _test_getreadbuf(PyObject *o, Py_ssize_t i, void **r)
+{
+    static char buf[] = "RDB";
+    *r = buf;
+    return 3;
+}
+static Py_ssize_t _test_getwritebuf(PyObject *o, Py_ssize_t i, void **r)
+{
+    static char buf[] = "WRB";
+    *r = buf;
+    return 3;
+}
+static Py_ssize_t _test_getcharbuf(PyObject *o, Py_ssize_t i, char **r)
+{
+    static char buf[] = "CHB";
+    *r = buf;
+    return 3;
+}
+static int _test_getbuf(PyObject *self, Py_buffer *view, int flags)
+{
+    static char buf[] = "GTB";
+    return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/0, flags);
+}
+static int _test_getbuf_ro(PyObject *self, Py_buffer *view, int flags)
+{
+    static char buf[] = "ROB";
+    return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/1, flags);
+}
+
+
+static PyObject *b__testbuff(PyObject *self, PyObject *args)
+{
+    /* for testing only */
+    int methods;
+    PyTypeObject *obj;
+    if (!PyArg_ParseTuple(args, "O!i|_testbuff", &PyType_Type, &obj, &methods))
+        return NULL;
+
+    assert(obj->tp_as_buffer != NULL);
+
+    obj->tp_as_buffer->bf_getsegcount = &_test_segcountproc;
+    obj->tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER;
+    obj->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
+    if (methods & 1)  obj->tp_as_buffer->bf_getreadbuffer  = &_test_getreadbuf;
+    if (methods & 2)  obj->tp_as_buffer->bf_getwritebuffer = &_test_getwritebuf;
+    if (methods & 4)  obj->tp_as_buffer->bf_getcharbuffer  = &_test_getcharbuf;
+    if (methods & 8)  obj->tp_as_buffer->bf_getbuffer      = &_test_getbuf;
+    if (methods & 16) obj->tp_as_buffer->bf_getbuffer      = &_test_getbuf_ro;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
 static PyMethodDef FFIBackendMethods[] = {
     {"load_library", b_load_library, METH_VARARGS},
     {"new_primitive_type", b_new_primitive_type, METH_VARARGS},
@@ -5473,6 +5533,7 @@
 #endif
     {"_get_types", b__get_types, METH_NOARGS},
     {"_testfunc", b__testfunc, METH_VARARGS},
+    {"_testbuff", b__testbuff, METH_VARARGS},
     {NULL,     NULL}    /* Sentinel */
 };
 
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -3258,6 +3258,56 @@
     cast(p, c)[1] += 500
     assert list(a) == [10000, 20500, 30000]
 
+def test_from_buffer_more_cases():
+    try:
+        from _cffi_backend import _testbuff
+    except ImportError:
+        py.test.skip("not for pypy")
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    #
+    def check1(bufobj, expected):
+        c = from_buffer(BCharA, bufobj)
+        assert typeof(c) is BCharA
+        assert list(c) == list(expected)
+    #
+    def check(methods, expected, expeted_for_memoryview=None):
+        from __builtin__ import buffer, memoryview
+        class X(object):
+            pass
+        _testbuff(X, methods)
+        bufobj = X()
+        check1(bufobj, expected)
+        try:
+            bufobjb = buffer(bufobj)
+        except TypeError:
+            pass
+        else:
+            check1(bufobjb, expected)
+        try:
+            bufobjm = memoryview(bufobj)
+        except TypeError:
+            pass
+        else:
+            check1(bufobjm, expeted_for_memoryview or expected)
+    #
+    check(1, "RDB")
+    check(2, "WRB")
+    check(4, "CHB")
+    check(8, "GTB")
+    check(16, "ROB")
+    #
+    check(1 | 2, "RDB")
+    check(1 | 4, "RDB")
+    check(2 | 4, "CHB")
+    check(1 | 8, "RDB", "GTB")
+    check(1 | 16, "RDB", "ROB")
+    check(2 | 8, "WRB", "GTB")
+    check(2 | 16, "WRB", "ROB")
+    check(4 | 8, "CHB", "GTB")
+    check(4 | 16, "CHB", "ROB")
+
 def test_version():
     # this test is here mostly for PyPy
     assert __version__ == "0.9.1"


More information about the pypy-commit mailing list