Why is Python telling me that the memory view of a record is readonly?
>>> x = np.zeros(1, dtype='d,d,i')
>>> x
array([(0., 0., 0)], dtype=[('f0', '<f8'), ('f1', '<f8'), ('f2', '<i4')])
>>> memoryview(x).readonly
False
>>> memoryview(x[0]).readonly
True
Obviously, x[0] isn't readonly, since
>>> x[0][0] += 1
>>> x[0]
(1., 0., 0)
Memory view doesn't have trouble with normal arrays:
>>> y = np.zeros((3, 4))
>>> memoryview(y).readonly
False
>>> memoryview(y[0]).readonly
False
Likewise, the deprecated __array_info__ knows that x[0] is read-write:
>>> x.__array_interface__['data'] # returns tuple (address, read-only)
(105553143159680, False)
>>> x[0].array_interface__['data']
(105553143159680, False)
My actual issue is in C code. Fortunately all my issues there can also be shown in pure Python.
I'm trying to read and write numpy records in C code, and I just need the address of the data. I can find the address of the data just fine using __array_interface__ and its corresponding C-side __array_struct__. But there is a note on that page saying that this is legacy, and that new code should be using the buffer protocol.
But the buffer protocol (which is mimicked by memoryview in Python) thinks the record is read-only. I have to specifically ask for a readonly buffer. Yes, I could get the address of the data from the "readonly" buffer, and write to it anyway, but that feels dirty.
Updated to respond to @tdelaney's comment:
As an experiment, I wrote a small C function that requested a read-only memory buffer, found the start address, and incremented the double there even though it wasn't supposed to.
void foo(PyObject *object) {
Py_buffer view;
PyObject_GetBuffer(object, &view, PyBUF_CONTIG_RO);
if (view.obj) {
double *data = (double *)view.buf;
*data += 1;
}
PyBuffer_Release(&view);
}
I could then look at the resulting array in Python.
For arrays of records, both foo(x) and foo(x[1]) correctly incremented an array element.
For the two-dimensional array of doubles, both foo(y) and foo(y[1]) correctly incremented an array element. As expected, foo(y[1][2]) did nothing.
So for records, the np.void is not copied. At least in the case.