summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorNico Schlömer <nico.schloemer@gmail.com>2023-04-20 18:00:59 +0200
committerGitHub <noreply@github.com>2023-04-20 18:00:59 +0200
commit1acac891f99075128450aacf2a4538de3ff9d028 (patch)
treed7de52ce7cbac0e2ae51eb68ec2ca4289a9eb77f /numpy
parent64e692f741da5bf781870de7f6cd1aa5bb5a4070 (diff)
downloadnumpy-1acac891f99075128450aacf2a4538de3ff9d028.tar.gz
DEP: deprecate scalar conversions for arrays with ndim > 0 (#10615)
This PR reflects some of the progress achieved in issue #10404 and is used to asses the impact of the changes. With the changes in this PR, `float(numpy.array([1.0])` now gives a warning; likewise some other things: ```python import numpy a = numpy.random.rand(10, 1) a[0] = numpy.array([1.0]) # okay a[0] = numpy.array(1.0) # okay a[0] = 1.0 # okay b = numpy.random.rand(10) b[0] = numpy.array([1.0]) # ValueError: setting an array element with a sequence. b[0, ...] = numpy.array([1.0]) # okay b[0] = numpy.array(1.0) # okay b[0] = 1.0 # okay ``` This aligns the behavior of numpy arrays with that of lists: ```python float([3.14]) ``` ``` TypeError: float() argument must be a string or a number, not 'list' ``` ```python import numpy as np a = np.random.rand(5) a[0] = [3.14] ``` ``` ValueError: setting an array element with a sequence. ``` Fixes #10404.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/function_base.py2
-rw-r--r--numpy/core/src/multiarray/common.c29
-rw-r--r--numpy/core/src/multiarray/common.h7
-rw-r--r--numpy/core/src/multiarray/methods.c4
-rw-r--r--numpy/core/src/multiarray/number.c6
-rw-r--r--numpy/core/tests/test_deprecations.py12
-rw-r--r--numpy/core/tests/test_multiarray.py25
7 files changed, 69 insertions, 16 deletions
diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py
index c82b495b1..00e4e6b0e 100644
--- a/numpy/core/function_base.py
+++ b/numpy/core/function_base.py
@@ -168,7 +168,7 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,
y += start
if endpoint and num > 1:
- y[-1] = stop
+ y[-1, ...] = stop
if axis != 0:
y = _nx.moveaxis(y, 0, axis)
diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c
index 001d299c7..573d0d606 100644
--- a/numpy/core/src/multiarray/common.c
+++ b/numpy/core/src/multiarray/common.c
@@ -441,3 +441,32 @@ new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out,
}
}
+NPY_NO_EXPORT int
+check_is_convertible_to_scalar(PyArrayObject *v)
+{
+ if (PyArray_NDIM(v) == 0) {
+ return 0;
+ }
+
+ /* Remove this if-else block when the deprecation expires */
+ if (PyArray_SIZE(v) == 1) {
+ /* Numpy 1.25.0, 2023-01-02 */
+ if (DEPRECATE(
+ "Conversion of an array with ndim > 0 to a scalar "
+ "is deprecated, and will error in future. "
+ "Ensure you extract a single element from your array "
+ "before performing this operation. "
+ "(Deprecated NumPy 1.25.)") < 0) {
+ return -1;
+ }
+ return 0;
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "only length-1 arrays can be converted to Python scalars");
+ return -1;
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "only 0-dimensional arrays can be converted to Python scalars");
+ return -1;
+}
diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h
index 127c6250d..cb9fadc4e 100644
--- a/numpy/core/src/multiarray/common.h
+++ b/numpy/core/src/multiarray/common.h
@@ -332,6 +332,13 @@ PyArray_TupleFromItems(int n, PyObject *const *items, int make_null_none)
return tuple;
}
+/*
+ * Returns 0 if the array has rank 0, -1 otherwise. Prints a deprecation
+ * warning for arrays of _size_ 1.
+ */
+NPY_NO_EXPORT int
+check_is_convertible_to_scalar(PyArrayObject *v);
+
#include "ucsnarrow.h"
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 93b290020..3b2e72820 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -2804,9 +2804,7 @@ array_complex(PyArrayObject *self, PyObject *NPY_UNUSED(args))
PyArray_Descr *dtype;
PyObject *c;
- if (PyArray_SIZE(self) != 1) {
- PyErr_SetString(PyExc_TypeError,
- "only length-1 arrays can be converted to Python scalars");
+ if (check_is_convertible_to_scalar(self) < 0) {
return NULL;
}
diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c
index c208fb203..35e4af79c 100644
--- a/numpy/core/src/multiarray/number.c
+++ b/numpy/core/src/multiarray/number.c
@@ -869,13 +869,11 @@ array_scalar_forward(PyArrayObject *v,
PyObject *(*builtin_func)(PyObject *),
const char *where)
{
- PyObject *scalar;
- if (PyArray_SIZE(v) != 1) {
- PyErr_SetString(PyExc_TypeError, "only size-1 arrays can be"\
- " converted to Python scalars");
+ if (check_is_convertible_to_scalar(v) < 0) {
return NULL;
}
+ PyObject *scalar;
scalar = PyArray_GETITEM(v, PyArray_DATA(v));
if (scalar == NULL) {
return NULL;
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py
index b92a20a12..e47a24995 100644
--- a/numpy/core/tests/test_deprecations.py
+++ b/numpy/core/tests/test_deprecations.py
@@ -822,6 +822,18 @@ class TestLoadtxtParseIntsViaFloat(_DeprecationTestCase):
assert isinstance(e.__cause__, DeprecationWarning)
+class TestScalarConversion(_DeprecationTestCase):
+ # 2023-01-02, 1.25.0
+ def test_float_conversion(self):
+ self.assert_deprecated(float, args=(np.array([3.14]),))
+
+ def test_behaviour(self):
+ b = np.array([[3.14]])
+ c = np.zeros(5)
+ with pytest.warns(DeprecationWarning):
+ c[0] = b
+
+
class TestPyIntConversion(_DeprecationTestCase):
message = r".*stop allowing conversion of out-of-bound.*"
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 4a064827d..984047c87 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -3644,9 +3644,13 @@ class TestMethods:
msg = 'dtype: {0}'.format(dt)
ap = complex(a)
assert_equal(ap, a, msg)
- bp = complex(b)
+
+ with assert_warns(DeprecationWarning):
+ bp = complex(b)
assert_equal(bp, b, msg)
- cp = complex(c)
+
+ with assert_warns(DeprecationWarning):
+ cp = complex(c)
assert_equal(cp, c, msg)
def test__complex__should_not_work(self):
@@ -3669,7 +3673,8 @@ class TestMethods:
assert_raises(TypeError, complex, d)
e = np.array(['1+1j'], 'U')
- assert_raises(TypeError, complex, e)
+ with assert_warns(DeprecationWarning):
+ assert_raises(TypeError, complex, e)
class TestCequenceMethods:
def test_array_contains(self):
@@ -8756,8 +8761,10 @@ class TestConversion:
int_funcs = (int, lambda x: x.__int__())
for int_func in int_funcs:
assert_equal(int_func(np.array(0)), 0)
- assert_equal(int_func(np.array([1])), 1)
- assert_equal(int_func(np.array([[42]])), 42)
+ with assert_warns(DeprecationWarning):
+ assert_equal(int_func(np.array([1])), 1)
+ with assert_warns(DeprecationWarning):
+ assert_equal(int_func(np.array([[42]])), 42)
assert_raises(TypeError, int_func, np.array([1, 2]))
# gh-9972
@@ -8772,7 +8779,8 @@ class TestConversion:
def __trunc__(self):
return 3
assert_equal(3, int_func(np.array(HasTrunc())))
- assert_equal(3, int_func(np.array([HasTrunc()])))
+ with assert_warns(DeprecationWarning):
+ assert_equal(3, int_func(np.array([HasTrunc()])))
else:
pass
@@ -8781,8 +8789,9 @@ class TestConversion:
raise NotImplementedError
assert_raises(NotImplementedError,
int_func, np.array(NotConvertible()))
- assert_raises(NotImplementedError,
- int_func, np.array([NotConvertible()]))
+ with assert_warns(DeprecationWarning):
+ assert_raises(NotImplementedError,
+ int_func, np.array([NotConvertible()]))
class TestWhere: