summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c35
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src9
-rw-r--r--numpy/core/tests/test_deprecations.py14
-rw-r--r--numpy/core/tests/test_records.py11
4 files changed, 54 insertions, 15 deletions
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 1aad70dc6..a4efdfac3 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -2003,20 +2003,41 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
int alloc = 0;
void *dptr;
PyObject *ret;
-
+ PyObject *base = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O:scalar", kwlist,
&PyArrayDescr_Type, &typecode, &obj)) {
return NULL;
}
if (PyDataType_FLAGCHK(typecode, NPY_LIST_PICKLE)) {
- if (!PySequence_Check(obj)) {
- PyErr_SetString(PyExc_TypeError,
- "found non-sequence while unpickling scalar with "
- "NPY_LIST_PICKLE set");
+ if (typecode->type_num == NPY_OBJECT) {
+ /* Deprecated 2020-11-24, NumPy 1.20 */
+ if (DEPRECATE(
+ "Unpickling a scalar with object dtype is deprecated. "
+ "Object scalars should never be created. If this was a "
+ "properly created pickle, please open a NumPy issue. In "
+ "a best effort this returns the original object.") < 0) {
+ return NULL;
+ }
+ Py_INCREF(obj);
+ return obj;
+ }
+ /* We store the full array to unpack it here: */
+ if (!PyArray_CheckExact(obj)) {
+ /* We pickle structured voids as arrays currently */
+ PyErr_SetString(PyExc_RuntimeError,
+ "Unpickling NPY_LIST_PICKLE (structured void) scalar "
+ "requires an array. The pickle file may be corrupted?");
return NULL;
}
- dptr = &obj;
+ if (!PyArray_EquivTypes(PyArray_DESCR((PyArrayObject *)obj), typecode)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Pickled array is not compatible with requested scalar "
+ "dtype. The pickle file may be corrupted?");
+ return NULL;
+ }
+ base = obj;
+ dptr = PyArray_BYTES((PyArrayObject *)obj);
}
else if (PyDataType_FLAGCHK(typecode, NPY_ITEM_IS_POINTER)) {
@@ -2066,7 +2087,7 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
dptr = PyBytes_AS_STRING(obj);
}
}
- ret = PyArray_Scalar(dptr, typecode, NULL);
+ ret = PyArray_Scalar(dptr, typecode, base);
/* free dptr which contains zeros */
if (alloc) {
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index f04bdbaa8..b8976d08f 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -1742,13 +1742,8 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args))
if (arr == NULL) {
return NULL;
}
- /* arr.item() */
- PyObject *val = PyArray_GETITEM(arr, PyArray_DATA(arr));
- Py_DECREF(arr);
- if (val == NULL) {
- return NULL;
- }
- PyObject *tup = Py_BuildValue("NN", obj, val);
+ /* Use the whole array which handles sturctured void correctly */
+ PyObject *tup = Py_BuildValue("NN", obj, arr);
if (tup == NULL) {
return NULL;
}
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py
index 380b78f67..a67fe62c3 100644
--- a/numpy/core/tests/test_deprecations.py
+++ b/numpy/core/tests/test_deprecations.py
@@ -771,3 +771,17 @@ class TestDeprecateSubarrayDTypeDuringArrayCoercion(_DeprecationTestCase):
np.array(arr, dtype="(2,2)f")
self.assert_deprecated(check)
+
+
+class TestDeprecatedUnpickleObjectScalar(_DeprecationTestCase):
+ # Deprecated 2020-11-24, NumPy 1.20
+ """
+ Technically, it should be impossible to create numpy object scalars,
+ but there was an unpickle path that would in theory allow it. That
+ path is invalid and must lead to the warning.
+ """
+ message = "Unpickling a scalar with object dtype is deprecated."
+
+ def test_deprecated(self):
+ ctor = np.core.multiarray.scalar
+ self.assert_deprecated(lambda: ctor(np.dtype("O"), 1))
diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py
index f28ad5ac9..4d4b4b515 100644
--- a/numpy/core/tests/test_records.py
+++ b/numpy/core/tests/test_records.py
@@ -424,7 +424,16 @@ class TestRecord:
# make sure we did not pickle the address
assert not isinstance(obj, bytes)
- assert_raises(TypeError, ctor, dtype, 13)
+ assert_raises(RuntimeError, ctor, dtype, 13)
+
+ # Test roundtrip:
+ dump = pickle.dumps(a[0])
+ unpickled = pickle.loads(dump)
+ assert a[0] == unpickled
+
+ # Also check the similar (impossible) "object scalar" path:
+ with pytest.warns(DeprecationWarning):
+ assert ctor(np.dtype("O"), data) is data
def test_objview_record(self):
# https://github.com/numpy/numpy/issues/2599