summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2021-11-12 15:45:26 -0600
committerSebastian Berg <sebastian@sipsolutions.net>2021-11-12 15:45:26 -0600
commitf31c4a66ad27b428b6a20f9fa28c1b33854ea949 (patch)
tree00fe1a10a557393287d64176c667805ef48dc612 /numpy
parent84951a63df4dce887f31969e2a109936b368198a (diff)
downloadnumpy-f31c4a66ad27b428b6a20f9fa28c1b33854ea949.tar.gz
MAINT,BUG: Refactor __array__ and never-copy to move check later
Doing the check before calling `PyArray_FromArrayAttr` means we look up the attribute twice. It further fixes two bugs: 1. The reference counting was wrong. 2. In debug mode there was a failure for some deprecation warnings (probably due to error propagation)
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/ctors.c61
-rw-r--r--numpy/core/src/multiarray/ctors.h4
2 files changed, 50 insertions, 15 deletions
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index 7c3ac61c0..3f1d7835e 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -1349,13 +1349,7 @@ _array_from_array_like(PyObject *op,
* this should be changed!
*/
if (!writeable && tmp == Py_NotImplemented) {
- PyObject* array_meth = PyArray_LookupSpecial_OnInstance(op, "__array__");
- int has_get = array_meth && PyType_Check(op) && PyObject_HasAttrString(array_meth, "__get__");
- if (array_meth != NULL && !has_get && allow_copy) {
- PyErr_SetString(PyExc_ValueError, "Calling __array__ in never copy mode is not allowed.");
- return NULL;
- }
- tmp = PyArray_FromArrayAttr(op, requested_dtype, context);
+ tmp = PyArray_FromArrayAttr_int(op, requested_dtype, allow_copy);
if (tmp == NULL) {
return NULL;
}
@@ -2463,18 +2457,30 @@ PyArray_FromInterface(PyObject *origin)
return NULL;
}
-/*NUMPY_API
+
+/**
+ * Check for an __array__ attribute and call it when it exists.
+ *
+ * .. warning:
+ * If returned, `NotImplemented` is borrowed and must not be Decref'd
+ *
+ * @param op The Python object to convert to an array.
+ * @param descr The desired `arr.dtype`, passed into the `__array__` call,
+ * as information but is not checked/enforced!
+ * @param never_copy Indicator that a copy is not allowed.
+ * NOTE: Currently, this means an error is raised instead of calling
+ * `op.__array__()`. In the future we could call for example call
+ * `op.__array__(never_copy=True)` instead.
+ * @returns NotImplemented if `__array__` is not defined or a NumPy array
+ * (or subclass). On error, return NULL.
*/
NPY_NO_EXPORT PyObject *
-PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
+PyArray_FromArrayAttr_int(
+ PyObject *op, PyArray_Descr *descr, int never_copy)
{
PyObject *new;
PyObject *array_meth;
- if (context != NULL) {
- PyErr_SetString(PyExc_RuntimeError, "'context' must be NULL");
- return NULL;
- }
array_meth = PyArray_LookupSpecial_OnInstance(op, "__array__");
if (array_meth == NULL) {
if (PyErr_Occurred()) {
@@ -2490,6 +2496,16 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
}
return Py_NotImplemented;
}
+ if (never_copy) {
+ /* Currently, we must always assume that `__array__` returns a copy */
+ PyErr_SetString(PyExc_ValueError,
+ "Unable to avoid copy while converting from an object "
+ "implementing the `__array__` protocol. NumPy cannot ensure "
+ "that no copy will be made.");
+ Py_DECREF(array_meth);
+ return NULL;
+ }
+
if (PyType_Check(op) && PyObject_HasAttrString(array_meth, "__get__")) {
/*
* If the input is a class `array_meth` may be a property-like object.
@@ -2500,11 +2516,11 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
Py_DECREF(array_meth);
return Py_NotImplemented;
}
- if (typecode == NULL) {
+ if (descr == NULL) {
new = PyObject_CallFunction(array_meth, NULL);
}
else {
- new = PyObject_CallFunction(array_meth, "O", typecode);
+ new = PyObject_CallFunction(array_meth, "O", descr);
}
Py_DECREF(array_meth);
if (new == NULL) {
@@ -2520,6 +2536,21 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
return new;
}
+
+/*NUMPY_API
+ */
+NPY_NO_EXPORT PyObject *
+PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
+{
+ if (context != NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "'context' must be NULL");
+ return NULL;
+ }
+
+ return PyArray_FromArrayAttr_int(op, typecode, 0);
+}
+
+
/*NUMPY_API
* new reference -- accepts NULL for mintype
*/
diff --git a/numpy/core/src/multiarray/ctors.h b/numpy/core/src/multiarray/ctors.h
index cf01a6256..2f9e8547d 100644
--- a/numpy/core/src/multiarray/ctors.h
+++ b/numpy/core/src/multiarray/ctors.h
@@ -53,6 +53,10 @@ NPY_NO_EXPORT PyObject *
PyArray_FromInterface(PyObject *input);
NPY_NO_EXPORT PyObject *
+PyArray_FromArrayAttr_int(
+ PyObject *op, PyArray_Descr *descr, int never_copy);
+
+NPY_NO_EXPORT PyObject *
PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode,
PyObject *context);