summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwarren <warren.weckesser@gmail.com>2021-09-17 12:11:56 -0400
committerwarren <warren.weckesser@gmail.com>2021-09-17 12:11:56 -0400
commitee885f247cf866db439117c1160a3048274ea9a0 (patch)
treef9652a50a91fe71500cbf815dbcbc5cfbfb66073
parent5d75d4862fd3409b54a29a3523e60ac1d6e374ec (diff)
downloadnumpy-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.c11
-rw-r--r--numpy/core/tests/test_numeric.py15
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):