summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2022-07-12 16:17:36 -0500
committerGitHub <noreply@github.com>2022-07-12 16:17:36 -0500
commitb9061328b6c5c49b3f92b4e9793bb8c3ac11b544 (patch)
treeb70c047f4c27a05ef5e6f588a58b426d0d4b9212 /numpy
parenta878789ef79abe402340c50a47429367800fe49a (diff)
parentf1f9834d84a3508aa10cc01157011b5349ec3903 (diff)
downloadnumpy-b9061328b6c5c49b3f92b4e9793bb8c3ac11b544.tar.gz
Merge pull request #21925 from seberg/subarray-object-cast
BUG: Fix subarray to object cast ownership details
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src11
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.c1
-rw-r--r--numpy/core/tests/test_dtype.py23
3 files changed, 34 insertions, 1 deletions
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 7cd80ba9a..56ac83cbb 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -804,7 +804,7 @@ VOID_getitem(void *input, void *vap)
* could have special handling.
*/
PyObject *base = (PyObject *)ap;
- while (Py_TYPE(base) == NULL) {
+ while (base != NULL && Py_TYPE(base) == NULL) {
base = PyArray_BASE((PyArrayObject *)base);
}
ret = (PyArrayObject *)PyArray_NewFromDescrAndBase(
@@ -812,6 +812,15 @@ VOID_getitem(void *input, void *vap)
shape.len, shape.ptr, NULL, ip,
PyArray_FLAGS(ap) & ~NPY_ARRAY_F_CONTIGUOUS,
NULL, base);
+ if (base == NULL) {
+ /*
+ * Need to create a copy, or we may point to wrong data. This path
+ * is taken when no "valid" array is passed. This happens for
+ * casts.
+ */
+ PyObject *copy = PyArray_FromArray(ret, NULL, NPY_ARRAY_ENSURECOPY);
+ Py_SETREF(ret, (PyArrayObject *)copy);
+ }
npy_free_cache_dim_obj(shape);
return (PyObject *)ret;
}
diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c
index f8458d2d7..c588494e7 100644
--- a/numpy/core/src/multiarray/dtype_transfer.c
+++ b/numpy/core/src/multiarray/dtype_transfer.c
@@ -258,6 +258,7 @@ any_to_object_get_loop(
data->base.free = &_any_to_object_auxdata_free;
data->base.clone = &_any_to_object_auxdata_clone;
data->arr_fields.base = NULL;
+ Py_SET_TYPE(&data->arr_fields, NULL);
data->arr_fields.descr = context->descriptors[0];
Py_INCREF(data->arr_fields.descr);
data->arr_fields.flags = aligned ? NPY_ARRAY_ALIGNED : 0;
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index f95f95893..9b471a5bf 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -650,6 +650,29 @@ class TestSubarray:
dt = np.dtype({"names": [], "formats": [], "itemsize": 0}, align=True)
assert dt == np.dtype([])
+ def test_subarray_base_item(self):
+ arr = np.ones(3, dtype=[("f", "i", 3)])
+ # Extracting the field "absorbs" the subarray into a view:
+ assert arr["f"].base is arr
+ # Extract the structured item, and then check the tuple component:
+ item = arr.item(0)
+ assert type(item) is tuple and len(item) == 1
+ assert item[0].base is arr
+
+ def test_subarray_cast_copies(self):
+ # Older versions of NumPy did NOT copy, but they got the ownership
+ # wrong (not actually knowing the correct base!). Versions since 1.21
+ # (I think) crashed fairly reliable. This defines the correct behavior
+ # as a copy. Keeping the ownership would be possible (but harder)
+ arr = np.ones(3, dtype=[("f", "i", 3)])
+ cast = arr.astype(object)
+ for fields in cast:
+ assert type(fields) == tuple and len(fields) == 1
+ subarr = fields[0]
+ assert subarr.base is None
+ assert subarr.flags.owndata
+
+
def iter_struct_object_dtypes():
"""
Iterates over a few complex dtypes and object pattern which