summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2020-07-20 12:05:24 -0500
committerSebastian Berg <sebastian@sipsolutions.net>2020-07-20 12:06:57 -0500
commita44e6719eea572a785851306c9d41ae0199cfa70 (patch)
treeea4ca869e8cf94d5fa5ccdb1a38178101405b8da
parent6ef5ec39cdfaf77aa4600ec2e3bf9f679a4fd527 (diff)
downloadnumpy-a44e6719eea572a785851306c9d41ae0199cfa70.tar.gz
BUG: Fix string/bytes to complex assignment
This was a regression, which happened because of discrepencies between setting a single item from a string (which was not possible) and casting from a string to array (which was possible before). The seconnd code path changed, relying on the former behaviour, which broke it. This fixes the issue by bringing the second branch in line. Closes gh-16909
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src37
-rw-r--r--numpy/core/tests/test_api.py23
-rw-r--r--numpy/core/tests/test_regression.py5
3 files changed, 61 insertions, 4 deletions
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 1c93fa0ef..54fd092b0 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -302,9 +302,42 @@ static int
oop.real = NPY_NAN;
oop.imag = NPY_NAN;
}
+ else if (PyBytes_Check(op) || PyUnicode_Check(op)) {
+ /*
+ * Unlike most numeric conversion functions PyComplex_AsCComplex
+ * does not handle strings, so we have to use its constructor.
+ */
+ PyObject *pycomplex, *args;
+ if (PyBytes_Check(op)) {
+ /* The complex constructor expects unicode */
+ PyObject *unicode;
+ unicode = PyUnicode_FromEncodedObject(op, NULL, NULL);
+ if (unicode == NULL) {
+ return -1;
+ }
+ args = PyTuple_Pack(1, unicode);
+ Py_DECREF(unicode);
+ }
+ else {
+ args = PyTuple_Pack(1, op);
+ }
+ if (args == NULL) {
+ return -1;
+ }
+ pycomplex = PyComplex_Type.tp_new(&PyComplex_Type, args, NULL);
+ Py_DECREF(args);
+ if (pycomplex == NULL) {
+ return -1;
+ }
+ oop = PyComplex_AsCComplex(pycomplex);
+ Py_DECREF(pycomplex);
+ if (error_converting(oop.real)) {
+ return -1;
+ }
+ }
else {
- oop = PyComplex_AsCComplex (op);
- if (PyErr_Occurred()) {
+ oop = PyComplex_AsCComplex(op);
+ if (error_converting(oop.real)) {
return -1;
}
}
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py
index 067cadf78..24730f969 100644
--- a/numpy/core/tests/test_api.py
+++ b/numpy/core/tests/test_api.py
@@ -317,6 +317,29 @@ def test_string_to_boolean_cast_errors(dtype, out_dtype):
with assert_raises(ValueError):
arr.astype(out_dtype)
+@pytest.mark.parametrize("str_type", [str, bytes, np.str_, np.unicode_])
+@pytest.mark.parametrize("scalar_type",
+ [np.complex64, np.complex128, np.clongdouble])
+def test_string_to_complex_cast(str_type, scalar_type):
+ value = scalar_type(b"1+3j")
+ assert scalar_type(value) == 1+3j
+ assert np.array([value], dtype=object).astype(scalar_type)[()] == 1+3j
+ assert np.array(value).astype(scalar_type)[()] == 1+3j
+ arr = np.zeros(1, dtype=scalar_type)
+ arr[0] = value
+ assert arr[0] == 1+3j
+
+@pytest.mark.parametrize("dtype", np.typecodes["AllFloat"])
+def test_none_to_nan_cast(dtype):
+ # Note that at the time of writing this test, the scalar constructors
+ # reject None
+ arr = np.zeros(1, dtype=dtype)
+ arr[0] = None
+ assert np.isnan(arr)[0]
+ assert np.isnan(np.array(None, dtype=dtype))[()]
+ assert np.isnan(np.array([None], dtype=dtype))[0]
+ assert np.isnan(np.array(None).astype(dtype))[()]
+
def test_copyto_fromscalar():
a = np.arange(6, dtype='f4').reshape(2, 3)
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index 170d20157..51cf7039f 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -2427,9 +2427,10 @@ class TestRegression:
assert b'numpy.core.multiarray' in s
def test_object_casting_errors(self):
- # gh-11993
+ # gh-11993 update to ValueError (see gh-16909), since strings can in
+ # principle be converted to complex, but this string cannot.
arr = np.array(['AAAAA', 18465886.0, 18465886.0], dtype=object)
- assert_raises(TypeError, arr.astype, 'c8')
+ assert_raises(ValueError, arr.astype, 'c8')
def test_eff1d_casting(self):
# gh-12711