Thanks to visit codestin.com
Credit goes to github.com

Skip to content

BUG: numpy.putmask respecting writeable flag #17873

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

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions numpy/core/src/multiarray/_multiarray_tests.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions numpy/core/src/multiarray/item_selection.c
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,10 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0)
return NULL;
}

if (PyArray_FailUnlessWriteable(self, "putmask: output array") < 0) {
return NULL;
}

mask = (PyArrayObject *)PyArray_FROM_OTF(mask0, NPY_BOOL,
NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST);
if (mask == NULL) {
Expand Down
3 changes: 2 additions & 1 deletion numpy/core/src/multiarray/mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
14 changes: 13 additions & 1 deletion numpy/core/src/multiarray/scalartypes.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -2560,6 +2570,7 @@ static int
view->shape = &length;
view->strides = NULL;
view->suboffsets = NULL;
view->readonly = 1;
Py_INCREF(self);
view->obj = self;

Expand Down Expand Up @@ -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);
}
Expand Down
10 changes: 8 additions & 2 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -4643,6 +4643,12 @@ def test_overlaps(self):
np.putmask(x[1:4], x[:3], [True, False, True])
assert_equal(x, np.array([True, True, True, True]))

def test_writeable(self):
a = np.arange(12)
a.setflags(write = False)

with pytest.raises(ValueError):
np.putmask(a, a >= 3, 2)

class TestTake:
def tst_basic(self, x):
Expand Down Expand Up @@ -7350,9 +7356,9 @@ def test_export_flags(self):
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)
Expand Down
39 changes: 34 additions & 5 deletions numpy/core/tests/test_scalarbuffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
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
Expand Down Expand Up @@ -52,10 +53,20 @@ def test_scalar_dim(self, scalar):
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,))])
Expand All @@ -77,9 +88,14 @@ def test_void_scalar_structured_data(self):
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
Expand All @@ -88,7 +104,7 @@ def test_datetime_memoryview(self):
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

Expand All @@ -100,6 +116,10 @@ def test_datetime_memoryview(self):
# 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"),
Expand All @@ -109,7 +129,8 @@ def test_str_ucs4(self, s):
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
Expand All @@ -119,7 +140,15 @@ def test_str_ucs4(self, s):

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"])
2 changes: 1 addition & 1 deletion test_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cython==0.29.21
wheel
setuptools<49.2.0
hypothesis==5.41.3
hypothesis==5.41.4
pytest==6.0.2
pytz==2020.4
pytest-cov==2.10.1
Expand Down
2 changes: 0 additions & 2 deletions tools/openblas_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@
"f68fea21fbc73d06b7566057cad2ed8c7c0eb71fabf9ed8a609f86e5bc60ce5e",
"openblas64_-v0.3.10-manylinux2014_aarch64.tar.gz":
"15e6eed8cb0df8b88e52baa136ffe1769c517e9de7bcdfd81ec56420ae1069e9",
"openblas64_-v0.3.10-win_amd64-gcc_7_1_0.zip":
"aea3f9c8bdfe0b837f0d2739a6c755b12b6838f6c983e4ede71b4e1b576e6e77",
}


Expand Down