Skip to content

Commit bf18717

Browse files
authored
Merge pull request numpy#31177 from seberg/no-deprecation-old-pickles
DEP: Undo deprecation for ``np.dtype()`` signature used by old pickles
2 parents 8df4566 + 1edf7b6 commit bf18717

4 files changed

Lines changed: 38 additions & 38 deletions

File tree

numpy/_core/src/multiarray/descriptor.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,6 +2529,7 @@ arraydescr_new(PyTypeObject *subtype,
25292529

25302530
PyObject *odescr;
25312531
PyObject *oalign = NULL;
2532+
PyObject *ocopy = NULL;
25322533
PyObject *metadata = NULL;
25332534
PyArray_Descr *conv;
25342535
npy_bool align = NPY_FALSE;
@@ -2537,21 +2538,36 @@ arraydescr_new(PyTypeObject *subtype,
25372538

25382539
static char *kwlist[] = {"dtype", "align", "copy", "metadata", NULL};
25392540

2540-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O!:dtype", kwlist,
2541+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO!:dtype", kwlist,
25412542
&odescr,
25422543
&oalign,
2543-
PyArray_BoolConverter, &copy,
2544+
&ocopy,
25442545
&PyDict_Type, &metadata)) {
25452546
return NULL;
25462547
}
25472548

2549+
if (ocopy != NULL && !PyArray_BoolConverter(ocopy, &copy)) {
2550+
return NULL;
2551+
}
25482552
if (oalign != NULL) {
25492553
/*
25502554
* In the future, reject non Python (or NumPy) boolean, including integers to avoid any
25512555
* possibility of thinking that an integer alignment makes sense here.
2556+
* We omit the case of `oalign == 0` and `ocopy == 1` if there are exact ints.
2557+
* This can fail, in which case res is -1 and we enter the deprecation path.
25522558
*/
2553-
if (!PyBool_Check(oalign) && !PyArray_IsScalar(oalign, Bool)) {
2559+
int res = 0;
2560+
int overflow;
2561+
if (!PyBool_Check(oalign) && !PyArray_IsScalar(oalign, Bool) && !(
2562+
// Some old pickles use 0, 1 exactly, assume no user passes it
2563+
// (It may also be possible to use `copyreg` instead.)
2564+
PyLong_CheckExact(oalign) && (res = PyLong_IsZero(oalign)) == 1 &&
2565+
ocopy != NULL && PyLong_CheckExact(ocopy) &&
2566+
(res = PyLong_AsLongAndOverflow(ocopy, &overflow)) == 1)) {
25542567
/* Deprecated 2025-07-01: NumPy 2.4 */
2568+
if (res == -1 && PyErr_Occurred()) {
2569+
return NULL; // Should actually be impossible (as inputs are `long`)
2570+
}
25552571
if (PyErr_WarnFormat(npy_static_pydata.VisibleDeprecationWarning, 1,
25562572
"dtype(): align should be passed as Python or NumPy boolean but got `align=%.100R`. "
25572573
"Did you mean to pass a tuple to create a subarray type? (Deprecated NumPy 2.4)",

numpy/_core/tests/test_datetime.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -910,20 +910,18 @@ def test_pickle(self):
910910
delta)
911911

912912
# Check that loading pickles from 1.6 works
913-
with pytest.warns(np.exceptions.VisibleDeprecationWarning,
914-
match=r".*align should be passed"):
915-
pkl = b"cnumpy\ndtype\np0\n(S'M8'\np1\nI0\nI1\ntp2\nRp3\n"\
916-
b"(I4\nS'<'\np4\nNNNI-1\nI-1\nI0\n((dp5\n(S'D'\np6\n"\
917-
b"I7\nI1\nI1\ntp7\ntp8\ntp9\nb."
918-
assert_equal(pickle.loads(pkl), np.dtype('<M8[7D]'))
919-
pkl = b"cnumpy\ndtype\np0\n(S'M8'\np1\nI0\nI1\ntp2\nRp3\n"\
920-
b"(I4\nS'<'\np4\nNNNI-1\nI-1\nI0\n((dp5\n(S'W'\np6\n"\
921-
b"I1\nI1\nI1\ntp7\ntp8\ntp9\nb."
922-
assert_equal(pickle.loads(pkl), np.dtype('<M8[W]'))
923-
pkl = b"cnumpy\ndtype\np0\n(S'M8'\np1\nI0\nI1\ntp2\nRp3\n"\
924-
b"(I4\nS'>'\np4\nNNNI-1\nI-1\nI0\n((dp5\n(S'us'\np6\n"\
925-
b"I1\nI1\nI1\ntp7\ntp8\ntp9\nb."
926-
assert_equal(pickle.loads(pkl), np.dtype('>M8[us]'))
913+
pkl = b"cnumpy\ndtype\np0\n(S'M8'\np1\nI0\nI1\ntp2\nRp3\n"\
914+
b"(I4\nS'<'\np4\nNNNI-1\nI-1\nI0\n((dp5\n(S'D'\np6\n"\
915+
b"I7\nI1\nI1\ntp7\ntp8\ntp9\nb."
916+
assert_equal(pickle.loads(pkl), np.dtype('<M8[7D]'))
917+
pkl = b"cnumpy\ndtype\np0\n(S'M8'\np1\nI0\nI1\ntp2\nRp3\n"\
918+
b"(I4\nS'<'\np4\nNNNI-1\nI-1\nI0\n((dp5\n(S'W'\np6\n"\
919+
b"I1\nI1\nI1\ntp7\ntp8\ntp9\nb."
920+
assert_equal(pickle.loads(pkl), np.dtype('<M8[W]'))
921+
pkl = b"cnumpy\ndtype\np0\n(S'M8'\np1\nI0\nI1\ntp2\nRp3\n"\
922+
b"(I4\nS'>'\np4\nNNNI-1\nI-1\nI0\n((dp5\n(S'us'\np6\n"\
923+
b"I1\nI1\nI1\ntp7\ntp8\ntp9\nb."
924+
assert_equal(pickle.loads(pkl), np.dtype('>M8[us]'))
927925

928926
def test_gh_29555(self):
929927
# check that dtype metadata round-trips when none

numpy/_core/tests/test_deprecations.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,18 @@ def test_deprecated(self):
280280
# alignment, or pass them accidentally as a subarray shape (meaning to pass
281281
# a tuple).
282282
self.assert_deprecated(lambda: np.dtype("f8", align=3))
283+
self.assert_deprecated(lambda: np.dtype("f8", align=0, copy=10**100))
284+
self.assert_deprecated(lambda: np.dtype("f8", align=10**100, copy=0))
285+
# Subclasses of ints don't hit the below pickle code path:
286+
self.assert_deprecated(
287+
lambda: np.dtype("f8", align=np.long(0), copy=np.long(1)))
283288

284289
@pytest.mark.parametrize("align", [True, False, np.True_, np.False_])
285290
def test_not_deprecated(self, align):
286291
# if the user passes a bool, it is accepted.
287292
self.assert_not_deprecated(lambda: np.dtype("f8", align=align))
293+
# The following specific case is used by old pickles:
294+
self.assert_not_deprecated(lambda: np.dtype("f8", align=0, copy=1))
288295

289296

290297
class TestFlatiterIndexing0dBoolIndex(_DeprecationTestCase):

numpy/_core/tests/test_multiarray.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4729,9 +4729,6 @@ def _loads(self, obj):
47294729

47304730
# version 0 pickles, using protocol=2 to pickle
47314731
# version 0 doesn't have a version field
4732-
@pytest.mark.filterwarnings(
4733-
"ignore:.*align should be passed:numpy.exceptions.VisibleDeprecationWarning",
4734-
)
47354732
def test_version0_int8(self):
47364733
s = (
47374734
b"\x80\x02cnumpy.core._internal\n_reconstruct\nq\x01cnumpy\n"
@@ -4743,9 +4740,6 @@ def test_version0_int8(self):
47434740
p = self._loads(s)
47444741
assert_equal(a, p)
47454742

4746-
@pytest.mark.filterwarnings(
4747-
"ignore:.*align should be passed:numpy.exceptions.VisibleDeprecationWarning",
4748-
)
47494743
def test_version0_float32(self):
47504744
s = (
47514745
b"\x80\x02cnumpy.core._internal\n_reconstruct\nq\x01cnumpy\n"
@@ -4758,9 +4752,6 @@ def test_version0_float32(self):
47584752
p = self._loads(s)
47594753
assert_equal(a, p)
47604754

4761-
@pytest.mark.filterwarnings(
4762-
"ignore:.*align should be passed:numpy.exceptions.VisibleDeprecationWarning",
4763-
)
47644755
def test_version0_object(self):
47654756
s = (
47664757
b"\x80\x02cnumpy.core._internal\n_reconstruct\nq\x01cnumpy\n"
@@ -4774,9 +4765,6 @@ def test_version0_object(self):
47744765
assert_equal(a, p)
47754766

47764767
# version 1 pickles, using protocol=2 to pickle
4777-
@pytest.mark.filterwarnings(
4778-
"ignore:.*align should be passed:numpy.exceptions.VisibleDeprecationWarning",
4779-
)
47804768
def test_version1_int8(self):
47814769
s = (
47824770
b"\x80\x02cnumpy.core._internal\n_reconstruct\nq\x01cnumpy\n"
@@ -4788,9 +4776,6 @@ def test_version1_int8(self):
47884776
p = self._loads(s)
47894777
assert_equal(a, p)
47904778

4791-
@pytest.mark.filterwarnings(
4792-
"ignore:.*align should be passed:numpy.exceptions.VisibleDeprecationWarning",
4793-
)
47944779
def test_version1_float32(self):
47954780
s = (
47964781
b"\x80\x02cnumpy.core._internal\n_reconstruct\nq\x01cnumpy\n"
@@ -4803,9 +4788,6 @@ def test_version1_float32(self):
48034788
p = self._loads(s)
48044789
assert_equal(a, p)
48054790

4806-
@pytest.mark.filterwarnings(
4807-
"ignore:.*align should be passed:numpy.exceptions.VisibleDeprecationWarning",
4808-
)
48094791
def test_version1_object(self):
48104792
s = (
48114793
b"\x80\x02cnumpy.core._internal\n_reconstruct\nq\x01cnumpy\n"
@@ -4818,9 +4800,6 @@ def test_version1_object(self):
48184800
p = self._loads(s)
48194801
assert_equal(a, p)
48204802

4821-
@pytest.mark.filterwarnings(
4822-
"ignore:.*align should be passed:numpy.exceptions.VisibleDeprecationWarning",
4823-
)
48244803
def test_subarray_int_shape(self):
48254804
s = (
48264805
b"cnumpy.core.multiarray\n_reconstruct\np0\n"

0 commit comments

Comments
 (0)