bpo-33176: Add a toreadonly() method to memoryviews. (GH-6466)

This commit is contained in:
Antoine Pitrou 2018-04-14 19:49:21 +02:00 committed by GitHub
parent b1dc07509f
commit 480ab05d5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 15 deletions

View File

@ -3591,6 +3591,25 @@ copying.
:mod:`struct` module syntax as well as multi-dimensional
representations.
.. method:: toreadonly()
Return a readonly version of the memoryview object. The original
memoryview object is unchanged. ::
>>> m = memoryview(bytearray(b'abc'))
>>> mm = m.toreadonly()
>>> mm.tolist()
[89, 98, 99]
>>> mm[0] = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot modify read-only memory
>>> m[0] = 43
>>> mm.tolist()
[43, 98, 99]
.. versionadded:: 3.8
.. method:: release()
Release the underlying buffer exposed by the memoryview object. Many

View File

@ -924,11 +924,13 @@ class TestBufferProtocol(unittest.TestCase):
except BufferError: # re-exporter does not provide full information
return
ex = result.obj if isinstance(result, memoryview) else result
def check_memoryview(m, expected_readonly=readonly):
self.assertIs(m.obj, ex)
self.assertEqual(m.nbytes, expected_len)
self.assertEqual(m.itemsize, itemsize)
self.assertEqual(m.format, fmt)
self.assertEqual(m.readonly, readonly)
self.assertEqual(m.readonly, expected_readonly)
self.assertEqual(m.ndim, ndim)
self.assertEqual(m.shape, tuple(shape))
if not (sliced and suboffsets):
@ -942,6 +944,11 @@ class TestBufferProtocol(unittest.TestCase):
self.assertEqual(rep, lst)
self.assertEqual(m, result)
check_memoryview(m)
with m.toreadonly() as mm:
check_memoryview(mm, expected_readonly=True)
m.tobytes() # Releasing mm didn't release m
def verify_getbuf(self, orig_ex, ex, req, sliced=False):
def simple_fmt(ex):
return ex.format == '' or ex.format == 'B'

View File

@ -362,6 +362,17 @@ class AbstractMemoryTests:
self.assertEqual(list(reversed(m)), aslist)
self.assertEqual(list(reversed(m)), list(m[::-1]))
def test_toreadonly(self):
for tp in self._types:
b = tp(self._source)
m = self._view(b)
mm = m.toreadonly()
self.assertTrue(mm.readonly)
self.assertTrue(memoryview(mm).readonly)
self.assertEqual(mm.tolist(), m.tolist())
mm.release()
m.tolist()
def test_issue22668(self):
a = array.array('H', [256, 256, 256, 256])
x = memoryview(a)

View File

@ -0,0 +1 @@
Add a ``toreadonly()`` method to memoryviews.

View File

@ -1398,6 +1398,20 @@ error:
return NULL;
}
static PyObject *
memory_toreadonly(PyMemoryViewObject *self, PyObject *noargs)
{
CHECK_RELEASED(self);
/* Even if self is already readonly, we still need to create a new
* object for .release() to work correctly.
*/
self = (PyMemoryViewObject *) mbuf_add_view(self->mbuf, &self->view);
if (self != NULL) {
self->view.readonly = 1;
};
return (PyObject *) self;
}
/**************************************************************************/
/* getbuffer */
@ -3061,6 +3075,10 @@ PyDoc_STRVAR(memory_cast_doc,
"cast($self, /, format, *, shape)\n--\n\
\n\
Cast a memoryview to a new format or shape.");
PyDoc_STRVAR(memory_toreadonly_doc,
"toreadonly($self, /)\n--\n\
\n\
Return a readonly version of the memoryview.");
static PyMethodDef memory_methods[] = {
{"release", (PyCFunction)memory_release, METH_NOARGS, memory_release_doc},
@ -3068,6 +3086,7 @@ static PyMethodDef memory_methods[] = {
{"hex", (PyCFunction)memory_hex, METH_NOARGS, memory_hex_doc},
{"tolist", (PyCFunction)memory_tolist, METH_NOARGS, memory_tolist_doc},
{"cast", (PyCFunction)memory_cast, METH_VARARGS|METH_KEYWORDS, memory_cast_doc},
{"toreadonly", (PyCFunction)memory_toreadonly, METH_NOARGS, memory_toreadonly_doc},
{"__enter__", memory_enter, METH_NOARGS, NULL},
{"__exit__", memory_exit, METH_VARARGS, NULL},
{NULL, NULL}