diff options
author | warren <warren.weckesser@gmail.com> | 2021-09-17 12:11:56 -0400 |
---|---|---|
committer | warren <warren.weckesser@gmail.com> | 2021-09-17 12:11:56 -0400 |
commit | ee885f247cf866db439117c1160a3048274ea9a0 (patch) | |
tree | f9652a50a91fe71500cbf815dbcbc5cfbfb66073 | |
parent | 5d75d4862fd3409b54a29a3523e60ac1d6e374ec (diff) | |
download | numpy-ee885f247cf866db439117c1160a3048274ea9a0.tar.gz |
BUG: core: Fix *_like strides for str and bytes dtype.
When a user passed a noncontiguous array to empty_like (or any other _like
function) with dtype=str or dtype=bytes, the returned array would have
strides that were all 0, even though the data type was 'U1' or 'S1'.
The problem was in the C code in the function PyArray_NewLikeArrayWithShape.
Because the default order is "keep", the branch with the comment
/* KEEPORDER needs some analysis of the strides */ was taken. That branch
uses the itemsize (i.e. dtype->elsize) of the requested dtype, but for str
or bytes, that itemsize is 0, so the computed strides end up being 0. It
is in the subsequent call to PyArray_NewFromDescr_int that the itemsize is
updated to be nonzero.
The fix is to use the same check for str or bytes that is used in
PyArray_NewFromDescr_int in PyArray_NewLikeArrayWithShape to figure out
the itemsize when computing the strides in the KEEPORDER branch.
Closes gh-19860.
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 11 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 15 |
2 files changed, 26 insertions, 0 deletions
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 73d864a4d..04be45f00 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -1020,6 +1020,17 @@ PyArray_NewLikeArrayWithShape(PyArrayObject *prototype, NPY_ORDER order, /* Build the new strides */ stride = dtype->elsize; + if (stride == 0 && PyDataType_ISSTRING(dtype)) { + /* Special case for dtype=str or dtype=bytes. */ + if (dtype->type_num == NPY_STRING) { + /* dtype is bytes */ + stride = 1; + } + else { + /* dtype is str (type_num is NPY_UNICODE) */ + stride = 4; + } + } for (idim = ndim-1; idim >= 0; --idim) { npy_intp i_perm = strideperm[idim].perm; strides[i_perm] = stride; diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 19de0a8aa..4510333a1 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -2893,6 +2893,21 @@ class TestLikeFuncs: self.check_like_function(np.full_like, 123.456, True) self.check_like_function(np.full_like, np.inf, True) + @pytest.mark.parametrize('likefunc', [np.empty_like, np.full_like, + np.zeros_like, np.ones_like]) + @pytest.mark.parametrize('dtype', [str, bytes]) + def test_dtype_str_bytes(self, likefunc, dtype): + # Regression test for gh-19860 + a = np.arange(16).reshape(2, 8) + b = a[:, ::2] # Ensure b is not contiguous. + kwargs = {'fill_value': ''} if likefunc == np.full_like else {} + result = likefunc(b, dtype=dtype, **kwargs) + if dtype == str: + assert result.strides == (16, 4) + else: + # dtype is bytes + assert result.strides == (4, 1) + class TestCorrelate: def _setup(self, dt): |