changeset: 94423:369300948f3f user: Stefan Krah date: Sun Feb 01 14:53:54 2015 +0100 files: Lib/test/test_buffer.py Misc/NEWS Modules/_testbuffer.c Objects/abstract.c description: Issue #22445: PyBuffer_IsContiguous() now implements precise contiguity tests, compatible with NumPy's NPY_RELAXED_STRIDES_CHECKING compilation flag. Previously the function reported false negatives for corner cases. diff -r 469ff344f8fd -r 369300948f3f Lib/test/test_buffer.py --- a/Lib/test/test_buffer.py Sat Jan 31 12:20:40 2015 -0800 +++ b/Lib/test/test_buffer.py Sun Feb 01 14:53:54 2015 +0100 @@ -1007,6 +1007,7 @@ # shape, strides, offset structure = ( ([], [], 0), + ([1,3,1], [], 0), ([12], [], 0), ([12], [-1], 11), ([6], [2], 0), @@ -1078,6 +1079,18 @@ self.assertRaises(BufferError, ndarray, ex, getbuf=PyBUF_ANY_CONTIGUOUS) nd = ndarray(ex, getbuf=PyBUF_SIMPLE) + # Issue #22445: New precise contiguity definition. + for shape in [1,12,1], [7,0,7]: + for order in 0, ND_FORTRAN: + ex = ndarray(items, shape=shape, flags=order|ND_WRITABLE) + self.assertTrue(is_contiguous(ex, 'F')) + self.assertTrue(is_contiguous(ex, 'C')) + + for flags in requests: + nd = ndarray(ex, getbuf=flags) + self.assertTrue(is_contiguous(nd, 'F')) + self.assertTrue(is_contiguous(nd, 'C')) + def test_ndarray_exceptions(self): nd = ndarray([9], [1]) ndm = ndarray([9], [1], flags=ND_VAREXPORT) diff -r 469ff344f8fd -r 369300948f3f Misc/NEWS --- a/Misc/NEWS Sat Jan 31 12:20:40 2015 -0800 +++ b/Misc/NEWS Sun Feb 01 14:53:54 2015 +0100 @@ -1531,6 +1531,10 @@ C API ----- +- Issue #22445: PyBuffer_IsContiguous() now implements precise contiguity + tests, compatible with NumPy's NPY_RELAXED_STRIDES_CHECKING compilation + flag. Previously the function reported false negatives for corner cases. + - Issue #22079: PyType_Ready() now checks that statically allocated type has no dynamically allocated bases. diff -r 469ff344f8fd -r 369300948f3f Modules/_testbuffer.c --- a/Modules/_testbuffer.c Sat Jan 31 12:20:40 2015 -0800 +++ b/Modules/_testbuffer.c Sun Feb 01 14:53:54 2015 +0100 @@ -1510,6 +1510,19 @@ view->shape = NULL; } + /* Ascertain that the new buffer has the same contiguity as the exporter */ + if (ND_C_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'C') || + /* skip cast to 1-d */ + (view->format != NULL && view->shape != NULL && + ND_FORTRAN_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'F')) || + /* cast to 1-d */ + (view->format == NULL && view->shape == NULL && + !PyBuffer_IsContiguous(view, 'F'))) { + PyErr_SetString(PyExc_BufferError, + "ndarray: contiguity mismatch in getbuf()"); + return -1; + } + view->obj = (PyObject *)self; Py_INCREF(view->obj); self->head->exports++; @@ -2206,6 +2219,8 @@ for (i = 0; i < base->ndim; i++) base->suboffsets[i] = -1; + nd->head->flags &= ~(ND_C|ND_FORTRAN); + Py_RETURN_NONE; } @@ -2469,13 +2484,12 @@ { Py_ssize_t i; - if (ndim == 1 && shape && shape[0] == 1) { - /* This is for comparing strides: For example, the array - [175], shape=[1], strides=[-5] is considered contiguous. */ - return 1; - } for (i = 0; i < ndim; i++) { + if (shape && shape[i] <= 1) { + /* strides can differ if the dimension is less than 2 */ + continue; + } if (a1[i] != a2[i]) { return 0; } @@ -2555,30 +2569,35 @@ PyObject *obj; PyObject *order; PyObject *ret = NULL; - Py_buffer view; + Py_buffer view, *base; char ord; if (!PyArg_ParseTuple(args, "OO", &obj, &order)) { return NULL; } - if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) { - PyErr_SetString(PyExc_TypeError, - "is_contiguous: object does not implement the buffer " - "protocol"); - return NULL; - } - ord = get_ascii_order(order); if (ord == CHAR_MAX) { - goto release; + return NULL; + } + + if (NDArray_Check(obj)) { + /* Skip the buffer protocol to check simple etc. buffers directly. */ + base = &((NDArrayObject *)obj)->head->base; + ret = PyBuffer_IsContiguous(base, ord) ? Py_True : Py_False; } - - ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False; + else { + if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) { + PyErr_SetString(PyExc_TypeError, + "is_contiguous: object does not implement the buffer " + "protocol"); + return NULL; + } + ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False; + PyBuffer_Release(&view); + } + Py_INCREF(ret); - -release: - PyBuffer_Release(&view); return ret; } diff -r 469ff344f8fd -r 369300948f3f Objects/abstract.c --- a/Objects/abstract.c Sat Jan 31 12:20:40 2015 -0800 +++ b/Objects/abstract.c Sun Feb 01 14:53:54 2015 +0100 @@ -367,16 +367,35 @@ Py_ssize_t sd, dim; int i; - if (view->ndim == 0) return 1; - if (view->strides == NULL) return (view->ndim == 1); + /* 1) len = product(shape) * itemsize + 2) itemsize > 0 + 3) len = 0 <==> exists i: shape[i] = 0 */ + if (view->len == 0) return 1; + if (view->strides == NULL) { /* C-contiguous by definition */ + /* Trivially F-contiguous */ + if (view->ndim <= 1) return 1; + + /* ndim > 1 implies shape != NULL */ + assert(view->shape != NULL); + + /* Effectively 1-d */ + sd = 0; + for (i=0; indim; i++) { + if (view->shape[i] > 1) sd += 1; + } + return sd <= 1; + } + + /* strides != NULL implies both of these */ + assert(view->ndim > 0); + assert(view->shape != NULL); sd = view->itemsize; - if (view->ndim == 1) return (view->shape[0] == 1 || - sd == view->strides[0]); for (i=0; indim; i++) { dim = view->shape[i]; - if (dim == 0) return 1; - if (view->strides[i] != sd) return 0; + if (dim > 1 && view->strides[i] != sd) { + return 0; + } sd *= dim; } return 1; @@ -388,16 +407,22 @@ Py_ssize_t sd, dim; int i; - if (view->ndim == 0) return 1; - if (view->strides == NULL) return 1; + /* 1) len = product(shape) * itemsize + 2) itemsize > 0 + 3) len = 0 <==> exists i: shape[i] = 0 */ + if (view->len == 0) return 1; + if (view->strides == NULL) return 1; /* C-contiguous by definition */ + + /* strides != NULL implies both of these */ + assert(view->ndim > 0); + assert(view->shape != NULL); sd = view->itemsize; - if (view->ndim == 1) return (view->shape[0] == 1 || - sd == view->strides[0]); for (i=view->ndim-1; i>=0; i--) { dim = view->shape[i]; - if (dim == 0) return 1; - if (view->strides[i] != sd) return 0; + if (dim > 1 && view->strides[i] != sd) { + return 0; + } sd *= dim; } return 1;