Author: Matti Picus <[email protected]>
Branch: buffer-interface
Changeset: r86841:0c283916dcf6
Date: 2016-09-02 14:20 +0300
http://bitbucket.org/pypy/pypy/changeset/0c283916dcf6/
Log: add a flags test from numpy to test_memoryobject.py, implement
enough to run it
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,8 @@
from pypy.interpreter.error import oefmt
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (
- cpython_api, CANNOT_FAIL, Py_buffer, Py_TPFLAGS_HAVE_NEWBUFFER)
-from pypy.module.cpyext.pyobject import PyObject
+ cpython_api, CANNOT_FAIL, Py_buffer, Py_TPFLAGS_HAVE_NEWBUFFER,
Py_ssize_tP)
+from pypy.module.cpyext.pyobject import PyObject, as_pyobj, incref
@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
def PyObject_CheckBuffer(space, pyobj):
@@ -33,13 +33,80 @@
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")
+ buf = space.call_method(w_obj, "__buffer__", space.newint(flags))
+ try:
+ view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
+ except ValueError:
+ raise BufferError("could not create buffer from object")
+ view.c_len = buf.getlength()
+ view.c_obj = as_pyobj(space, w_obj)
+ incref(space, view.c_obj)
+ ndim = buf.getndim()
+ view.c_itemsize = buf.getitemsize()
+ rffi.setintfield(view, 'c_readonly', int(buf.readonly))
+ rffi.setintfield(view, 'c_ndim', ndim)
+ view.c_format = rffi.str2charp(buf.getformat())
+ view.c_shape = lltype.malloc(Py_ssize_tP.TO, ndim, flavor='raw')
+ view.c_strides = lltype.malloc(Py_ssize_tP.TO, ndim, flavor='raw')
+ shape = buf.getshape()
+ strides = buf.getstrides()
+ for i in range(ndim):
+ view.c_shape[i] = shape[i]
+ view.c_strides[i] = strides[i]
+ view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
+ view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
+ return 0
+
+def _IsFortranContiguous(view):
+ if view.ndim == 0:
+ return 1
+ if not view.strides:
+ return view.ndim == 1
+ sd = view.itemsize
+ if view.ndim == 1:
+ return view.shape[0] == 1 or sd == view.strides[0]
+ for i in range(view.ndim):
+ dim = view.shape[i]
+ if dim == 0:
+ return 1
+ if view.strides[i] != sd:
+ return 0
+ sd *= dim
+ return 1
+
+def _IsCContiguous(view):
+ if view.ndim == 0:
+ return 1
+ if not view.strides:
+ return view.ndim == 1
+ sd = view.itemsize
+ if view.ndim == 1:
+ return view.shape[0] == 1 or sd == view.strides[0]
+ for i in range(view.ndim-1, -1, -1):
+ dim = view.shape[i]
+ if dim == 0:
+ return 1
+ if view.strides[i] != sd:
+ return 0
+ sd *= dim
+ return 1
+
@cpython_api([lltype.Ptr(Py_buffer), lltype.Char], rffi.INT_real,
error=CANNOT_FAIL)
-def PyBuffer_IsContiguous(space, view, fortran):
+def PyBuffer_IsContiguous(space, view, fort):
"""Return 1 if the memory defined by the view is C-style (fortran is
'C') or Fortran-style (fortran is 'F') contiguous or either one
(fortran is 'A'). Return 0 otherwise."""
- # PyPy only supports contiguous Py_buffers for now.
- return 1
+ # traverse the strides, checking for consistent stride increases from
+ # right-to-left (c) or left-to-right (fortran). Copied from cpython
+ if not view.suboffsets:
+ return 0
+ if (fort == 'C'):
+ return _IsCContiguous(view)
+ elif (fort == 'F'):
+ return _IsFortranContiguous(view)
+ elif (fort == 'A'):
+ return (_IsCContiguous(view) or _IsFortranContiguous(view))
+ return 0
+
+
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
@@ -13,6 +13,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
+ # XXX needed for numpy on py3k
raise NotImplementedError('PyMemoryView_GET_BUFFER')
@cpython_api([PyObject], lltype.Ptr(Py_buffer), error=CANNOT_FAIL)
diff --git a/pypy/module/cpyext/test/buffer_test.c
b/pypy/module/cpyext/test/buffer_test.c
--- a/pypy/module/cpyext/test/buffer_test.c
+++ b/pypy/module/cpyext/test/buffer_test.c
@@ -198,10 +198,118 @@
return PyInt_FromLong(view->len);
}
+/* Copied from numpy tests */
+/*
+ * Create python string from a FLAG and or the corresponding PyBuf flag
+ * for the use in get_buffer_info.
+ */
+#define GET_PYBUF_FLAG(FLAG) \
+ buf_flag = PyUnicode_FromString(#FLAG); \
+ flag_matches = PyObject_RichCompareBool(buf_flag, tmp, Py_EQ); \
+ Py_DECREF(buf_flag); \
+ if (flag_matches == 1) { \
+ Py_DECREF(tmp); \
+ flags |= PyBUF_##FLAG; \
+ continue; \
+ } \
+ else if (flag_matches == -1) { \
+ Py_DECREF(tmp); \
+ return NULL; \
+ }
+
+
+/*
+ * Get information for a buffer through PyBuf_GetBuffer with the
+ * corresponding flags or'ed. Note that the python caller has to
+ * make sure that or'ing those flags actually makes sense.
+ * More information should probably be returned for future tests.
+ */
+static PyObject *
+get_buffer_info(PyObject *self, PyObject *args)
+{
+ PyObject *buffer_obj, *pyflags;
+ PyObject *tmp, *buf_flag;
+ Py_buffer buffer;
+ PyObject *shape, *strides;
+ Py_ssize_t i, n;
+ int flag_matches;
+ int flags = 0;
+
+ if (!PyArg_ParseTuple(args, "OO", &buffer_obj, &pyflags)) {
+ return NULL;
+ }
+
+ n = PySequence_Length(pyflags);
+ if (n < 0) {
+ return NULL;
+ }
+
+ for (i=0; i < n; i++) {
+ tmp = PySequence_GetItem(pyflags, i);
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ GET_PYBUF_FLAG(SIMPLE);
+ GET_PYBUF_FLAG(WRITABLE);
+ GET_PYBUF_FLAG(STRIDES);
+ GET_PYBUF_FLAG(ND);
+ GET_PYBUF_FLAG(C_CONTIGUOUS);
+ GET_PYBUF_FLAG(F_CONTIGUOUS);
+ GET_PYBUF_FLAG(ANY_CONTIGUOUS);
+ GET_PYBUF_FLAG(INDIRECT);
+ GET_PYBUF_FLAG(FORMAT);
+ GET_PYBUF_FLAG(STRIDED);
+ GET_PYBUF_FLAG(STRIDED_RO);
+ GET_PYBUF_FLAG(RECORDS);
+ GET_PYBUF_FLAG(RECORDS_RO);
+ GET_PYBUF_FLAG(FULL);
+ GET_PYBUF_FLAG(FULL_RO);
+ GET_PYBUF_FLAG(CONTIG);
+ GET_PYBUF_FLAG(CONTIG_RO);
+
+ Py_DECREF(tmp);
+
+ /* One of the flags must match */
+ PyErr_SetString(PyExc_ValueError, "invalid flag used.");
+ return NULL;
+ }
+
+ if (PyObject_GetBuffer(buffer_obj, &buffer, flags) < 0) {
+ return NULL;
+ }
+
+ if (buffer.shape == NULL) {
+ Py_INCREF(Py_None);
+ shape = Py_None;
+ }
+ else {
+ shape = PyTuple_New(buffer.ndim);
+ for (i=0; i < buffer.ndim; i++) {
+ PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(buffer.shape[i]));
+ }
+ }
+
+ if (buffer.strides == NULL) {
+ Py_INCREF(Py_None);
+ strides = Py_None;
+ }
+ else {
+ strides = PyTuple_New(buffer.ndim);
+ for (i=0; i < buffer.ndim; i++) {
+ PyTuple_SET_ITEM(strides, i,
PyLong_FromSsize_t(buffer.strides[i]));
+ }
+ }
+
+ PyBuffer_Release(&buffer);
+ return Py_BuildValue("(NN)", shape, strides);
+}
+
static PyMethodDef buffer_functions[] = {
{"test_buffer", (PyCFunction)test_buffer, METH_VARARGS, NULL},
+ {"get_buffer_info", (PyCFunction)get_buffer_info, METH_VARARGS, NULL},
{NULL, NULL} /* Sentinel */
};
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
@@ -27,3 +27,19 @@
assert s == struct.pack('i', 3)
viewlen = module.test_buffer(arr)
assert viewlen == y.itemsize * len(y)
+
+ def test_buffer_info(self):
+ from _numpypy import multiarray as np
+ module = self.import_module(name='buffer_test')
+ get_buffer_info = module.get_buffer_info
+ # test_export_flags from numpy test_multiarray
+ raises(ValueError, get_buffer_info, np.arange(5)[::2], ('SIMPLE',))
+ # test_relaxed_strides from numpy test_multiarray
+ arr = np.ones((1, 10))
+ if arr.flags.f_contiguous:
+ shape, strides = get_buffer_info(arr, ['F_CONTIGUOUS'])
+ assert strides[0] == 8
+ arr = np.ones((10, 1), order='F')
+ shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS'])
+ assert strides[-1] == 8
+
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
@@ -293,6 +293,8 @@
STRUCT_TYPE = PyNumberMethods
elif slot_names[0] == 'c_tp_as_sequence':
STRUCT_TYPE = PySequenceMethods
+ elif slot_names[0] == 'c_tp_as_buffer':
+ STRUCT_TYPE = PyBufferProcs
else:
raise AssertionError(
"Structure not allocated: %s" % (slot_names[0],))
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
@@ -377,7 +377,8 @@
def __exit__(self, typ, value, traceback):
keepalive_until_here(self)
- def get_buffer(self, space, readonly):
+ def get_buffer(self, space, flags):
+ readonly = not bool(flags & space.BUF_WRITABLE)
return ArrayBuffer(self, readonly)
def astype(self, space, dtype, order, copy=True):
@@ -695,6 +696,8 @@
index + self.impl.start)
def setitem(self, index, v):
+ if self.readonly:
+ raise oefmt(space.w_BufferError, "cannot write to a readonly
buffer")
raw_storage_setitem(self.impl.storage, index + self.impl.start,
rffi.cast(lltype.Char, v))
diff --git a/pypy/module/micronumpy/ndarray.py
b/pypy/module/micronumpy/ndarray.py
--- a/pypy/module/micronumpy/ndarray.py
+++ b/pypy/module/micronumpy/ndarray.py
@@ -804,20 +804,20 @@
""")
return w_result
- def buffer_w(self, space, flags):
- return self.implementation.get_buffer(space, True)
+ def buffer_w(self, space, w_flags):
+ return self.implementation.get_buffer(space, space.int_w(w_flags))
def readbuf_w(self, space):
- return self.implementation.get_buffer(space, True)
+ return self.implementation.get_buffer(space, space.BUF_FULL_RO)
def writebuf_w(self, space):
- return self.implementation.get_buffer(space, False)
+ return self.implementation.get_buffer(space, space.BUF_FULL)
def charbuf_w(self, space):
- return self.implementation.get_buffer(space, True).as_str()
+ return self.implementation.get_buffer(space,
space.BUF_FULL_RO).as_str()
def descr_get_data(self, space):
- return space.newbuffer(self.implementation.get_buffer(space, False))
+ return space.newbuffer(self.implementation.get_buffer(space,
space.BUF_FULL))
@unwrap_spec(offset=int, axis1=int, axis2=int)
def descr_diagonal(self, space, offset=0, axis1=0, axis2=1):
@@ -1697,6 +1697,7 @@
__array_wrap__ = interp2app(W_NDimArray.descr___array_wrap__),
__array_priority__ = GetSetProperty(W_NDimArray.descr___array_priority__),
__array__ = interp2app(W_NDimArray.descr___array__),
+ __buffer__ = interp2app(W_NDimArray.buffer_w),
)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit