Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

DEP: Deprecate setting the strides attribute of a numpy array #28925

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
Loading
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions 9 doc/release/upcoming_changes/28925.deprecation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Setting the ``strides`` attribute is deprecated
-----------------------------------------------
Setting the strides attribute is now deprecated since mutating
an array is unsafe if an array is shared, especially by multiple
threads. As an alternative, you can create a new view (no copy) via:
* `np.lib.stride_tricks.strided_window_view` if applicable,
* `np.lib.stride_tricks.as_strided` for the general case,
* or the `np.ndarray` constructor (``buffer`` is the original array) for a light-weight version.
eendebakpt marked this conversation as resolved.
Show resolved Hide resolved

3 changes: 2 additions & 1 deletion 3 numpy/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ from typing import (
# library include `typing_extensions` stubs:
# https://github.com/python/typeshed/blob/main/stdlib/typing_extensions.pyi
from _typeshed import Incomplete, StrOrBytesPath, SupportsFlush, SupportsLenAndGetItem, SupportsWrite
from typing_extensions import CapsuleType, TypeVar
from typing_extensions import deprecated, CapsuleType, TypeVar

from numpy import (
char,
Expand Down Expand Up @@ -2150,6 +2150,7 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]):
def shape(self, value: _ShapeLike) -> None: ...
@property
def strides(self) -> _Shape: ...
@deprecated("Setting the strides on a NumPy array has been deprecated in NumPy 2.3")
@strides.setter
def strides(self, value: _ShapeLike) -> None: ...
def byteswap(self, inplace: builtins.bool = ...) -> Self: ...
Expand Down
22 changes: 15 additions & 7 deletions 22 numpy/_core/src/multiarray/getset.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ array_shape_set(PyArrayObject *self, PyObject *val, void* NPY_UNUSED(ignored))
/* Free old dimensions and strides */
npy_free_cache_dim_array(self);
((PyArrayObject_fields *)self)->nd = nd;
((PyArrayObject_fields *)self)->dimensions = _dimensions;
((PyArrayObject_fields *)self)->dimensions = _dimensions;
((PyArrayObject_fields *)self)->strides = _dimensions + nd;

if (nd) {
Expand All @@ -95,7 +95,7 @@ array_shape_set(PyArrayObject *self, PyObject *val, void* NPY_UNUSED(ignored))
}
else {
/* Free old dimensions and strides */
npy_free_cache_dim_array(self);
npy_free_cache_dim_array(self);
((PyArrayObject_fields *)self)->nd = 0;
((PyArrayObject_fields *)self)->dimensions = NULL;
((PyArrayObject_fields *)self)->strides = NULL;
Expand All @@ -116,6 +116,19 @@ array_strides_get(PyArrayObject *self, void *NPY_UNUSED(ignored))
static int
array_strides_set(PyArrayObject *self, PyObject *obj, void *NPY_UNUSED(ignored))
{
if (obj == NULL) {
PyErr_SetString(PyExc_AttributeError,
"Cannot delete array strides");
return -1;
}

/* Deprecated NumPy 2.4, 2025-05-11 */
if( DEPRECATE("Setting the strides on a NumPy array has been deprecated in NumPy 2.4.\n"
"As an alternative, you can create a new view using np.lib.stride_tricks.as_strided."
) < 0 ) {
return -1;
}

PyArray_Dims newstrides = {NULL, -1};
PyArrayObject *new;
npy_intp numbytes = 0;
Expand All @@ -124,11 +137,6 @@ array_strides_set(PyArrayObject *self, PyObject *obj, void *NPY_UNUSED(ignored))
npy_intp upper_offset = 0;
Py_buffer view;

if (obj == NULL) {
PyErr_SetString(PyExc_AttributeError,
"Cannot delete array strides");
return -1;
}
if (!PyArray_OptionalIntpConverter(obj, &newstrides) ||
newstrides.len == -1) {
PyErr_SetString(PyExc_TypeError, "invalid strides");
Expand Down
3 changes: 2 additions & 1 deletion 3 numpy/_core/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import numpy as np
import numpy._core.umath as ncu
from numpy.lib import stride_tricks
from numpy.testing import (
HAS_REFCOUNT,
assert_,
Expand Down Expand Up @@ -558,7 +559,7 @@ def check_copy_result(x, y, ccontig, fcontig, strides=False):

def test_contiguous_flags():
a = np.ones((4, 4, 1))[::2, :, :]
a.strides = a.strides[:2] + (-123,)
a = stride_tricks.as_strided(a, strides=a.strides[:2] + (-123,))
b = np.ones((2, 2, 1, 2, 2)).swapaxes(3, 4)

def check_contig(a, ccontig, fcontig):
Expand Down
7 changes: 7 additions & 0 deletions 7 numpy/_core/tests/test_deprecations.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,13 @@ def __array_wrap__(self, arr):
self.assert_deprecated(lambda: np.negative(test2))
assert test2.called

class TestDeprecatedArrayAttributeSetting(_DeprecationTestCase):
message = "Setting the .*on a NumPy array has been deprecated.*"

def test_deprecated_strides_set(self):
x = np.eye(2)
self.assert_deprecated(setattr, args=(x, 'strides', x.strides))


class TestDeprecatedDTypeParenthesizedRepeatCount(_DeprecationTestCase):
message = "Passing in a parenthesized single number"
Expand Down
6 changes: 3 additions & 3 deletions 6 numpy/_core/tests/test_half.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class TestHalf:
def setup_method(self):
# An array of all possible float16 values
self.all_f16 = np.arange(0x10000, dtype=uint16)
self.all_f16.dtype = float16
self.all_f16 = self.all_f16.view(float16)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are not strictly related any more, though arguably improvements regardless.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed not related. The idea was to add them so the diff for the other (more complex) PR for the dtype will be small.


# NaN value can cause an invalid FP exception if HW is being used
with np.errstate(invalid='ignore'):
Expand All @@ -32,7 +32,7 @@ def setup_method(self):
self.nonan_f16 = np.concatenate(
(np.arange(0xfc00, 0x7fff, -1, dtype=uint16),
np.arange(0x0000, 0x7c01, 1, dtype=uint16)))
self.nonan_f16.dtype = float16
self.nonan_f16 = self.nonan_f16.view(float16)
self.nonan_f32 = np.array(self.nonan_f16, dtype=float32)
self.nonan_f64 = np.array(self.nonan_f16, dtype=float64)

Expand Down Expand Up @@ -218,7 +218,7 @@ def test_half_values(self):
0x0001, 0x8001,
0x0000, 0x8000,
0x7c00, 0xfc00], dtype=uint16)
b.dtype = float16
b = b.view(dtype=float16)
assert_equal(a, b)

def test_half_rounding(self):
Expand Down
36 changes: 23 additions & 13 deletions 36 numpy/_core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from numpy._core.multiarray import _get_ndarray_c_version, dot
from numpy._core.tests._locales import CommaDecimalPointLocale
from numpy.exceptions import AxisError, ComplexWarning
from numpy.lib import stride_tricks
from numpy.lib.recfunctions import repack_fields
from numpy.testing import (
HAS_REFCOUNT,
Expand Down Expand Up @@ -381,7 +382,8 @@ def make_array(size, offset, strides):
offset=offset * x.itemsize)
except Exception as e:
raise RuntimeError(e)
r.strides = strides = strides * x.itemsize
with pytest.warns(DeprecationWarning):
r.strides = strides * x.itemsize
return r

assert_equal(make_array(4, 4, -1), np.array([4, 3, 2, 1]))
Expand All @@ -391,24 +393,28 @@ def make_array(size, offset, strides):
assert_raises(RuntimeError, make_array, 8, 3, 1)
# Check that the true extent of the array is used.
# Test relies on as_strided base not exposing a buffer.
x = np.lib.stride_tricks.as_strided(np.arange(1), (10, 10), (0, 0))
x = stride_tricks.as_strided(np.arange(1), (10, 10), (0, 0))

def set_strides(arr, strides):
arr.strides = strides
with pytest.warns(DeprecationWarning):
arr.strides = strides

assert_raises(ValueError, set_strides, x, (10 * x.itemsize, x.itemsize))

# Test for offset calculations:
x = np.lib.stride_tricks.as_strided(np.arange(10, dtype=np.int8)[-1],
x = stride_tricks.as_strided(np.arange(10, dtype=np.int8)[-1],
shape=(10,), strides=(-1,))
assert_raises(ValueError, set_strides, x[::-1], -1)
a = x[::-1]
a.strides = 1
a[::2].strides = 2
with pytest.warns(DeprecationWarning):
a.strides = 1
with pytest.warns(DeprecationWarning):
a[::2].strides = 2

# test 0d
arr_0d = np.array(0)
arr_0d.strides = ()
with pytest.warns(DeprecationWarning):
arr_0d.strides = ()
assert_raises(TypeError, set_strides, arr_0d, None)

def test_fill(self):
Expand Down Expand Up @@ -3629,7 +3635,7 @@ def test_ravel(self):
a = a.reshape(2, 1, 2, 2).swapaxes(-1, -2)
strides = list(a.strides)
strides[1] = 123
a.strides = strides
a = stride_tricks.as_strided(a, strides=strides)
assert_(a.ravel(order='K').flags.owndata)
assert_equal(a.ravel('K'), np.arange(0, 15, 2))

Expand All @@ -3638,7 +3644,7 @@ def test_ravel(self):
a = a.reshape(2, 1, 2, 2).swapaxes(-1, -2)
strides = list(a.strides)
strides[1] = 123
a.strides = strides
a = stride_tricks.as_strided(a, strides=strides)
assert_(np.may_share_memory(a.ravel(order='K'), a))
assert_equal(a.ravel(order='K'), np.arange(2**3))

Expand All @@ -3651,7 +3657,7 @@ def test_ravel(self):

# 1-element tidy strides test:
a = np.array([[1]])
a.strides = (123, 432)
a = stride_tricks.as_strided(a, strides=(123, 432))
if np.ones(1).strides == (8,):
assert_(np.may_share_memory(a.ravel('K'), a))
assert_equal(a.ravel('K').strides, (a.dtype.itemsize,))
Expand Down Expand Up @@ -4540,7 +4546,8 @@ def test_datetime64_byteorder(self):
original = np.array([['2015-02-24T00:00:00.000000000']], dtype='datetime64[ns]')

original_byte_reversed = original.copy(order='K')
original_byte_reversed.dtype = original_byte_reversed.dtype.newbyteorder('S')
new_dtype = original_byte_reversed.dtype.newbyteorder('S')
original_byte_reversed = original_byte_reversed.view(dtype=new_dtype)
original_byte_reversed.byteswap(inplace=True)

new = pickle.loads(pickle.dumps(original_byte_reversed))
Expand Down Expand Up @@ -8332,10 +8339,13 @@ def test_padded_struct_array(self):
self._check_roundtrip(x3)

@pytest.mark.valgrind_error(reason="leaks buffer info cache temporarily.")
def test_relaxed_strides(self, c=np.ones((1, 10, 10), dtype='i8')): # noqa: B008
def test_relaxed_strides(self, c=stride_tricks.as_strided( # noqa: B008
np.ones((1, 10, 10), dtype='i8'), # noqa: B008
strides=(-1, 80, 8)
)
):
# Note: c defined as parameter so that it is persistent and leak
# checks will notice gh-16934 (buffer info cache leak).
c.strides = (-1, 80, 8) # strides need to be fixed at export

assert_(memoryview(c).strides == (800, 80, 8))

Expand Down
4 changes: 2 additions & 2 deletions 4 numpy/_core/tests/test_nditer.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ def test_iter_nbo_align_contig():

# Unaligned input
a = np.zeros((6 * 4 + 1,), dtype='i1')[1:]
a.dtype = 'f4'
a = a.view('f4')
a[:] = np.arange(6, dtype='f4')
assert_(not a.flags.aligned)
# Without 'aligned', shouldn't copy
Expand Down Expand Up @@ -1803,7 +1803,7 @@ def test_iter_buffering():
arrays.append(np.arange(10, dtype='f4'))
# Unaligned array
a = np.zeros((4 * 16 + 1,), dtype='i1')[1:]
a.dtype = 'i4'
a = a.view('i4')
a[:] = np.arange(16, dtype='i4')
arrays.append(a)
# 4-D F-order array
Expand Down
23 changes: 11 additions & 12 deletions 23 numpy/_core/tests/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import numpy as np
from numpy._utils import asbytes, asunicode
from numpy.exceptions import AxisError, ComplexWarning
from numpy.lib.stride_tricks import as_strided
from numpy.testing import (
HAS_REFCOUNT,
IS_64BIT,
Expand Down Expand Up @@ -208,7 +209,7 @@ def test_mem_dot(self):
# Dummy array to detect bad memory access:
_z = np.ones(10)
_dummy = np.empty((0, 10))
z = np.lib.stride_tricks.as_strided(_z, _dummy.shape, _dummy.strides)
z = as_strided(_z, _dummy.shape, _dummy.strides)
np.dot(x, np.transpose(y), out=z)
assert_equal(_z, np.ones(10))
# Do the same for the built-in dot:
Expand Down Expand Up @@ -438,19 +439,16 @@ def test_lexsort_zerolen_custom_strides(self):
xs = np.array([], dtype='i8')
assert np.lexsort((xs,)).shape[0] == 0 # Works

xs.strides = (16,)
xs = as_strided(xs, strides=(16,))
assert np.lexsort((xs,)).shape[0] == 0 # Was: MemoryError

def test_lexsort_zerolen_custom_strides_2d(self):
xs = np.array([], dtype='i8')
xt = as_strided(xs, shape=(0, 2), strides=(16, 16))
assert np.lexsort((xt,), axis=0).shape[0] == 0

xs.shape = (0, 2)
xs.strides = (16, 16)
assert np.lexsort((xs,), axis=0).shape[0] == 0

xs.shape = (2, 0)
xs.strides = (16, 16)
assert np.lexsort((xs,), axis=0).shape[0] == 2
xt = as_strided(xs, shape=(2, 0), strides=(16, 16))
assert np.lexsort((xt,), axis=0).shape[0] == 2

def test_lexsort_invalid_axis(self):
assert_raises(AxisError, np.lexsort, (np.arange(1),), axis=2)
Expand Down Expand Up @@ -644,7 +642,7 @@ def test_reshape_order(self):
def test_reshape_zero_strides(self):
# Issue #380, test reshaping of zero strided arrays
a = np.ones(1)
a = np.lib.stride_tricks.as_strided(a, shape=(5,), strides=(0,))
a = as_strided(a, shape=(5,), strides=(0,))
assert_(a.reshape(5, 1).strides[0] == 0)

def test_reshape_zero_size(self):
Expand Down Expand Up @@ -1654,7 +1652,7 @@ def test_eq_string_and_object_array(self):

def test_nonzero_byteswap(self):
a = np.array([0x80000000, 0x00000080, 0], dtype=np.uint32)
a.dtype = np.float32
a = a.view(np.float32)
assert_equal(a.nonzero()[0], [1])
a = a.byteswap()
a = a.view(a.dtype.newbyteorder())
Expand Down Expand Up @@ -1878,7 +1876,8 @@ def test_alignment_update(self):
# Check that alignment flag is updated on stride setting
a = np.arange(10)
assert_(a.flags.aligned)
a.strides = 3
with pytest.warns(DeprecationWarning):
a.strides = 3
assert_(not a.flags.aligned)

def test_ticket_1770(self):
Expand Down
2 changes: 1 addition & 1 deletion 2 numpy/lib/_npyio_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1736,7 +1736,7 @@ def fromregex(file, regexp, dtype, encoding=None):
# re-interpret as a single-field structured array.
newdtype = np.dtype(dtype[dtype.names[0]])
output = np.array(seq, dtype=newdtype)
output.dtype = dtype
output = output.view(dtype)
else:
output = np.array(seq, dtype=dtype)

Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.