From c89f9559ae4e638f1530a31630517f616f5c1be6 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 7 Nov 2024 09:18:42 -0700 Subject: [PATCH 1/3] BUG: fix incorrect output descriptor in fancy indexing --- numpy/_core/src/multiarray/mapping.c | 2 +- numpy/_core/tests/test_stringdtype.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/numpy/_core/src/multiarray/mapping.c b/numpy/_core/src/multiarray/mapping.c index b69fa9139957..01852ae3ad5c 100644 --- a/numpy/_core/src/multiarray/mapping.c +++ b/numpy/_core/src/multiarray/mapping.c @@ -1668,7 +1668,7 @@ array_subscript(PyArrayObject *self, PyObject *op) if (PyArray_GetDTypeTransferFunction(1, itemsize, itemsize, - PyArray_DESCR(self), PyArray_DESCR(self), + PyArray_DESCR(self), PyArray_DESCR(mit->extra_op), 0, &cast_info, &transfer_flags) != NPY_SUCCEED) { goto finish; } diff --git a/numpy/_core/tests/test_stringdtype.py b/numpy/_core/tests/test_stringdtype.py index 41ae1d1515ab..e54dd131c1a1 100644 --- a/numpy/_core/tests/test_stringdtype.py +++ b/numpy/_core/tests/test_stringdtype.py @@ -496,14 +496,15 @@ def test_fancy_indexing(string_list): assert_array_equal(sarr, sarr[np.arange(sarr.shape[0])]) # see gh-27003 and gh-27053 - for ind in [[True, True], [0, 1], ...]: - for lop in [['a'*16, 'b'*16], ['', '']]: + for ind in [[True, True], [0, 1], ..., np.array([0, 1], dtype='uint8')]: + for lop in [['a'*25, 'b'*25], ['', '']]: a = np.array(lop, dtype="T") - rop = ['d'*16, 'e'*16] + assert_array_equal(a[ind], a) + rop = ['d'*25, 'e'*25] for b in [rop, np.array(rop, dtype="T")]: a[ind] = b assert_array_equal(a, b) - assert a[0] == 'd'*16 + assert a[0] == 'd'*25 def test_creation_functions(): From 72da79860760d0239a9c235b4c4014e3574da451 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Wed, 13 Nov 2024 08:20:37 -0700 Subject: [PATCH 2/3] TST: add segfaulting test --- numpy/_core/tests/test_stringdtype.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/numpy/_core/tests/test_stringdtype.py b/numpy/_core/tests/test_stringdtype.py index e54dd131c1a1..cdd0fecbe86c 100644 --- a/numpy/_core/tests/test_stringdtype.py +++ b/numpy/_core/tests/test_stringdtype.py @@ -495,9 +495,23 @@ def test_fancy_indexing(string_list): sarr = np.array(string_list, dtype="T") assert_array_equal(sarr, sarr[np.arange(sarr.shape[0])]) + inds = [ + [True, True], + [0, 1], + ..., + np.array([0, 1], dtype='uint8'), + ] + + lops = [ + ['a'*25, 'b'*25], + ['', ''], + ['hello', 'world'], + ['hello', 'world'*25], + ] + # see gh-27003 and gh-27053 - for ind in [[True, True], [0, 1], ..., np.array([0, 1], dtype='uint8')]: - for lop in [['a'*25, 'b'*25], ['', '']]: + for ind in inds: + for lop in lops: a = np.array(lop, dtype="T") assert_array_equal(a[ind], a) rop = ['d'*25, 'e'*25] From 6a855171ec32b7689ce2a54181f307cc7a6b35ad Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Wed, 13 Nov 2024 16:26:53 +0100 Subject: [PATCH 3/3] BUG: Ensure nditer always adds necessary casts (and tiny simplification) --- numpy/_core/src/multiarray/mapping.c | 10 ++++++---- numpy/_core/src/multiarray/nditer_constr.c | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/numpy/_core/src/multiarray/mapping.c b/numpy/_core/src/multiarray/mapping.c index 01852ae3ad5c..0ae63549b60b 100644 --- a/numpy/_core/src/multiarray/mapping.c +++ b/numpy/_core/src/multiarray/mapping.c @@ -2035,7 +2035,6 @@ array_assign_subscript(PyArrayObject *self, PyObject *ind, PyObject *op) goto fail; } - int allocated_array = 0; if (tmp_arr == NULL) { /* Fill extra op, need to swap first */ tmp_arr = mit->extra_op; @@ -2049,7 +2048,11 @@ array_assign_subscript(PyArrayObject *self, PyObject *ind, PyObject *op) if (PyArray_CopyObject(tmp_arr, op) < 0) { goto fail; } - allocated_array = 1; + /* + * In this branch we copy directly from a newly allocated array which + * may have a new descr: + */ + descr = PyArray_DESCR(tmp_arr); } if (PyArray_MapIterCheckIndices(mit) < 0) { @@ -2097,8 +2100,7 @@ array_assign_subscript(PyArrayObject *self, PyObject *ind, PyObject *op) // for non-REFCHK user DTypes. See gh-27057 for the prior discussion about this. if (PyArray_GetDTypeTransferFunction( 1, itemsize, itemsize, - allocated_array ? PyArray_DESCR(mit->extra_op) : PyArray_DESCR(self), - PyArray_DESCR(self), + descr, PyArray_DESCR(self), 0, &cast_info, &transfer_flags) != NPY_SUCCEED) { goto fail; } diff --git a/numpy/_core/src/multiarray/nditer_constr.c b/numpy/_core/src/multiarray/nditer_constr.c index 427dd3d876bc..ab1a540cb283 100644 --- a/numpy/_core/src/multiarray/nditer_constr.c +++ b/numpy/_core/src/multiarray/nditer_constr.c @@ -1315,8 +1315,10 @@ npyiter_check_casting(int nop, PyArrayObject **op, printf("\n"); #endif /* If the types aren't equivalent, a cast is necessary */ - if (op[iop] != NULL && !PyArray_EquivTypes(PyArray_DESCR(op[iop]), - op_dtype[iop])) { + npy_intp view_offset = NPY_MIN_INTP; + if (op[iop] != NULL && !(PyArray_SafeCast( + PyArray_DESCR(op[iop]), op_dtype[iop], &view_offset, + NPY_NO_CASTING, 1) && view_offset == 0)) { /* Check read (op -> temp) casting */ if ((op_itflags[iop] & NPY_OP_ITFLAG_READ) && !PyArray_CanCastArrayTo(op[iop],