-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
DEP: Deprecate setting the strides and dtype of a numpy array #28901
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
base: main
Are you sure you want to change the base?
Changes from all commits
76303cf
c27f61a
f9a2e00
11f6569
aacef24
052da80
3d36fb9
c49db94
782cf38
a306469
b0d908e
23114ff
07c776b
3050ecd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
|
@@ -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; | ||
|
@@ -124,6 +124,14 @@ array_strides_set(PyArrayObject *self, PyObject *obj, void *NPY_UNUSED(ignored)) | |
npy_intp upper_offset = 0; | ||
Py_buffer view; | ||
|
||
/* DEPRECATED 2025-05-04, NumPy 2.3 */ | ||
int ret = PyErr_WarnEx(PyExc_DeprecationWarning, | ||
"Setting the strides on a NumPy array has been deprecated in NumPy 2.3.\n", | ||
1); | ||
if (ret) { | ||
return -1; | ||
} | ||
|
||
if (obj == NULL) { | ||
PyErr_SetString(PyExc_AttributeError, | ||
"Cannot delete array strides"); | ||
|
@@ -367,8 +375,8 @@ array_nbytes_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) | |
* (contiguous or fortran) with compatible dimensions The shape and strides | ||
* will be adjusted in that case as well. | ||
*/ | ||
static int | ||
array_descr_set(PyArrayObject *self, PyObject *arg, void *NPY_UNUSED(ignored)) | ||
int | ||
array_descr_set_internal(PyArrayObject *self, PyObject *arg) | ||
{ | ||
PyArray_Descr *newtype = NULL; | ||
|
||
|
@@ -514,6 +522,28 @@ array_descr_set(PyArrayObject *self, PyObject *arg, void *NPY_UNUSED(ignored)) | |
return -1; | ||
} | ||
|
||
|
||
static int | ||
array_descr_set(PyArrayObject *self, PyObject *arg, void *NPY_UNUSED(ignored)) | ||
{ | ||
// to be replaced with PyUnstable_Object_IsUniquelyReferenced https://github.com/python/cpython/pull/133144 | ||
int unique_reference = (Py_REFCNT(self) == 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we probably do not care too much about a missed deprecation warning in some cases on python 3.14, but we do have numpy/numpy/_core/src/multiarray/temp_elide.c Line 120 in 949554c
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
if (!unique_reference) { | ||
// this will not emit deprecation warnings for all cases, but for most it will | ||
/* DEPRECATED 2025-05-04, NumPy 2.3 */ | ||
int ret = PyErr_WarnEx(PyExc_DeprecationWarning, | ||
"Setting the dtype on a NumPy array has been deprecated in NumPy 2.3.\n" | ||
"Instead of changing the dtype on an array x, create a new array with x.view(new_dtype)", | ||
1); | ||
if (ret) { | ||
return -1; | ||
} | ||
} | ||
|
||
return array_descr_set_internal(self, arg); | ||
} | ||
|
||
static PyObject * | ||
array_struct_get(PyArrayObject *self, void *NPY_UNUSED(ignored)) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -409,7 +409,24 @@ 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_dtype_set(self): | ||
x = np.eye(2) | ||
eendebakpt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def set_dtype(x): | ||
x.dtype = int | ||
self.assert_deprecated(lambda: set_dtype(x)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe simpler and clearer as |
||
|
||
def test_deprecated_strides_set(self): | ||
x = np.eye(2) | ||
|
||
def set_strides(x): | ||
eendebakpt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
s = x.strides | ||
x.strides = s | ||
|
||
self.assert_deprecated(lambda: set_strides(x)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above |
||
class TestDeprecatedDTypeParenthesizedRepeatCount(_DeprecationTestCase): | ||
message = "Passing in a parenthesized single number" | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,8 @@ | |
import types | ||
from itertools import permutations | ||
from typing import Any | ||
|
||
import pickle | ||
import warnings | ||
import hypothesis | ||
import pytest | ||
from hypothesis.extra import numpy as hynp | ||
|
@@ -1225,7 +1226,9 @@ def test_zero_stride(self): | |
arr = np.broadcast_to(arr, 10) | ||
assert arr.strides == (0,) | ||
with pytest.raises(ValueError): | ||
arr.dtype = "i1" | ||
with warnings.catch_warnings(): # gh-28901 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might as well test that the deprecation warning happens here?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some cases where we sometimes get a warning, and sometimes not (depending on optimizations perhaps? or pypy? refcounting is funny stuff). For that reason I picked I will check once more whether we can use a |
||
warnings.filterwarnings(action="ignore", category=DeprecationWarning) | ||
arr.dtype = "i1" | ||
|
||
class TestDTypeMakeCanonical: | ||
def check_canonical(self, dtype, canonical): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
|
||
# NaN value can cause an invalid FP exception if HW is being used | ||
with np.errstate(invalid='ignore'): | ||
|
@@ -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) | ||
|
||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Total nit, but either remove the spaces around |
||
assert_equal(a, b) | ||
|
||
def test_half_rounding(self): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,11 @@ | |
from numpy._core.tests._locales import CommaDecimalPointLocale | ||
from numpy.exceptions import AxisError, ComplexWarning | ||
from numpy.lib.recfunctions import repack_fields | ||
from numpy._core.multiarray import _get_ndarray_c_version, dot | ||
from numpy.lib import stride_tricks | ||
|
||
# Need to test an object that does not fully implement math interface | ||
from datetime import timedelta, datetime | ||
from numpy.testing import ( | ||
HAS_REFCOUNT, | ||
IS_64BIT, | ||
|
@@ -375,6 +380,7 @@ def make_array(size, offset, strides): | |
make_array(0, 0, 10) | ||
|
||
def test_set_stridesattr(self): | ||
# gh-28901: setting strides has been deprecated | ||
x = self.one | ||
|
||
def make_array(size, offset, strides): | ||
|
@@ -383,7 +389,11 @@ def make_array(size, offset, strides): | |
offset=offset * x.itemsize) | ||
except Exception as e: | ||
raise RuntimeError(e) | ||
r.strides = strides = strides * x.itemsize | ||
|
||
with warnings.catch_warnings(): # gh-28901 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, I think one might as well use |
||
warnings.filterwarnings(action="ignore", category=DeprecationWarning) | ||
|
||
r.strides = strides * x.itemsize | ||
return r | ||
|
||
assert_equal(make_array(4, 4, -1), np.array([4, 3, 2, 1])) | ||
|
@@ -396,7 +406,9 @@ def make_array(size, offset, strides): | |
x = np.lib.stride_tricks.as_strided(np.arange(1), (10, 10), (0, 0)) | ||
|
||
def set_strides(arr, strides): | ||
arr.strides = strides | ||
with warnings.catch_warnings(): # gh-28901 | ||
warnings.filterwarnings(action="ignore", category=DeprecationWarning) | ||
arr.strides = strides | ||
|
||
assert_raises(ValueError, set_strides, x, (10 * x.itemsize, x.itemsize)) | ||
|
||
|
@@ -405,12 +417,16 @@ def set_strides(arr, strides): | |
shape=(10,), strides=(-1,)) | ||
assert_raises(ValueError, set_strides, x[::-1], -1) | ||
a = x[::-1] | ||
a.strides = 1 | ||
a[::2].strides = 2 | ||
|
||
with warnings.catch_warnings(): # gh-28901 | ||
warnings.filterwarnings(action="ignore", category=DeprecationWarning) | ||
|
||
a.strides = 1 | ||
a[::2].strides = 2 | ||
|
||
# test 0d | ||
arr_0d = np.array(0) | ||
arr_0d.strides = () | ||
arr_0d = np.lib.stride_tricks.as_strided(arr_0d, strides = ()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove spaces around |
||
assert_raises(TypeError, set_strides, arr_0d, None) | ||
|
||
def test_fill(self): | ||
|
@@ -3631,7 +3647,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)) | ||
|
||
|
@@ -3640,7 +3656,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)) | ||
|
||
|
@@ -3653,7 +3669,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,)) | ||
|
@@ -4542,7 +4558,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)) | ||
|
@@ -8340,7 +8357,10 @@ def test_padded_struct_array(self): | |
def test_relaxed_strides(self, c=np.ones((1, 10, 10), dtype='i8')): | ||
# 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 | ||
with warnings.catch_warnings(): # gh-28901 | ||
warnings.filterwarnings(action="ignore", category=DeprecationWarning) | ||
|
||
c.strides = (-1, 80, 8) # strides need to be fixed at export | ||
|
||
assert_(memoryview(c).strides == (800, 80, 8)) | ||
|
||
|
@@ -8762,9 +8782,15 @@ class TestArrayAttributeDeletion: | |
def test_multiarray_writable_attributes_deletion(self): | ||
# ticket #2046, should not seqfault, raise AttributeError | ||
a = np.ones(2) | ||
attr = ['shape', 'strides', 'data', 'dtype', 'real', 'imag', 'flat'] | ||
attr = ['shape', 'data', 'real', 'imag', 'flat'] | ||
with suppress_warnings() as sup: | ||
sup.filter(DeprecationWarning, "Assigning the 'data' attribute") | ||
for s in attr: | ||
assert_raises(AttributeError, delattr, a, s) | ||
attr = ['strides', 'dtype'] | ||
with suppress_warnings() as sup: | ||
sup.filter(DeprecationWarning, "Assigning the 'data' attribute") | ||
sup.filter(DeprecationWarning, "Setting the ") | ||
for s in attr: | ||
assert_raises(AttributeError, delattr, a, s) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use the
DEPRECATE()
macro? As e.g. done atnumpy/numpy/_core/src/multiarray/descriptor.c
Lines 1845 to 1848 in 949554c