Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions doc/release/upcoming_changes/15118.change.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Remove handling of extra argument to ``__array__``
--------------------------------------------------
A code path and test have been in the code since NumPy 0.4 for a two-argument
variant of ``__array__(dtype=None, context=None)``. It was activated when
calling ``ufunc(op)`` or ``ufunc.reduce(op)`` if ``op.__array__`` existed.
However that variant is not documented, and it is not clear what the intention
was for its use. It has been removed.
21 changes: 10 additions & 11 deletions doc/source/reference/c-api/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,8 @@ From other objects
may be 0. Also, if *op* is not already an array (or does not
expose the array interface), then a new array will be created (and
filled from *op* using the sequence protocol). The new array will
have :c:data:`NPY_ARRAY_DEFAULT` as its flags member. The *context* argument
is passed to the :obj:`~numpy.class.__array__` method of *op* and is only used if
the array is constructed that way. Almost always this
parameter is ``NULL``.
have :c:data:`NPY_ARRAY_DEFAULT` as its flags member. The *context*
argument is unused.

.. c:var:: NPY_ARRAY_C_CONTIGUOUS

Expand Down Expand Up @@ -574,6 +572,8 @@ From other objects
:c:data:`NPY_ARRAY_WRITEABLE` to PyArray_FromAny, where the writeable array may
be a copy of the input.

`context` is not used.

When success (0 return value) is returned, either out_arr
is filled with a non-NULL PyArrayObject and
the rest of the parameters are untouched, or out_arr is
Expand Down Expand Up @@ -677,10 +677,8 @@ From other objects
PyObject* op, PyArray_Descr* dtype, PyObject* context)

Return an ndarray object from a Python object that exposes the
:obj:`~numpy.class.__array__` method. The :obj:`~numpy.class.__array__` method can take 0, 1, or 2
arguments ([dtype, context]) where *context* is used to pass
information about where the :obj:`~numpy.class.__array__` method is being called
from (currently only used in ufuncs).
:obj:`~numpy.class.__array__` method. The :obj:`~numpy.class.__array__`
method can take 0, or 1 argument ``([dtype])``. ``context`` is unused.

.. c:function:: PyObject* PyArray_ContiguousFromAny( \
PyObject* op, int typenum, int min_depth, int max_depth)
Expand Down Expand Up @@ -859,15 +857,16 @@ General check of Python Type
conversion occurs. Otherwise, out will contain a borrowed
reference to :c:data:`Py_NotImplemented` and no error condition is set.

.. c:function:: PyArray_HasArrayInterfaceType(op, type, context, out)
.. c:function:: PyArray_HasArrayInterfaceType(op, dtype, context, out)

If ``op`` implements any part of the array interface, then ``out``
will contain a new reference to the newly created ndarray using
the interface or ``out`` will contain ``NULL`` if an error during
conversion occurs. Otherwise, out will contain a borrowed
reference to Py_NotImplemented and no error condition is set.
This version allows setting of the type and context in the part of
the array interface that looks for the :obj:`~numpy.class.__array__` attribute.
This version allows setting of the dtype in the part of the array interface
that looks for the :obj:`~numpy.class.__array__` attribute. `context` is
unused.

.. c:function:: PyArray_IsZeroDim(op)

Expand Down
37 changes: 15 additions & 22 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,8 @@ _array_from_buffer_3118(PyObject *memoryview)
* validate and possibly copy arr itself ...
* }
* ... use arr ...
* context is passed to PyArray_FromArrayAttr, which ignores it. Since this is
* a NUMPY_API function, we cannot remove it.
*/
NPY_NO_EXPORT int
PyArray_GetArrayParamsFromObject(PyObject *op,
Expand Down Expand Up @@ -1871,6 +1873,10 @@ PyArray_GetArrayParamsFromObject(PyObject *op,
/*NUMPY_API
* Does not check for NPY_ARRAY_ENSURECOPY and NPY_ARRAY_NOTSWAPPED in flags
* Steals a reference to newtype --- which can be NULL
*
* context is passed to PyArray_GetArrayParamsFromObject, which passes it to
* PyArray_FromArrayAttr, which raises if it is not NULL. Since this is a
* NUMPY_API function, we cannot remove it.
*/
NPY_NO_EXPORT PyObject *
PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
Expand Down Expand Up @@ -2623,43 +2629,30 @@ PyArray_FromInterface(PyObject *origin)
return NULL;
}

/*NUMPY_API*/
/*NUMPY_API
*/
NPY_NO_EXPORT PyObject *
PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
{
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()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}
return Py_NotImplemented;
}
if (context == NULL) {
if (typecode == NULL) {
new = PyObject_CallFunction(array_meth, NULL);
}
else {
new = PyObject_CallFunction(array_meth, "O", typecode);
}
if (typecode == NULL) {
new = PyObject_CallFunction(array_meth, NULL);
}
else {
if (typecode == NULL) {
new = PyObject_CallFunction(array_meth, "OO", Py_None, context);
if (new == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Clear();
new = PyObject_CallFunction(array_meth, "");
}
}
else {
new = PyObject_CallFunction(array_meth, "OO", typecode, context);
if (new == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Clear();
new = PyObject_CallFunction(array_meth, "O", typecode);
}
}
new = PyObject_CallFunction(array_meth, "O", typecode);
}
Py_DECREF(array_meth);
if (new == NULL) {
Expand Down
10 changes: 5 additions & 5 deletions numpy/core/src/multiarray/datetime_busday.c
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ array_busday_offset(PyObject *NPY_UNUSED(self),

/* This steals the datetime_dtype reference */
dates = (PyArrayObject *)PyArray_FromAny(dates_in, datetime_dtype,
0, 0, 0, dates_in);
0, 0, 0, NULL);
if (dates == NULL) {
goto fail;
}
Expand All @@ -1021,7 +1021,7 @@ array_busday_offset(PyObject *NPY_UNUSED(self),
/* Make 'offsets' into an array */
offsets = (PyArrayObject *)PyArray_FromAny(offsets_in,
PyArray_DescrFromType(NPY_INT64),
0, 0, 0, offsets_in);
0, 0, 0, NULL);
if (offsets == NULL) {
goto fail;
}
Expand Down Expand Up @@ -1142,7 +1142,7 @@ array_busday_count(PyObject *NPY_UNUSED(self),
/* This steals the datetime_dtype reference */
dates_begin = (PyArrayObject *)PyArray_FromAny(dates_begin_in,
datetime_dtype,
0, 0, 0, dates_begin_in);
0, 0, 0, NULL);
if (dates_begin == NULL) {
goto fail;
}
Expand All @@ -1165,7 +1165,7 @@ array_busday_count(PyObject *NPY_UNUSED(self),
/* This steals the datetime_dtype reference */
dates_end = (PyArrayObject *)PyArray_FromAny(dates_end_in,
datetime_dtype,
0, 0, 0, dates_end_in);
0, 0, 0, NULL);
if (dates_end == NULL) {
goto fail;
}
Expand Down Expand Up @@ -1286,7 +1286,7 @@ array_is_busday(PyObject *NPY_UNUSED(self),
/* This steals the datetime_dtype reference */
dates = (PyArrayObject *)PyArray_FromAny(dates_in,
datetime_dtype,
0, 0, 0, dates_in);
0, 0, 0, NULL);
if (dates == NULL) {
goto fail;
}
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/src/multiarray/datetime_busdaycal.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ PyArray_HolidaysConverter(PyObject *dates_in, npy_holidayslist *holidays)

/* This steals the datetime_dtype reference */
dates = (PyArrayObject *)PyArray_FromAny(dates_in, datetime_dtype,
0, 0, 0, dates_in);
0, 0, 0, NULL);
if (dates == NULL) {
goto fail;
}
Expand Down
29 changes: 4 additions & 25 deletions numpy/core/src/umath/ufunc_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
int nin = ufunc->nin;
int nout = ufunc->nout;
int nop = ufunc->nargs;
PyObject *obj, *context;
PyObject *obj;
PyArray_Descr *dtype = NULL;
/*
* Initialize output objects so caller knows when outputs and optional
Expand Down Expand Up @@ -1068,22 +1068,8 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
out_op[i] = (PyArrayObject *)PyArray_FromArray(obj_a, NULL, 0);
}
else {
if (!PyArray_IsScalar(obj, Generic)) {
/*
* TODO: There should be a comment here explaining what
* context does.
*/
context = Py_BuildValue("OOi", ufunc, args, i);
if (context == NULL) {
goto fail;
}
}
else {
context = NULL;
}
out_op[i] = (PyArrayObject *)PyArray_FromAny(obj,
NULL, 0, 0, 0, context);
Py_XDECREF(context);
NULL, 0, 0, 0, NULL);
}

if (out_op[i] == NULL) {
Expand Down Expand Up @@ -4387,7 +4373,7 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
PyObject *axes_in = NULL;
PyArrayObject *mp = NULL, *wheremask = NULL, *ret = NULL;
PyObject *op;
PyObject *obj_ind, *context;
PyObject *obj_ind;
PyArrayObject *indices = NULL;
PyArray_Descr *otype = NULL;
PyArrayObject *out = NULL;
Expand Down Expand Up @@ -4478,14 +4464,7 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args,
}
}
/* Ensure input is an array */
if (!PyArray_Check(op) && !PyArray_IsScalar(op, Generic)) {
context = Py_BuildValue("O(O)i", ufunc, op, 0);
}
else {
context = NULL;
}
mp = (PyArrayObject *)PyArray_FromAny(op, NULL, 0, 0, 0, context);
Py_XDECREF(context);
mp = (PyArrayObject *)PyArray_FromAny(op, NULL, 0, 0, 0, NULL);
if (mp == NULL) {
goto fail;
}
Expand Down
26 changes: 4 additions & 22 deletions numpy/core/tests/test_umath.py
Original file line number Diff line number Diff line change
Expand Up @@ -1848,32 +1848,14 @@ def __array_prepare__(self, arr, context=None):
a = A()
assert_raises(RuntimeError, ncu.maximum, a, a)

def test_array_with_context(self):
def test_array_too_many_args(self):

class A:
def __array__(self, dtype=None, context=None):
func, args, i = context
self.func = func
self.args = args
self.i = i
return np.zeros(1)

class B:
def __array__(self, dtype=None):
return np.zeros(1, dtype)

class C:
def __array__(self):
class A(object):
def __array__(self, dtype, context):
return np.zeros(1)

a = A()
ncu.maximum(np.zeros(1), a)
assert_(a.func is ncu.maximum)
assert_equal(a.args[0], 0)
assert_(a.args[1] is a)
assert_(a.i == 1)
assert_equal(ncu.maximum(a, B()), 0)
assert_equal(ncu.maximum(a, C()), 0)
assert_raises_regex(TypeError, '2 required positional', np.sum, a)

def test_ufunc_override(self):
# check override works even with instance with high priority.
Expand Down