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 :mod:`struct` module syntax as well as multi-dimensional
representations. 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() .. method:: release()
Release the underlying buffer exposed by the memoryview object. Many 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 except BufferError: # re-exporter does not provide full information
return return
ex = result.obj if isinstance(result, memoryview) else result ex = result.obj if isinstance(result, memoryview) else result
def check_memoryview(m, expected_readonly=readonly):
self.assertIs(m.obj, ex) self.assertIs(m.obj, ex)
self.assertEqual(m.nbytes, expected_len) self.assertEqual(m.nbytes, expected_len)
self.assertEqual(m.itemsize, itemsize) self.assertEqual(m.itemsize, itemsize)
self.assertEqual(m.format, fmt) self.assertEqual(m.format, fmt)
self.assertEqual(m.readonly, readonly) self.assertEqual(m.readonly, expected_readonly)
self.assertEqual(m.ndim, ndim) self.assertEqual(m.ndim, ndim)
self.assertEqual(m.shape, tuple(shape)) self.assertEqual(m.shape, tuple(shape))
if not (sliced and suboffsets): if not (sliced and suboffsets):
@ -942,6 +944,11 @@ class TestBufferProtocol(unittest.TestCase):
self.assertEqual(rep, lst) self.assertEqual(rep, lst)
self.assertEqual(m, result) 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 verify_getbuf(self, orig_ex, ex, req, sliced=False):
def simple_fmt(ex): def simple_fmt(ex):
return ex.format == '' or ex.format == 'B' 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)), aslist)
self.assertEqual(list(reversed(m)), list(m[::-1])) 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): def test_issue22668(self):
a = array.array('H', [256, 256, 256, 256]) a = array.array('H', [256, 256, 256, 256])
x = memoryview(a) x = memoryview(a)

View File

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

View File

@ -1398,6 +1398,20 @@ error:
return NULL; 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 */ /* getbuffer */
@ -3061,6 +3075,10 @@ PyDoc_STRVAR(memory_cast_doc,
"cast($self, /, format, *, shape)\n--\n\ "cast($self, /, format, *, shape)\n--\n\
\n\ \n\
Cast a memoryview to a new format or shape."); 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[] = { static PyMethodDef memory_methods[] = {
{"release", (PyCFunction)memory_release, METH_NOARGS, memory_release_doc}, {"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}, {"hex", (PyCFunction)memory_hex, METH_NOARGS, memory_hex_doc},
{"tolist", (PyCFunction)memory_tolist, METH_NOARGS, memory_tolist_doc}, {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, memory_tolist_doc},
{"cast", (PyCFunction)memory_cast, METH_VARARGS|METH_KEYWORDS, memory_cast_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}, {"__enter__", memory_enter, METH_NOARGS, NULL},
{"__exit__", memory_exit, METH_VARARGS, NULL}, {"__exit__", memory_exit, METH_VARARGS, NULL},
{NULL, NULL} {NULL, NULL}