summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/_multiarray_tests.c.src5
-rw-r--r--numpy/core/src/multiarray/mapping.c3
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src14
-rw-r--r--numpy/core/tests/test_multiarray.py4
-rw-r--r--numpy/core/tests/test_scalarbuffer.py39
5 files changed, 54 insertions, 11 deletions
diff --git a/numpy/core/src/multiarray/_multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src
index 3811e87a8..3c8caefce 100644
--- a/numpy/core/src/multiarray/_multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/_multiarray_tests.c.src
@@ -677,11 +677,12 @@ create_custom_field_dtype(PyObject *NPY_UNUSED(mod), PyObject *args)
"invalid error argument to test function.");
}
if (PyArray_RegisterDataType(dtype) < 0) {
- /* Fix original type in the error_path == 2 case. */
+ /* Fix original type in the error_path == 2 case and delete it */
Py_SET_TYPE(dtype, original_type);
+ Py_DECREF(dtype);
return NULL;
}
- Py_INCREF(dtype);
+ Py_INCREF(dtype); /* hold on to the original (leaks a reference) */
return (PyObject *)dtype;
}
diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c
index cb5c3823d..d64962f87 100644
--- a/numpy/core/src/multiarray/mapping.c
+++ b/numpy/core/src/multiarray/mapping.c
@@ -3194,9 +3194,10 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
}
PyObject *shape2 = convert_shape_to_string(mit->nd, mit->dimensions, "");
- if (shape2 == NULL)
+ if (shape2 == NULL) {
Py_DECREF(shape1);
goto finish;
+ }
PyErr_Format(PyExc_ValueError,
"shape mismatch: value array of shape %S could not be broadcast "
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index d018fccbb..e480628e7 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -2395,6 +2395,10 @@ gentype_arrtype_getbuffer(PyObject *self, Py_buffer *view, int flags)
self);
return -1;
}
+ if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) {
+ PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly");
+ return -1;
+ }
PyArray_Descr *descr = PyArray_DescrFromScalar(self);
if (descr == NULL) {
return -1;
@@ -2413,6 +2417,7 @@ gentype_arrtype_getbuffer(PyObject *self, Py_buffer *view, int flags)
view->shape = NULL;
view->strides = NULL;
view->suboffsets = NULL;
+ view->readonly = 1; /* assume general (user) scalars are readonly. */
Py_INCREF(self);
view->obj = self;
view->buf = scalar_value(self, descr);
@@ -2444,6 +2449,7 @@ static int
@name@_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) {
+ PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly");
return -1;
}
Py@Name@ScalarObject *scalar = (Py@Name@ScalarObject *)self;
@@ -2456,6 +2462,7 @@ static int
view->shape = NULL;
view->strides = NULL;
view->suboffsets = NULL;
+ view->readonly = 1;
Py_INCREF(self);
view->obj = self;
view->buf = &(scalar->obval);
@@ -2482,6 +2489,7 @@ static int
unicode_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) {
+ PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly");
return -1;
}
PyUnicodeScalarObject *scalar = (PyUnicodeScalarObject *)self;
@@ -2493,6 +2501,7 @@ unicode_getbuffer(PyObject *self, Py_buffer *view, int flags)
view->shape = NULL;
view->strides = NULL;
view->suboffsets = NULL;
+ view->readonly = 1;
Py_INCREF(self);
view->obj = self;
@@ -2522,7 +2531,7 @@ unicode_getbuffer(PyObject *self, Py_buffer *view, int flags)
view->format = scalar->buffer_fmt;
}
else {
- scalar->buffer_fmt = PyObject_Malloc(22);
+ scalar->buffer_fmt = PyMem_Malloc(22);
if (scalar->buffer_fmt == NULL) {
Py_SETREF(view->obj, NULL);
return -1;
@@ -2549,6 +2558,7 @@ static int
@name@_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
if ((flags & PyBUF_WRITEABLE) == PyBUF_WRITEABLE) {
+ PyErr_SetString(PyExc_BufferError, "scalar buffer is readonly");
return -1;
}
Py@Name@ScalarObject *scalar = (Py@Name@ScalarObject *)self;
@@ -2560,6 +2570,7 @@ static int
view->shape = &length;
view->strides = NULL;
view->suboffsets = NULL;
+ view->readonly = 1;
Py_INCREF(self);
view->obj = self;
@@ -2651,6 +2662,7 @@ unicode_arrtype_dealloc(PyObject *v)
{
/* note: may be null if it was never requested */
PyMem_Free(PyArrayScalar_VAL(v, Unicode));
+ PyMem_Free(((PyUnicodeScalarObject *)v)->buffer_fmt);
/* delegate to the base class */
PyUnicode_Type.tp_dealloc(v);
}
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 12306cbb8..bd8c51ab7 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -7350,9 +7350,9 @@ class TestNewBufferProtocol:
def test_export_and_pickle_user_dtype(self, obj, error):
# User dtypes should export successfully when FORMAT was not requested.
with pytest.raises(error):
- _multiarray_tests.get_buffer_info(obj, ("STRIDED", "FORMAT"))
+ _multiarray_tests.get_buffer_info(obj, ("STRIDED_RO", "FORMAT"))
- _multiarray_tests.get_buffer_info(obj, ("STRIDED",))
+ _multiarray_tests.get_buffer_info(obj, ("STRIDED_RO",))
# This is currently also necessary to implement pickling:
pickle_obj = pickle.dumps(obj)
diff --git a/numpy/core/tests/test_scalarbuffer.py b/numpy/core/tests/test_scalarbuffer.py
index 574c56864..851cd3081 100644
--- a/numpy/core/tests/test_scalarbuffer.py
+++ b/numpy/core/tests/test_scalarbuffer.py
@@ -3,6 +3,7 @@ Test scalar buffer interface adheres to PEP 3118
"""
import numpy as np
from numpy.core._rational_tests import rational
+from numpy.core._multiarray_tests import get_buffer_info
import pytest
from numpy.testing import assert_, assert_equal, assert_raises
@@ -52,10 +53,20 @@ class TestScalarPEP3118:
assert_equal(mv_x.suboffsets, ())
@pytest.mark.parametrize('scalar, code', scalars_and_codes, ids=codes_only)
- def test_scalar_known_code(self, scalar, code):
+ def test_scalar_code_and_properties(self, scalar, code):
x = scalar()
+ expected = dict(strides=(), itemsize=x.dtype.itemsize, ndim=0,
+ shape=(), format=code, readonly=True)
+
mv_x = memoryview(x)
- assert_equal(mv_x.format, code)
+ print(mv_x.readonly, self._as_dict(mv_x))
+ assert self._as_dict(mv_x) == expected
+
+ @pytest.mark.parametrize('scalar', scalars_only, ids=codes_only)
+ def test_scalar_buffers_readonly(self, scalar):
+ x = scalar()
+ with pytest.raises(BufferError, match="scalar buffer is readonly"):
+ get_buffer_info(x, ["WRITABLE"])
def test_void_scalar_structured_data(self):
dt = np.dtype([('name', np.unicode_, 16), ('grades', np.float64, (2,))])
@@ -77,9 +88,14 @@ class TestScalarPEP3118:
assert_equal(mv_x.itemsize, mv_a.itemsize)
assert_equal(mv_x.format, mv_a.format)
+ # Check that we do not allow writeable buffer export (technically
+ # we could allow it sometimes here...)
+ with pytest.raises(BufferError, match="scalar buffer is readonly"):
+ get_buffer_info(x, ["WRITABLE"])
+
def _as_dict(self, m):
return dict(strides=m.strides, shape=m.shape, itemsize=m.itemsize,
- ndim=m.ndim, format=m.format)
+ ndim=m.ndim, format=m.format, readonly=m.readonly)
def test_datetime_memoryview(self):
# gh-11656
@@ -88,7 +104,7 @@ class TestScalarPEP3118:
dt1 = np.datetime64('2016-01-01')
dt2 = np.datetime64('2017-01-01')
expected = dict(strides=(1,), itemsize=1, ndim=1, shape=(8,),
- format='B')
+ format='B', readonly=True)
v = memoryview(dt1)
assert self._as_dict(v) == expected
@@ -100,6 +116,10 @@ class TestScalarPEP3118:
# Fails to create a PEP 3118 valid buffer
assert_raises((ValueError, BufferError), memoryview, a[0])
+ # Check that we do not allow writeable buffer export
+ with pytest.raises(BufferError, match="scalar buffer is readonly"):
+ get_buffer_info(dt1, ["WRITABLE"])
+
@pytest.mark.parametrize('s', [
pytest.param("\x32\x32", id="ascii"),
pytest.param("\uFE0F\uFE0F", id="basic multilingual"),
@@ -109,7 +129,8 @@ class TestScalarPEP3118:
s = np.str_(s) # only our subclass implements the buffer protocol
# all the same, characters always encode as ucs4
- expected = dict(strides=(), itemsize=8, ndim=0, shape=(), format='2w')
+ expected = dict(strides=(), itemsize=8, ndim=0, shape=(), format='2w',
+ readonly=True)
v = memoryview(s)
assert self._as_dict(v) == expected
@@ -119,7 +140,15 @@ class TestScalarPEP3118:
assert_equal(code_points, [ord(c) for c in s])
+ # Check that we do not allow writeable buffer export
+ with pytest.raises(BufferError, match="scalar buffer is readonly"):
+ get_buffer_info(s, ["WRITABLE"])
+
def test_user_scalar_fails_buffer(self):
r = rational(1)
with assert_raises(TypeError):
memoryview(r)
+
+ # Check that we do not allow writeable buffer export
+ with pytest.raises(BufferError, match="scalar buffer is readonly"):
+ get_buffer_info(r, ["WRITABLE"]) \ No newline at end of file