diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/arrayfunction_override.c | 85 | ||||
-rw-r--r-- | numpy/core/src/multiarray/convert.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/item_selection.c | 106 | ||||
m--------- | numpy/core/src/npysort/x86-simd-sort | 0 | ||||
-rw-r--r-- | numpy/core/src/umath/loops_trigonometric.dispatch.c.src | 2 | ||||
-rw-r--r-- | numpy/core/tests/test_overrides.py | 19 | ||||
-rw-r--r-- | numpy/lib/npyio.pyi | 2 | ||||
-rw-r--r-- | numpy/typing/tests/data/reveal/npyio.pyi | 10 |
8 files changed, 201 insertions, 25 deletions
diff --git a/numpy/core/src/multiarray/arrayfunction_override.c b/numpy/core/src/multiarray/arrayfunction_override.c index 04768504e..3c55e2164 100644 --- a/numpy/core/src/multiarray/arrayfunction_override.c +++ b/numpy/core/src/multiarray/arrayfunction_override.c @@ -419,6 +419,9 @@ typedef struct { PyObject *dict; PyObject *relevant_arg_func; PyObject *default_impl; + /* The following fields are used to clean up TypeError messages only: */ + PyObject *dispatcher_name; + PyObject *public_name; } PyArray_ArrayFunctionDispatcherObject; @@ -428,10 +431,72 @@ dispatcher_dealloc(PyArray_ArrayFunctionDispatcherObject *self) Py_CLEAR(self->relevant_arg_func); Py_CLEAR(self->default_impl); Py_CLEAR(self->dict); + Py_CLEAR(self->dispatcher_name); + Py_CLEAR(self->public_name); PyObject_FREE(self); } +static void +fix_name_if_typeerror(PyArray_ArrayFunctionDispatcherObject *self) +{ + if (!PyErr_ExceptionMatches(PyExc_TypeError)) { + return; + } + + PyObject *exc, *val, *tb, *message; + PyErr_Fetch(&exc, &val, &tb); + + if (!PyUnicode_CheckExact(val)) { + /* + * We expect the error to be unnormalized, but maybe it isn't always + * the case, so normalize and fetch args[0] if it isn't a string. + */ + PyErr_NormalizeException(&exc, &val, &tb); + + PyObject *args = PyObject_GetAttrString(val, "args"); + if (args == NULL || !PyTuple_CheckExact(args) + || PyTuple_GET_SIZE(args) != 1) { + Py_XDECREF(args); + goto restore_error; + } + message = PyTuple_GET_ITEM(args, 0); + Py_INCREF(message); + Py_DECREF(args); + if (!PyUnicode_CheckExact(message)) { + Py_DECREF(message); + goto restore_error; + } + } + else { + Py_INCREF(val); + message = val; + } + + Py_ssize_t cmp = PyUnicode_Tailmatch( + message, self->dispatcher_name, 0, -1, -1); + if (cmp <= 0) { + Py_DECREF(message); + goto restore_error; + } + Py_SETREF(message, PyUnicode_Replace( + message, self->dispatcher_name, self->public_name, 1)); + if (message == NULL) { + goto restore_error; + } + PyErr_SetObject(PyExc_TypeError, message); + Py_DECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + Py_DECREF(message); + return; + + restore_error: + /* replacement not successful, so restore original error */ + PyErr_Restore(exc, val, tb); +} + + static PyObject * dispatcher_vectorcall(PyArray_ArrayFunctionDispatcherObject *self, PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) @@ -458,6 +523,7 @@ dispatcher_vectorcall(PyArray_ArrayFunctionDispatcherObject *self, relevant_args = PyObject_Vectorcall( self->relevant_arg_func, args, len_args, kwnames); if (relevant_args == NULL) { + fix_name_if_typeerror(self); return NULL; } Py_SETREF(relevant_args, PySequence_Fast(relevant_args, @@ -600,14 +666,31 @@ dispatcher_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwargs) } self->vectorcall = (vectorcallfunc)dispatcher_vectorcall; + Py_INCREF(self->default_impl); + self->dict = NULL; + self->dispatcher_name = NULL; + self->public_name = NULL; + if (self->relevant_arg_func == Py_None) { /* NULL in the relevant arg function means we use `like=` */ Py_CLEAR(self->relevant_arg_func); } else { + /* Fetch names to clean up TypeErrors (show actual name) */ Py_INCREF(self->relevant_arg_func); + self->dispatcher_name = PyObject_GetAttrString( + self->relevant_arg_func, "__qualname__"); + if (self->dispatcher_name == NULL) { + Py_DECREF(self); + return NULL; + } + self->public_name = PyObject_GetAttrString( + self->default_impl, "__qualname__"); + if (self->public_name == NULL) { + Py_DECREF(self); + return NULL; + } } - Py_INCREF(self->default_impl); /* Need to be like a Python function that has arbitrary attributes */ self->dict = PyDict_New(); diff --git a/numpy/core/src/multiarray/convert.c b/numpy/core/src/multiarray/convert.c index 7ef80cf28..d1f6e66af 100644 --- a/numpy/core/src/multiarray/convert.c +++ b/numpy/core/src/multiarray/convert.c @@ -393,7 +393,7 @@ PyArray_FillWithScalar(PyArrayObject *arr, PyObject *obj) char *value = (char *)value_buffer_stack; PyArray_Descr *descr = PyArray_DESCR(arr); - if (descr->elsize > sizeof(value_buffer_stack)) { + if ((size_t)descr->elsize > sizeof(value_buffer_stack)) { /* We need a large temporary buffer... */ value_buffer_heap = PyObject_Calloc(1, descr->elsize); if (value_buffer_heap == NULL) { diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c index 676a2a6b4..f42ae7c2d 100644 --- a/numpy/core/src/multiarray/item_selection.c +++ b/numpy/core/src/multiarray/item_selection.c @@ -1122,10 +1122,10 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, npy_intp elsize = (npy_intp)PyArray_ITEMSIZE(op); npy_intp astride = PyArray_STRIDE(op, axis); int swap = PyArray_ISBYTESWAPPED(op); - int needcopy = !IsAligned(op) || swap || astride != elsize; + int is_aligned = IsAligned(op); + int needcopy = !is_aligned || swap || astride != elsize; int needs_api = PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_PYAPI); - PyArray_CopySwapNFunc *copyswapn = PyArray_DESCR(op)->f->copyswapn; char *buffer = NULL; PyArrayIterObject *it; @@ -1133,6 +1133,12 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, int ret = 0; + PyArray_Descr *descr = PyArray_DESCR(op); + PyArray_Descr *odescr = NULL; + + NPY_cast_info to_cast_info = {.func = NULL}; + NPY_cast_info from_cast_info = {.func = NULL}; + NPY_BEGIN_THREADS_DEF; /* Check if there is any sorting to do */ @@ -1157,18 +1163,49 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, ret = -1; goto fail; } - if (PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_INIT)) { + if (PyDataType_FLAGCHK(descr, NPY_NEEDS_INIT)) { memset(buffer, 0, N * elsize); } + + if (swap) { + odescr = PyArray_DescrNewByteorder(descr, NPY_SWAP); + } + else { + odescr = descr; + Py_INCREF(odescr); + } + + NPY_ARRAYMETHOD_FLAGS to_transfer_flags; + + if (PyArray_GetDTypeTransferFunction( + is_aligned, astride, elsize, descr, odescr, 0, &to_cast_info, + &to_transfer_flags) != NPY_SUCCEED) { + goto fail; + } + + NPY_ARRAYMETHOD_FLAGS from_transfer_flags; + + if (PyArray_GetDTypeTransferFunction( + is_aligned, elsize, astride, odescr, descr, 0, &from_cast_info, + &from_transfer_flags) != NPY_SUCCEED) { + goto fail; + } } - NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(op)); + NPY_BEGIN_THREADS_DESCR(descr); while (size--) { char *bufptr = it->dataptr; if (needcopy) { - copyswapn(buffer, elsize, it->dataptr, astride, N, swap, op); + char *args[2] = {it->dataptr, buffer}; + npy_intp strides[2] = {astride, elsize}; + + if (NPY_UNLIKELY(to_cast_info.func( + &to_cast_info.context, args, &N, strides, + to_cast_info.auxdata) < 0)) { + goto fail; + } bufptr = buffer; } /* @@ -1204,18 +1241,26 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, } if (needcopy) { - copyswapn(it->dataptr, astride, buffer, elsize, N, swap, op); + char *args[2] = {buffer, it->dataptr}; + npy_intp strides[2] = {elsize, astride}; + + if (NPY_UNLIKELY(from_cast_info.func( + &from_cast_info.context, args, &N, strides, + from_cast_info.auxdata) < 0)) { + goto fail; + } } PyArray_ITER_NEXT(it); } fail: - NPY_END_THREADS_DESCR(PyArray_DESCR(op)); + NPY_END_THREADS_DESCR(descr); /* cleanup internal buffer */ if (needcopy) { - PyArray_ClearBuffer(PyArray_DESCR(op), buffer, elsize, N, 1); + PyArray_ClearBuffer(odescr, buffer, elsize, N, 1); PyDataMem_UserFREE(buffer, N * elsize, mem_handler); + Py_DECREF(odescr); } if (ret < 0 && !PyErr_Occurred()) { /* Out of memory during sorting or buffer creation */ @@ -1223,6 +1268,8 @@ fail: } Py_DECREF(it); Py_DECREF(mem_handler); + NPY_cast_info_xfree(&to_cast_info); + NPY_cast_info_xfree(&from_cast_info); return ret; } @@ -1236,11 +1283,11 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, npy_intp elsize = (npy_intp)PyArray_ITEMSIZE(op); npy_intp astride = PyArray_STRIDE(op, axis); int swap = PyArray_ISBYTESWAPPED(op); - int needcopy = !IsAligned(op) || swap || astride != elsize; + int is_aligned = IsAligned(op); + int needcopy = !is_aligned || swap || astride != elsize; int needs_api = PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_PYAPI); int needidxbuffer; - PyArray_CopySwapNFunc *copyswapn = PyArray_DESCR(op)->f->copyswapn; char *valbuffer = NULL; npy_intp *idxbuffer = NULL; @@ -1252,6 +1299,12 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, int ret = 0; + PyArray_Descr *descr = PyArray_DESCR(op); + PyArray_Descr *odescr = NULL; + + NPY_ARRAYMETHOD_FLAGS transfer_flags; + NPY_cast_info cast_info = {.func = NULL}; + NPY_BEGIN_THREADS_DEF; PyObject *mem_handler = PyDataMem_GetHandler(); @@ -1290,9 +1343,23 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, ret = -1; goto fail; } - if (PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_INIT)) { + if (PyDataType_FLAGCHK(descr, NPY_NEEDS_INIT)) { memset(valbuffer, 0, N * elsize); } + + if (swap) { + odescr = PyArray_DescrNewByteorder(descr, NPY_SWAP); + } + else { + odescr = descr; + Py_INCREF(odescr); + } + + if (PyArray_GetDTypeTransferFunction( + is_aligned, astride, elsize, descr, odescr, 0, &cast_info, + &transfer_flags) != NPY_SUCCEED) { + goto fail; + } } if (needidxbuffer) { @@ -1304,7 +1371,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, } } - NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(op)); + NPY_BEGIN_THREADS_DESCR(descr); while (size--) { char *valptr = it->dataptr; @@ -1312,7 +1379,14 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, npy_intp *iptr, i; if (needcopy) { - copyswapn(valbuffer, elsize, it->dataptr, astride, N, swap, op); + char *args[2] = {it->dataptr, valbuffer}; + npy_intp strides[2] = {astride, elsize}; + + if (NPY_UNLIKELY(cast_info.func( + &cast_info.context, args, &N, strides, + cast_info.auxdata) < 0)) { + goto fail; + } valptr = valbuffer; } @@ -1366,11 +1440,12 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, } fail: - NPY_END_THREADS_DESCR(PyArray_DESCR(op)); + NPY_END_THREADS_DESCR(descr); /* cleanup internal buffers */ if (needcopy) { - PyArray_ClearBuffer(PyArray_DESCR(op), valbuffer, elsize, N, 1); + PyArray_ClearBuffer(odescr, valbuffer, elsize, N, 1); PyDataMem_UserFREE(valbuffer, N * elsize, mem_handler); + Py_DECREF(odescr); } PyDataMem_UserFREE(idxbuffer, N * sizeof(npy_intp), mem_handler); if (ret < 0) { @@ -1384,6 +1459,7 @@ fail: Py_XDECREF(it); Py_XDECREF(rit); Py_DECREF(mem_handler); + NPY_cast_info_xfree(&cast_info); return (PyObject *)rop; } diff --git a/numpy/core/src/npysort/x86-simd-sort b/numpy/core/src/npysort/x86-simd-sort -Subproject 1735e86cda95a469357a19ab8984ad8530372e7 +Subproject ac6c10cd71a8aaa93996860f33dd81e15447f58 diff --git a/numpy/core/src/umath/loops_trigonometric.dispatch.c.src b/numpy/core/src/umath/loops_trigonometric.dispatch.c.src index 43eb58ffe..1b77592c8 100644 --- a/numpy/core/src/umath/loops_trigonometric.dispatch.c.src +++ b/numpy/core/src/umath/loops_trigonometric.dispatch.c.src @@ -354,7 +354,7 @@ simd_sincos_f32(const float *src, npy_intp ssrc, float *dst, npy_intp sdst, npyv_storen_till_f32(dst, sdst, len, cos); } } - if (simd_maski != ((1 << vstep) - 1)) { + if (simd_maski != (npy_uint64)((1 << vstep) - 1)) { float NPY_DECL_ALIGNED(NPY_SIMD_WIDTH) ip_fback[npyv_nlanes_f32]; npyv_storea_f32(ip_fback, x_in); diff --git a/numpy/core/tests/test_overrides.py b/numpy/core/tests/test_overrides.py index 25f551f6f..5924358ea 100644 --- a/numpy/core/tests/test_overrides.py +++ b/numpy/core/tests/test_overrides.py @@ -359,6 +359,17 @@ class TestArrayFunctionImplementation: TypeError, "no implementation found for 'my.func'"): func(MyArray()) + @pytest.mark.parametrize("name", ["concatenate", "mean", "asarray"]) + def test_signature_error_message_simple(self, name): + func = getattr(np, name) + try: + # all of these functions need an argument: + func() + except TypeError as e: + exc = e + + assert exc.args[0].startswith(f"{name}()") + def test_signature_error_message(self): # The lambda function will be named "<lambda>", but the TypeError # should show the name as "func" @@ -370,7 +381,7 @@ class TestArrayFunctionImplementation: pass try: - func(bad_arg=3) + func._implementation(bad_arg=3) except TypeError as e: expected_exception = e @@ -378,6 +389,12 @@ class TestArrayFunctionImplementation: func(bad_arg=3) raise AssertionError("must fail") except TypeError as exc: + if exc.args[0].startswith("_dispatcher"): + # We replace the qualname currently, but it used `__name__` + # (relevant functions have the same name and qualname anyway) + pytest.skip("Python version is not using __qualname__ for " + "TypeError formatting.") + assert exc.args == expected_exception.args @pytest.mark.parametrize("value", [234, "this func is not replaced"]) diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index cc81e82b7..ef0f2a5f1 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -239,7 +239,7 @@ def genfromtxt( *, ndmin: L[0, 1, 2] = ..., like: None | _SupportsArrayFunc = ..., -) -> NDArray[float64]: ... +) -> NDArray[Any]: ... @overload def genfromtxt( fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], diff --git a/numpy/typing/tests/data/reveal/npyio.pyi b/numpy/typing/tests/data/reveal/npyio.pyi index 68605cf94..2c62d8d21 100644 --- a/numpy/typing/tests/data/reveal/npyio.pyi +++ b/numpy/typing/tests/data/reveal/npyio.pyi @@ -75,13 +75,13 @@ reveal_type(np.fromregex(str_path, re.compile("test"), dtype=np.str_, encoding=" reveal_type(np.fromregex(pathlib_path, "test", np.float64)) # E: ndarray[Any, dtype[{float64}]] reveal_type(np.fromregex(bytes_reader, "test", np.float64)) # E: ndarray[Any, dtype[{float64}]] -reveal_type(np.genfromtxt(bytes_file)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.genfromtxt(bytes_file)) # E: ndarray[Any, dtype[Any]] reveal_type(np.genfromtxt(pathlib_path, dtype=np.str_)) # E: ndarray[Any, dtype[str_]] reveal_type(np.genfromtxt(str_path, dtype=str, skip_header=2)) # E: ndarray[Any, dtype[Any]] -reveal_type(np.genfromtxt(str_file, comments="test")) # E: ndarray[Any, dtype[{float64}]] -reveal_type(np.genfromtxt(str_path, delimiter="\n")) # E: ndarray[Any, dtype[{float64}]] -reveal_type(np.genfromtxt(str_path, ndmin=2)) # E: ndarray[Any, dtype[{float64}]] -reveal_type(np.genfromtxt(["1", "2", "3"], ndmin=2)) # E: ndarray[Any, dtype[{float64}]] +reveal_type(np.genfromtxt(str_file, comments="test")) # E: ndarray[Any, dtype[Any]] +reveal_type(np.genfromtxt(str_path, delimiter="\n")) # E: ndarray[Any, dtype[Any]] +reveal_type(np.genfromtxt(str_path, ndmin=2)) # E: ndarray[Any, dtype[Any]] +reveal_type(np.genfromtxt(["1", "2", "3"], ndmin=2)) # E: ndarray[Any, dtype[Any]] reveal_type(np.recfromtxt(bytes_file)) # E: recarray[Any, dtype[record]] reveal_type(np.recfromtxt(pathlib_path, usemask=True)) # E: ma.mrecords.MaskedRecords[Any, dtype[void]] |