[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