diff options
-rw-r--r-- | doc/release/upcoming_changes/15118.change.rst | 7 | ||||
-rw-r--r-- | doc/source/reference/c-api/array.rst | 21 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 37 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime_busday.c | 10 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime_busdaycal.c | 2 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 29 | ||||
-rw-r--r-- | numpy/core/tests/test_umath.py | 26 |
7 files changed, 46 insertions, 86 deletions
diff --git a/doc/release/upcoming_changes/15118.change.rst b/doc/release/upcoming_changes/15118.change.rst new file mode 100644 index 000000000..f14beebbe --- /dev/null +++ b/doc/release/upcoming_changes/15118.change.rst @@ -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. diff --git a/doc/source/reference/c-api/array.rst b/doc/source/reference/c-api/array.rst index e396ee020..ce8671a51 100644 --- a/doc/source/reference/c-api/array.rst +++ b/doc/source/reference/c-api/array.rst @@ -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 @@ -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 @@ -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) @@ -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) diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 41dcaf3bb..bf90142e1 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -1623,6 +1623,8 @@ fail: * 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, @@ -1875,6 +1877,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, @@ -2648,13 +2654,18 @@ 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()) { @@ -2662,29 +2673,11 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context) } 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) { diff --git a/numpy/core/src/multiarray/datetime_busday.c b/numpy/core/src/multiarray/datetime_busday.c index cdeb65d0e..d3cce8a37 100644 --- a/numpy/core/src/multiarray/datetime_busday.c +++ b/numpy/core/src/multiarray/datetime_busday.c @@ -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; } @@ -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; } @@ -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; } @@ -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; } @@ -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; } diff --git a/numpy/core/src/multiarray/datetime_busdaycal.c b/numpy/core/src/multiarray/datetime_busdaycal.c index eb6ef04be..1aa5f6ab1 100644 --- a/numpy/core/src/multiarray/datetime_busdaycal.c +++ b/numpy/core/src/multiarray/datetime_busdaycal.c @@ -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; } diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index db2842542..66d52cf92 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -1034,7 +1034,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 @@ -1071,22 +1071,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) { @@ -4401,7 +4387,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; @@ -4495,14 +4481,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; } diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 0ab029988..b7baa16e1 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1848,32 +1848,14 @@ class TestSpecialMethods: 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. |