summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2022-06-28 17:31:28 -0700
committerSebastian Berg <sebastianb@nvidia.com>2022-10-12 10:41:40 +0200
commitd894650a624234bd1bc7fb20d74a58b44715622c (patch)
tree82d28b7684ec81f5bdd3e59d50ff5b7c85cd1aec
parent409cccf70341a9ef9c80138dd9569f6ca3720cef (diff)
downloadnumpy-d894650a624234bd1bc7fb20d74a58b44715622c.tar.gz
ENH: Get correct integer conversion into ufuncs
-rw-r--r--numpy/core/src/multiarray/dtypemeta.h2
-rw-r--r--numpy/core/src/umath/ufunc_object.c35
-rw-r--r--numpy/core/tests/test_nep50_promotions.py9
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