Skip to content

Commit bf45691

Browse files
committed
Add readonly mode to memoryviews
1 parent 8534d53 commit bf45691

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

Lib/test/test_memoryview.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,36 @@ def setitem(value):
7171
m = None
7272
self.assertEqual(sys.getrefcount(b), oldrefcount)
7373

74+
def test_setitem_readonly_and_writable_buffer(self):
75+
if not self.rw_type:
76+
self.skipTest("no writable type to test")
77+
b = self.rw_type(self._source)
78+
oldrefcount = sys.getrefcount(b)
79+
m = self._view(b)
80+
readonly_m = m.cast("B", readonly=True)
81+
def setitem(value):
82+
readonly_m[0] = value
83+
self.assertRaises(TypeError, setitem, 12)
84+
m = None
85+
readonly_m = None
86+
self.assertEqual(sys.getrefcount(b), oldrefcount)
87+
88+
def test_setitem_readonly_and_readonly_buffer(self):
89+
if not self.ro_type:
90+
self.skipTest("no writable type to test")
91+
tp = self.ro_type
92+
b = self.ro_type(self._source)
93+
oldrefcount = sys.getrefcount(b)
94+
m = self._view(b)
95+
self.assertRaises(TypeError, lambda: m.cast("B", readonly=False))
96+
readonly_m = m.cast("B", readonly=True)
97+
def setitem(value):
98+
readonly_m[0] = value
99+
self.assertRaises(TypeError, setitem, 12)
100+
m = None
101+
readonly_m = None
102+
self.assertEqual(sys.getrefcount(b), oldrefcount)
103+
74104
def test_setitem_writable(self):
75105
if not self.rw_type:
76106
self.skipTest("no writable type to test")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add readonly flag to memoryview.cast to create readonly memoryviews.

Objects/memoryobject.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,16 +1338,17 @@ zero_in_shape(PyMemoryViewObject *mv)
13381338
static PyObject *
13391339
memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds)
13401340
{
1341-
static char *kwlist[] = {"format", "shape", NULL};
1341+
static char *kwlist[] = {"format", "shape", "readonly", NULL};
13421342
PyMemoryViewObject *mv = NULL;
13431343
PyObject *shape = NULL;
1344+
int readonly = self->view.readonly;
13441345
PyObject *format;
13451346
Py_ssize_t ndim = 1;
13461347

13471348
CHECK_RELEASED(self);
13481349

1349-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
1350-
&format, &shape)) {
1350+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O$p", kwlist,
1351+
&format, &shape, &readonly)) {
13511352
return NULL;
13521353
}
13531354
if (!PyUnicode_Check(format)) {
@@ -1365,6 +1366,13 @@ memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds)
13651366
"memoryview: cannot cast view with zeros in shape or strides");
13661367
return NULL;
13671368
}
1369+
1370+
if (self->view.readonly && !readonly){
1371+
PyErr_SetString(PyExc_TypeError,
1372+
"memoryview: cannot cast readonly buffer to a writable buffer.");
1373+
return NULL;
1374+
}
1375+
13681376
if (shape) {
13691377
CHECK_LIST_OR_TUPLE(shape)
13701378
ndim = PySequence_Fast_GET_SIZE(shape);
@@ -1391,6 +1399,8 @@ memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds)
13911399
if (shape && cast_to_ND(mv, shape, (int)ndim) < 0)
13921400
goto error;
13931401

1402+
mv->view.readonly = readonly;
1403+
13941404
return (PyObject *)mv;
13951405

13961406
error:

0 commit comments

Comments
 (0)