summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2020-07-28 10:49:18 -0600
committerGitHub <noreply@github.com>2020-07-28 10:49:18 -0600
commit62ca9df412a83d2e641de12e04b776cb3dcdbe66 (patch)
treedad560c73468f4d0e03187b3661c570a1284931c
parent4690248dd48e11e57167da20e54dc21a5d853bfa (diff)
parent592040713bf2100e96f76db143f58477f5c48513 (diff)
downloadnumpy-62ca9df412a83d2e641de12e04b776cb3dcdbe66.tar.gz
Merge pull request #16941 from seberg/types-are-not-arraylikes
BUG: Allow array-like types to be coerced as object array elements
-rw-r--r--numpy/core/src/multiarray/ctors.c33
-rw-r--r--numpy/core/tests/test_array_coercion.py27
2 files changed, 58 insertions, 2 deletions
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index cb448756b..dc451685a 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -1748,6 +1748,15 @@ PyArray_FromStructInterface(PyObject *input)
}
}
if (!NpyCapsule_Check(attr)) {
+ if (PyType_Check(input) && PyObject_HasAttrString(attr, "__get__")) {
+ /*
+ * If the input is a class `attr` should be a property-like object.
+ * This cannot be interpreted as an array, but is a valid.
+ * (Needed due to the lookup being on the instance rather than type)
+ */
+ Py_DECREF(attr);
+ return Py_NotImplemented;
+ }
goto fail;
}
inter = NpyCapsule_AsVoidPtr(attr);
@@ -1844,8 +1853,8 @@ PyArray_FromInterface(PyObject *origin)
npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS];
int dataflags = NPY_ARRAY_BEHAVED;
- iface = PyArray_LookupSpecial_OnInstance(origin,
- "__array_interface__");
+ iface = PyArray_LookupSpecial_OnInstance(origin, "__array_interface__");
+
if (iface == NULL) {
if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
@@ -1853,6 +1862,16 @@ PyArray_FromInterface(PyObject *origin)
return Py_NotImplemented;
}
if (!PyDict_Check(iface)) {
+ if (PyType_Check(origin) && PyObject_HasAttrString(iface, "__get__")) {
+ /*
+ * If the input is a class `iface` should be a property-like object.
+ * This cannot be interpreted as an array, but is a valid.
+ * (Needed due to the lookup being on the instance rather than type)
+ */
+ Py_DECREF(iface);
+ return Py_NotImplemented;
+ }
+
Py_DECREF(iface);
PyErr_SetString(PyExc_ValueError,
"Invalid __array_interface__ value, must be a dict");
@@ -2119,6 +2138,16 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
}
return Py_NotImplemented;
}
+ if (PyType_Check(op) && PyObject_HasAttrString(array_meth, "__get__")) {
+ /*
+ * If the input is a class `array_meth` may be a property-like object.
+ * This cannot be interpreted as an array (called), but is a valid.
+ * Trying `array_meth.__call__()` on this should not be useful.
+ * (Needed due to the lookup being on the instance rather than type)
+ */
+ Py_DECREF(array_meth);
+ return Py_NotImplemented;
+ }
if (typecode == NULL) {
new = PyObject_CallFunction(array_meth, NULL);
}
diff --git a/numpy/core/tests/test_array_coercion.py b/numpy/core/tests/test_array_coercion.py
index 07a4c5c22..d18df2e9c 100644
--- a/numpy/core/tests/test_array_coercion.py
+++ b/numpy/core/tests/test_array_coercion.py
@@ -570,3 +570,30 @@ class TestArrayLikes:
with pytest.raises(ValueError):
# The error type does not matter much here.
np.array([obj])
+
+ def test_arraylike_classes(self):
+ # The classes of array-likes should generally be acceptable to be
+ # stored inside a numpy (object) array. This tests all of the
+ # special attributes (since all are checked during coercion).
+ arr = np.array(np.int64)
+ assert arr[()] is np.int64
+ arr = np.array([np.int64])
+ assert arr[0] is np.int64
+
+ # This also works for properties/unbound methods:
+ class ArrayLike:
+ @property
+ def __array_interface__(self):
+ pass
+
+ @property
+ def __array_struct__(self):
+ pass
+
+ def __array__(self):
+ pass
+
+ arr = np.array(ArrayLike)
+ assert arr[()] is ArrayLike
+ arr = np.array([ArrayLike])
+ assert arr[0] is ArrayLike