-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
bpo-33176 Add readonly mode to memoryviews #6314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3624,13 +3624,15 @@ copying. | |
|
||
.. versionadded:: 3.2 | ||
|
||
.. method:: cast(format[, shape]) | ||
.. method:: cast(format[, shape, readonly]) | ||
|
||
Cast a memoryview to a new format or shape. *shape* defaults to | ||
``[byte_length//new_itemsize]``, which means that the result view | ||
will be one-dimensional. The return value is a new memoryview, but | ||
the buffer itself is not copied. Supported casts are 1D -> C-:term:`contiguous` | ||
and C-contiguous -> 1D. | ||
and C-contiguous -> 1D. If *readonly* is ``True`` the result view will | ||
not be writable. Triying to cast a view with a readonly underlying buffer to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spelling: Trying |
||
a writable view will raise a :class:`ValueError`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Judging by the code, it's TypeError not ValueError. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also you'll need to add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does readonly=False mean, and what is the default behaviour? How do I avoid casting a readonly view to writable? Will I have to add readonly=True to all my casts? |
||
|
||
The destination format is restricted to a single element native format in | ||
:mod:`struct` syntax. One of the formats must be a byte format | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,6 +71,36 @@ def setitem(value): | |
m = None | ||
self.assertEqual(sys.getrefcount(b), oldrefcount) | ||
|
||
def test_setitem_readonly_and_writable_buffer(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I would call this test |
||
if not self.rw_type: | ||
self.skipTest("no writable type to test") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even if it's not writable, you should still be able to cast to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I test both (writable and not writable) separately? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did that adding a explicit test for |
||
b = self.rw_type(self._source) | ||
oldrefcount = sys.getrefcount(b) | ||
m = self._view(b) | ||
readonly_m = m.cast("B", readonly=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would test Same for the other test below. |
||
def setitem(value): | ||
readonly_m[0] = value | ||
self.assertRaises(TypeError, setitem, 12) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also check that Same for the other test below. |
||
m = None | ||
readonly_m = None | ||
self.assertEqual(sys.getrefcount(b), oldrefcount) | ||
|
||
def test_setitem_readonly_and_readonly_buffer(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above: rename this |
||
if not self.ro_type: | ||
self.skipTest("no writable type to test") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean "no readonly type to test". |
||
tp = self.ro_type | ||
b = self.ro_type(self._source) | ||
oldrefcount = sys.getrefcount(b) | ||
m = self._view(b) | ||
self.assertRaises(TypeError, lambda: m.cast("B", readonly=False)) | ||
readonly_m = m.cast("B", readonly=True) | ||
def setitem(value): | ||
readonly_m[0] = value | ||
self.assertRaises(TypeError, setitem, 12) | ||
m = None | ||
readonly_m = None | ||
self.assertEqual(sys.getrefcount(b), oldrefcount) | ||
|
||
def test_setitem_writable(self): | ||
if not self.rw_type: | ||
self.skipTest("no writable type to test") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add readonly flag to memoryview.cast to create readonly memoryviews. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1338,16 +1338,17 @@ zero_in_shape(PyMemoryViewObject *mv) | |
static PyObject * | ||
memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds) | ||
{ | ||
static char *kwlist[] = {"format", "shape", NULL}; | ||
static char *kwlist[] = {"format", "shape", "readonly", NULL}; | ||
PyMemoryViewObject *mv = NULL; | ||
PyObject *shape = NULL; | ||
int readonly = self->view.readonly; | ||
PyObject *format; | ||
Py_ssize_t ndim = 1; | ||
|
||
CHECK_RELEASED(self); | ||
|
||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, | ||
&format, &shape)) { | ||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O$p", kwlist, | ||
&format, &shape, &readonly)) { | ||
return NULL; | ||
} | ||
if (!PyUnicode_Check(format)) { | ||
|
@@ -1365,6 +1366,13 @@ memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds) | |
"memoryview: cannot cast view with zeros in shape or strides"); | ||
return NULL; | ||
} | ||
|
||
if (self->view.readonly && !readonly){ | ||
PyErr_SetString(PyExc_TypeError, | ||
"memoryview: cannot cast readonly buffer to a writable buffer."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: this line needs indenting. |
||
return NULL; | ||
} | ||
|
||
if (shape) { | ||
CHECK_LIST_OR_TUPLE(shape) | ||
ndim = PySequence_Fast_GET_SIZE(shape); | ||
|
@@ -1391,6 +1399,8 @@ memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds) | |
if (shape && cast_to_ND(mv, shape, (int)ndim) < 0) | ||
goto error; | ||
|
||
mv->view.readonly = readonly; | ||
|
||
return (PyObject *)mv; | ||
|
||
error: | ||
|
@@ -3058,7 +3068,7 @@ PyDoc_STRVAR(memory_tolist_doc, | |
\n\ | ||
Return the data in the buffer as a list of elements."); | ||
PyDoc_STRVAR(memory_cast_doc, | ||
"cast($self, /, format, *, shape)\n--\n\ | ||
"cast($self, /, format, *, shape, readonly)\n--\n\ | ||
\n\ | ||
Cast a memoryview to a new format or shape."); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signature here should be
cast(format[, shape[, readonly]])
(note the additional square brackets around the "readonly" parameter, since it's optional).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about something like
cast(format[, shape], *, readonly=
<unchanged>
)That implies readonly is a keyword-only parameter, which matches your doc string and tests. Using the keyword notation also reduces the bracket nesting.