diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2022-06-28 17:31:28 -0700 |
---|---|---|
committer | Sebastian Berg <sebastianb@nvidia.com> | 2022-10-12 10:41:40 +0200 |
commit | d894650a624234bd1bc7fb20d74a58b44715622c (patch) | |
tree | 82d28b7684ec81f5bdd3e59d50ff5b7c85cd1aec | |
parent | 409cccf70341a9ef9c80138dd9569f6ca3720cef (diff) | |
download | numpy-d894650a624234bd1bc7fb20d74a58b44715622c.tar.gz |
ENH: Get correct integer conversion into ufuncs
-rw-r--r-- | numpy/core/src/multiarray/dtypemeta.h | 2 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 35 | ||||
-rw-r--r-- | numpy/core/tests/test_nep50_promotions.py | 9 |
3 files changed, 42 insertions, 4 deletions
diff --git a/numpy/core/src/multiarray/dtypemeta.h b/numpy/core/src/multiarray/dtypemeta.h index 618491c98..15318f040 100644 --- a/numpy/core/src/multiarray/dtypemeta.h +++ b/numpy/core/src/multiarray/dtypemeta.h @@ -51,7 +51,6 @@ typedef struct { ensure_canonical_function *ensure_canonical; /* * Currently only used for experimental user DTypes. - * Typing as `void *` until NumPy itself uses these (directly). */ setitemfunction *setitem; getitemfunction *getitem; @@ -105,6 +104,7 @@ typedef struct { #define NPY_DT_CALL_setitem(descr, value, data_ptr) \ NPY_DT_SLOTS(NPY_DTYPE(descr))->setitem(descr, value, data_ptr) + /* * This function will hopefully be phased out or replaced, but was convenient * for incremental implementation of new DTypes based on DTypeMeta. diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 5485c2006..693a6d6c9 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -4947,6 +4947,41 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, goto fail; } + if (promoting_pyscalars) { + /* + * Python integers need to be cast specially. For other python + * scalars it does not hurt either. It would be nice to never create + * the array in this case, but that is difficult until value-based + * promotion rules are gone. (After that, we may get away with using + * dummy arrays rather than real arrays for the legacy resolvers.) + */ + for (int i = 0; i < nin; i++) { + int orig_flags = PyArray_FLAGS(operands[i]); + if (!(orig_flags & NPY_ARRAY_WAS_PYTHON_LITERAL)) { + continue; + } + /* If the descriptor matches, no need to worry about conversion */ + if (PyArray_EquivTypes( + PyArray_DESCR(operands[i]), operation_descrs[i])) { + continue; + } + /* Otherwise, replace the operand with a new array */ + PyArray_Descr *descr = operation_descrs[i]; + Py_INCREF(descr); + PyArrayObject *new = (PyArrayObject *)PyArray_NewFromDescr( + &PyArray_Type, descr, 0, NULL, NULL, NULL, 0, NULL); + Py_SETREF(operands[i], new); + if (operands[i] == NULL) { + goto fail; + } + + PyObject *value = PyTuple_GET_ITEM(full_args.in, i); + if (PyArray_SETITEM(new, PyArray_BYTES(operands[i]), value) < 0) { + goto fail; + } + } + } + if (subok) { _find_array_prepare(full_args, output_array_prepare, nout); } diff --git a/numpy/core/tests/test_nep50_promotions.py b/numpy/core/tests/test_nep50_promotions.py index 3957bdc3e..0c0755e66 100644 --- a/numpy/core/tests/test_nep50_promotions.py +++ b/numpy/core/tests/test_nep50_promotions.py @@ -76,12 +76,15 @@ def test_nep50_without_warnings(): assert res.dtype == np.float32 -@pytest.mark.xfail def test_nep50_integer_conversion_errors(): + # Do not worry about warnings here (auto-fixture will reset). + np._set_promotion_state("weak") # Implementation for error paths is mostly missing (as of writing) - with pytest.raises(ValueError): # (or TypeError?) + with pytest.raises(OverflowError, match=".*uint8"): np.array([1], np.uint8) + 300 - with pytest.raises(ValueError): # (or TypeError?) + with pytest.raises(OverflowError, match=".*uint8"): np.uint8(1) + 300 + with pytest.raises(OverflowError, match=".*unsigned int"): + np.uint8(1) + -1 |