summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/arrayfunction_override.c85
-rw-r--r--numpy/core/src/multiarray/convert.c2
-rw-r--r--numpy/core/src/multiarray/item_selection.c106
m---------numpy/core/src/npysort/x86-simd-sort0
-rw-r--r--numpy/core/src/umath/loops_trigonometric.dispatch.c.src2
-rw-r--r--numpy/core/tests/test_overrides.py19
-rw-r--r--numpy/lib/npyio.pyi2
-rw-r--r--numpy/typing/tests/data/reveal/npyio.pyi10
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]]