Skip to content

Commit

Permalink
bpo-33176: Add a toreadonly() method to memoryviews. (pythonGH-6466)
Browse files Browse the repository at this point in the history
  • Loading branch information
pitrou authored Apr 14, 2018
1 parent b1dc075 commit 480ab05
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 16 deletions.
19 changes: 19 additions & 0 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 23 additions & 16 deletions Lib/test/test_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -924,23 +924,30 @@ def verify(self, result, obj=-1,
except BufferError: # re-exporter does not provide full information
return
ex = result.obj if isinstance(result, memoryview) else result
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.ndim, ndim)
self.assertEqual(m.shape, tuple(shape))
if not (sliced and suboffsets):
self.assertEqual(m.strides, tuple(strides))
self.assertEqual(m.suboffsets, tuple(suboffsets))

n = 1 if ndim == 0 else len(lst)
self.assertEqual(len(m), n)

rep = result.tolist() if fmt else result.tobytes()
self.assertEqual(rep, lst)
self.assertEqual(m, 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, expected_readonly)
self.assertEqual(m.ndim, ndim)
self.assertEqual(m.shape, tuple(shape))
if not (sliced and suboffsets):
self.assertEqual(m.strides, tuple(strides))
self.assertEqual(m.suboffsets, tuple(suboffsets))

n = 1 if ndim == 0 else len(lst)
self.assertEqual(len(m), n)

rep = result.tolist() if fmt else result.tobytes()
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):
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,17 @@ def test_reversed(self):
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a ``toreadonly()`` method to memoryviews.
19 changes: 19 additions & 0 deletions Objects/memoryobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,20 @@ memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds)
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 */
Expand Down Expand Up @@ -3061,13 +3075,18 @@ 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},
{"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, memory_tobytes_doc},
{"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}
Expand Down

0 comments on commit 480ab05

Please sign in to comment.