summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Pitrou <pitrou@free.fr>2018-04-14 17:49:21 (GMT)
committerGitHub <noreply@github.com>2018-04-14 17:49:21 (GMT)
commit480ab05d5fee2b8fa161f799af33086a4e68c7dd (patch)
treeca45c39193fb970c4d300b27346a9dcf8aaac814
parentb1dc07509f78b354e83f5f4a902f1ff80c7bb05d (diff)
downloadcpython-480ab05d5fee2b8fa161f799af33086a4e68c7dd.zip
cpython-480ab05d5fee2b8fa161f799af33086a4e68c7dd.tar.gz
cpython-480ab05d5fee2b8fa161f799af33086a4e68c7dd.tar.bz2
bpo-33176: Add a toreadonly() method to memoryviews. (GH-6466)
-rw-r--r--Doc/library/stdtypes.rst19
-rw-r--r--Lib/test/test_buffer.py39
-rw-r--r--Lib/test/test_memoryview.py11
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-04-13-22-31-09.bpo-33176.PB9com.rst1
-rw-r--r--Objects/memoryobject.c19
5 files changed, 73 insertions, 16 deletions
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index a213189..af2b4e1 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -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
diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
index f302da4..a3f3ef0 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -924,23 +924,30 @@ class TestBufferProtocol(unittest.TestCase):
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):
diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py
index ddd5b9a..ca307d8 100644
--- a/Lib/test/test_memoryview.py
+++ b/Lib/test/test_memoryview.py
@@ -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)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-04-13-22-31-09.bpo-33176.PB9com.rst b/Misc/NEWS.d/next/Core and Builtins/2018-04-13-22-31-09.bpo-33176.PB9com.rst
new file mode 100644
index 0000000..68785b3
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-04-13-22-31-09.bpo-33176.PB9com.rst
@@ -0,0 +1 @@
+Add a ``toreadonly()`` method to memoryviews.
diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c
index ccf45ff..adaa67c 100644
--- a/Objects/memoryobject.c
+++ b/Objects/memoryobject.c
@@ -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}