10000 BUG: Allow array-like types to be coerced as object array elements by seberg · Pull Request #16941 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: Allow array-like types to be coerced as object array elements #16941

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

Merged
merged 2 commits into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1844,15 +1853,25 @@ PyArray_FromInterface(PyObject *origin)
npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS];
int dataflags = NPY_ 8000 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? */
}
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");
Expand Down Expand Up @@ -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);
}
Expand Down
27 changes: 27 additions & 0 deletions numpy/core/tests/test_array_coercion.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,3 +570,30 @@ def __float__(self):
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
0