diff --git a/benchmarks/benchmarks/bench_array_coercion.py b/benchmarks/benchmarks/bench_array_coercion.py index 17629fe7f92f..ca1f3cc83a3f 100644 --- a/benchmarks/benchmarks/bench_array_coercion.py +++ b/benchmarks/benchmarks/bench_array_coercion.py @@ -23,33 +23,32 @@ def time_array_dtype_not_kwargs(self, array_like): np.array(array_like, self.int64) def time_array_no_copy(self, array_like): - np.array(array_like, copy=False) + np.array(array_like, copy=None) def time_array_subok(self, array_like): np.array(array_like, subok=True) def time_array_all_kwargs(self, array_like): - np.array(array_like, dtype=self.int64, copy=False, order="F", + np.array(array_like, dtype=self.int64, copy=None, order="F", subok=False, ndmin=2) def time_asarray(self, array_like): np.asarray(array_like) def time_asarray_dtype(self, array_like): - np.array(array_like, dtype=self.int64) + np.asarray(array_like, dtype=self.int64) def time_asarray_dtype(self, array_like): - np.array(array_like, dtype=self.int64, order="F") + np.asarray(array_like, dtype=self.int64, order="F") def time_asanyarray(self, array_like): - np.asarray(array_like) + np.asanyarray(array_like) def time_asanyarray_dtype(self, array_like): - np.array(array_like, dtype=self.int64) + np.asanyarray(array_like, dtype=self.int64) def time_asanyarray_dtype(self, array_like): - np.array(array_like, dtype=self.int64, order="F") + np.asanyarray(array_like, dtype=self.int64, order="F") def time_ascontiguousarray(self, array_like): np.ascontiguousarray(array_like) - diff --git a/doc/release/upcoming_changes/25168.change.rst b/doc/release/upcoming_changes/25168.change.rst new file mode 100644 index 000000000000..40921b87792e --- /dev/null +++ b/doc/release/upcoming_changes/25168.change.rst @@ -0,0 +1,8 @@ +New ``copy`` keyword meaning for `numpy.array` and `numpy.asarray` +------------------------------------------------------------------ +Now `numpy.array` and `numpy.asarray` support three values for ``copy`` parameter: +* ``None`` - A copy will only be made if it is necessary. +* ``True`` - Always make a copy. +* ``False`` - Never make a copy. If a copy is required a ``ValueError`` is raised. + +The meaning of ``False`` changed as it now raises an exception if a copy is needed. diff --git a/doc/source/reference/array_api.rst b/doc/source/reference/array_api.rst index c433aace0685..bff86a38c7d9 100644 --- a/doc/source/reference/array_api.rst +++ b/doc/source/reference/array_api.rst @@ -494,9 +494,6 @@ Creation functions differences * - Feature - Type - Notes - * - ``copy`` keyword argument to ``asarray`` - - **Compatible** - - Elementwise functions differences --------------------------------- diff --git a/doc/source/reference/arrays.classes.rst b/doc/source/reference/arrays.classes.rst index 480e4572092c..04bced806587 100644 --- a/doc/source/reference/arrays.classes.rst +++ b/doc/source/reference/arrays.classes.rst @@ -305,19 +305,30 @@ NumPy provides several hooks that classes can customize: .. note:: For ufuncs, it is hoped to eventually deprecate this method in favour of :func:`__array_ufunc__`. -.. py:method:: class.__array__([dtype]) - - If defined on an object, should return an ``ndarray``. - This method is called by array-coercion functions like np.array() - if an object implementing this interface is passed to those functions. - Please refer to :ref:`Interoperability with NumPy ` - for the protocol hierarchy, of which ``__array__`` is the oldest and least - desirable. - - .. note:: If a class (ndarray subclass or not) having the :func:`__array__` - method is used as the output object of an :ref:`ufunc - `, results will *not* be written to the object - returned by :func:`__array__`. This practice will return ``TypeError``. +.. py:method:: class.__array__(dtype=None, copy=None) + + If defined on an object, should return an ``ndarray``. + This method is called by array-coercion functions like np.array() + if an object implementing this interface is passed to those functions. + The third-party implementations of ``__array__`` must take ``dtype`` and + ``copy`` keyword arguments, as ignoring them might break third-party code + or NumPy itself. + + - ``dtype`` is a data type of the returned array. + - ``copy`` is an optional boolean that indicates whether a copy should be + returned. For ``True`` a copy should always be made, for ``None`` only + if required (e.g. due to passed ``dtype`` value), and for ``False`` a copy + should never be made (if a copy is still required, an appropriate exception + should be raised). + + Please refer to :ref:`Interoperability with NumPy ` + for the protocol hierarchy, of which ``__array__`` is the oldest and least + desirable. + + .. note:: If a class (ndarray subclass or not) having the :func:`__array__` + method is used as the output object of an :ref:`ufunc + `, results will *not* be written to the object + returned by :func:`__array__`. This practice will return ``TypeError``. .. _matrix-objects: diff --git a/doc/source/reference/c-api/array.rst b/doc/source/reference/c-api/array.rst index ca4114f162c4..3ec6d5a56c80 100644 --- a/doc/source/reference/c-api/array.rst +++ b/doc/source/reference/c-api/array.rst @@ -514,8 +514,9 @@ 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, or 1 argument ``([dtype])``. ``context`` is unused. + :obj:`~numpy.class.__array__` method. The third-party implementations of + :obj:`~numpy.class.__array__` must take ``dtype`` and ``copy`` keyword + arguments. ``context`` is unused. .. c:function:: PyObject* PyArray_ContiguousFromAny( \ PyObject* op, int typenum, int min_depth, int max_depth) diff --git a/doc/source/user/basics.dispatch.rst b/doc/source/user/basics.dispatch.rst index fecdc77a9644..29b9eae06481 100644 --- a/doc/source/user/basics.dispatch.rst +++ b/doc/source/user/basics.dispatch.rst @@ -22,7 +22,7 @@ example that has rather narrow utility but illustrates the concepts involved. ... self._i = value ... def __repr__(self): ... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" -... def __array__(self, dtype=None): +... def __array__(self, dtype=None, copy=None): ... return self._i * np.eye(self._N, dtype=dtype) Our custom array can be instantiated like: @@ -84,7 +84,7 @@ For this example we will only handle the method ``__call__`` ... self._i = value ... def __repr__(self): ... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" -... def __array__(self, dtype=None): +... def __array__(self, dtype=None, copy=None): ... return self._i * np.eye(self._N, dtype=dtype) ... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): ... if method == '__call__': @@ -135,7 +135,7 @@ conveniently by inheriting from the mixin ... self._i = value ... def __repr__(self): ... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" -... def __array__(self, dtype=None): +... def __array__(self, dtype=None, copy=None): ... return self._i * np.eye(self._N, dtype=dtype) ... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): ... if method == '__call__': @@ -173,7 +173,7 @@ functions to our custom variants. ... self._i = value ... def __repr__(self): ... return f"{self.__class__.__name__}(N={self._N}, value={self._i})" -... def __array__(self, dtype=None): +... def __array__(self, dtype=None, copy=None): ... return self._i * np.eye(self._N, dtype=dtype) ... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): ... if method == '__call__': @@ -306,4 +306,3 @@ Refer to the `dask source code `_ and examples of custom array containers. See also :doc:`NEP 18`. - diff --git a/doc/source/user/basics.interoperability.rst b/doc/source/user/basics.interoperability.rst index fd787139f88c..e0faf0c052c9 100644 --- a/doc/source/user/basics.interoperability.rst +++ b/doc/source/user/basics.interoperability.rst @@ -75,8 +75,8 @@ relies on the existence of the following attributes or methods: - ``__array_interface__``: a Python dictionary containing the shape, the element type, and optionally, the data buffer address and the strides of an array-like object; -- ``__array__()``: a method returning the NumPy ndarray view of an array-like - object; +- ``__array__()``: a method returning the NumPy ndarray copy or a view of an + array-like object; The ``__array_interface__`` attribute can be inspected directly: @@ -125,6 +125,16 @@ new ndarray object. This is not optimal, as coercing arrays into ndarrays may cause performance problems or create the need for copies and loss of metadata, as the original object and any attributes/behavior it may have had, is lost. +The signature of the method should be ``__array__(self, dtype=None, copy=None)``. +If a passed ``dtype`` isn't ``None`` and different than the object's data type, +a casting should happen to a specified type. If ``copy`` is ``None``, a copy +should be made only if ``dtype`` argument enforces it. For ``copy=True``, a copy +should always be made, where ``copy=False`` should raise an exception if a copy +is needed. + +If a class implements the old signature ``__array__(self)``, for ``np.array(a)`` +a warning will be raised saying that ``dtype`` and ``copy`` arguments are missing. + To see an example of a custom array implementation including the use of ``__array__()``, see :ref:`basics.dispatch`. diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index c6067b391a2f..51103aaa991f 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1464,9 +1464,13 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): def __class_getitem__(self, item: Any) -> GenericAlias: ... @overload - def __array__(self, dtype: None = ..., /) -> ndarray[Any, _DType_co]: ... + def __array__( + self, dtype: None = ..., /, *, copy: None | bool = ... + ) -> ndarray[Any, _DType_co]: ... @overload - def __array__(self, dtype: _DType, /) -> ndarray[Any, _DType]: ... + def __array__( + self, dtype: _DType, /, *, copy: None | bool = ... + ) -> ndarray[Any, _DType]: ... def __array_ufunc__( self, @@ -3715,9 +3719,9 @@ class poly1d: __hash__: ClassVar[None] # type: ignore @overload - def __array__(self, t: None = ...) -> NDArray[Any]: ... + def __array__(self, t: None = ..., copy: None | bool = ...) -> NDArray[Any]: ... @overload - def __array__(self, t: _DType) -> ndarray[Any, _DType]: ... + def __array__(self, t: _DType, copy: None | bool = ...) -> ndarray[Any, _DType]: ... @overload def __call__(self, val: _ScalarLike_co) -> Any: ... diff --git a/numpy/_core/_add_newdocs.py b/numpy/_core/_add_newdocs.py index 8b4d8ca658f9..ddf3d0196c34 100644 --- a/numpy/_core/_add_newdocs.py +++ b/numpy/_core/_add_newdocs.py @@ -807,12 +807,14 @@ a default ``dtype`` that can represent the values (by applying promotion rules when necessary.) copy : bool, optional - If true (default), then the array data is copied. Otherwise, a copy - will only be made if ``__array__`` returns a copy, if obj is a nested - sequence, or if a copy is needed to satisfy any of the other + If ``True`` (default), then the array data is copied. If ``None``, + a copy will only be made if ``__array__`` returns a copy, if obj is + a nested sequence, or if a copy is needed to satisfy any of the other requirements (``dtype``, ``order``, etc.). Note that any copy of the data is shallow, i.e., for arrays with object dtype, the new array will point to the same objects. See Examples for `ndarray.copy`. + For ``False`` it raises a ``ValueError`` if a copy cannot be avoided. + Default: ``True``. order : {'K', 'A', 'C', 'F'}, optional Specify the memory layout of the array. If object is not an array, the newly created array will be in C order (row major) unless 'F' is @@ -828,7 +830,7 @@ 'F' F order F order ===== ========= =================================================== - When ``copy=False`` and a copy is made for other reasons, the result is + When ``copy=None`` and a copy is made for other reasons, the result is the same as if ``copy=True``, with some exceptions for 'A', see the Notes section. The default order is 'K'. subok : bool, optional @@ -915,7 +917,7 @@ add_newdoc('numpy._core.multiarray', 'asarray', """ - asarray(a, dtype=None, order=None, *, device=None, like=None) + asarray(a, dtype=None, order=None, *, device=None, copy=None, like=None) Convert the input to an array. @@ -939,6 +941,13 @@ For Array-API interoperability only, so must be ``"cpu"`` if passed. .. versionadded:: 2.0.0 + copy : bool, optional + If ``True``, then the object is copied. If ``None`` then the object is + copied only if needed, i.e. if ``__array__`` returns a copy, if obj + is a nested sequence, or if a copy is needed to satisfy any of + the other requirements (``dtype``, ``order``, etc.). + For ``False`` it raises a ``ValueError`` if a copy cannot be avoided. + Default: ``None``. ${ARRAY_FUNCTION_LIKE} .. versionadded:: 1.20.0 @@ -2934,11 +2943,15 @@ add_newdoc('numpy._core.multiarray', 'ndarray', ('__array__', """ - a.__array__([dtype], /) + a.__array__([dtype], /, *, copy=None) - Returns either a new reference to self if dtype is not given or a new array - of provided data type if dtype is different from the current dtype of the - array. + For ``dtype`` parameter it returns either a new reference to self if + ``dtype`` is not given or a new array of provided data type if ``dtype`` + is different from the current data type of the array. + For ``copy`` parameter it returns a new reference to self if + ``copy=False`` or ``copy=None`` and copying isn't enforced by ``dtype`` + parameter. The method returns a new array for ``copy=True``, regardless of + ``dtype`` parameter. """)) diff --git a/numpy/_core/_asarray.py b/numpy/_core/_asarray.py index a9abc5a88ca3..75eabb21f996 100644 --- a/numpy/_core/_asarray.py +++ b/numpy/_core/_asarray.py @@ -123,7 +123,7 @@ def require(a, dtype=None, requirements=None, *, like=None): order = 'C' requirements.remove('C') - arr = array(a, dtype=dtype, order=order, copy=False, subok=subok) + arr = array(a, dtype=dtype, order=order, copy=None, subok=subok) for prop in requirements: if not arr.flags[prop]: diff --git a/numpy/_core/defchararray.py b/numpy/_core/defchararray.py index 112c4ffc9623..96dec7543101 100644 --- a/numpy/_core/defchararray.py +++ b/numpy/_core/defchararray.py @@ -1148,7 +1148,7 @@ class adds the following functionality: copy : bool, optional If true (default), then the object is copied. Otherwise, a copy - will only be made if __array__ returns a copy, if obj is a + will only be made if ``__array__`` returns a copy, if obj is a nested sequence, or if a copy is needed to satisfy any of the other requirements (`itemsize`, unicode, `order`, etc.). diff --git a/numpy/_core/function_base.py b/numpy/_core/function_base.py index f3b8a2d54490..85118e4ff0ee 100644 --- a/numpy/_core/function_base.py +++ b/numpy/_core/function_base.py @@ -300,7 +300,7 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, """ ndmax = np.broadcast(start, stop, base).ndim start, stop, base = ( - np.array(a, copy=False, subok=True, ndmin=ndmax) + np.array(a, copy=None, subok=True, ndmin=ndmax) for a in (start, stop, base) ) y = linspace(start, stop, num=num, endpoint=endpoint, axis=axis) diff --git a/numpy/_core/multiarray.pyi b/numpy/_core/multiarray.pyi index 2618cdd9f495..74cc86e64e79 100644 --- a/numpy/_core/multiarray.pyi +++ b/numpy/_core/multiarray.pyi @@ -181,7 +181,7 @@ def array( object: _ArrayType, dtype: None = ..., *, - copy: bool | _CopyMode = ..., + copy: None | bool | _CopyMode = ..., order: _OrderKACF = ..., subok: L[True], ndmin: int = ..., @@ -192,7 +192,7 @@ def array( object: _ArrayLike[_SCT], dtype: None = ..., *, - copy: bool | _CopyMode = ..., + copy: None | bool | _CopyMode = ..., order: _OrderKACF = ..., subok: bool = ..., ndmin: int = ..., @@ -203,7 +203,7 @@ def array( object: object, dtype: None = ..., *, - copy: bool | _CopyMode = ..., + copy: None | bool | _CopyMode = ..., order: _OrderKACF = ..., subok: bool = ..., ndmin: int = ..., @@ -214,7 +214,7 @@ def array( object: Any, dtype: _DTypeLike[_SCT], *, - copy: bool | _CopyMode = ..., + copy: None | bool | _CopyMode = ..., order: _OrderKACF = ..., subok: bool = ..., ndmin: int = ..., @@ -225,7 +225,7 @@ def array( object: Any, dtype: DTypeLike, *, - copy: bool | _CopyMode = ..., + copy: None | bool | _CopyMode = ..., order: _OrderKACF = ..., subok: bool = ..., ndmin: int = ..., @@ -485,6 +485,7 @@ def asarray( order: _OrderKACF = ..., *, device: None | L["cpu"] = ..., + copy: None | bool = ..., like: None | _SupportsArrayFunc = ..., ) -> NDArray[_SCT]: ... @overload @@ -494,6 +495,7 @@ def asarray( order: _OrderKACF = ..., *, device: None | L["cpu"] = ..., + copy: None | bool = ..., like: None | _SupportsArrayFunc = ..., ) -> NDArray[Any]: ... @overload @@ -503,6 +505,7 @@ def asarray( order: _OrderKACF = ..., *, device: None | L["cpu"] = ..., + copy: None | bool = ..., like: None | _SupportsArrayFunc = ..., ) -> NDArray[_SCT]: ... @overload @@ -512,6 +515,7 @@ def asarray( order: _OrderKACF = ..., *, device: None | L["cpu"] = ..., + copy: None | bool = ..., like: None | _SupportsArrayFunc = ..., ) -> NDArray[Any]: ... diff --git a/numpy/_core/numeric.py b/numpy/_core/numeric.py index d0e05b6957b8..c8f7c616d78d 100644 --- a/numpy/_core/numeric.py +++ b/numpy/_core/numeric.py @@ -868,7 +868,7 @@ def convolve(a, v, mode='full'): array([2.5]) """ - a, v = array(a, copy=False, ndmin=1), array(v, copy=False, ndmin=1) + a, v = array(a, copy=None, ndmin=1), array(v, copy=None, ndmin=1) if (len(v) > len(a)): a, v = v, a if len(a) == 0: diff --git a/numpy/_core/shape_base.py b/numpy/_core/shape_base.py index 1efdcbc091b0..200d8e7c74d7 100644 --- a/numpy/_core/shape_base.py +++ b/numpy/_core/shape_base.py @@ -552,7 +552,7 @@ def _block_check_depths_match(arrays, parent_index=[]): def _atleast_nd(a, ndim): # Ensures `a` has at least `ndim` dimensions by prepending # ones to `a.shape` as necessary - return array(a, ndmin=ndim, copy=False, subok=True) + return array(a, ndmin=ndim, copy=None, subok=True) def _accumulate(values): diff --git a/numpy/_core/src/multiarray/conversion_utils.c b/numpy/_core/src/multiarray/conversion_utils.c index 3c5d64b35806..d58fee3823ee 100644 --- a/numpy/_core/src/multiarray/conversion_utils.c +++ b/numpy/_core/src/multiarray/conversion_utils.c @@ -227,11 +227,10 @@ PyArray_OptionalIntpConverter(PyObject *obj, PyArray_Dims *seq) } NPY_NO_EXPORT int -PyArray_CopyConverter(PyObject *obj, _PyArray_CopyMode *copymode) { +PyArray_CopyConverter(PyObject *obj, NPY_COPYMODE *copymode) { if (obj == Py_None) { - PyErr_SetString(PyExc_ValueError, - "NoneType copy mode not allowed."); - return NPY_FAIL; + *copymode = NPY_COPY_IF_NEEDED; + return NPY_SUCCEED; } int int_copymode; @@ -258,7 +257,32 @@ PyArray_CopyConverter(PyObject *obj, _PyArray_CopyMode *copymode) { int_copymode = (int)bool_copymode; } - *copymode = (_PyArray_CopyMode)int_copymode; + *copymode = (NPY_COPYMODE)int_copymode; + return NPY_SUCCEED; +} + +NPY_NO_EXPORT int +PyArray_AsTypeCopyConverter(PyObject *obj, NPY_ASTYPECOPYMODE *copymode) +{ + int int_copymode; + static PyObject* numpy_CopyMode = NULL; + npy_cache_import("numpy", "_CopyMode", &numpy_CopyMode); + + if (numpy_CopyMode != NULL && (PyObject *)Py_TYPE(obj) == numpy_CopyMode) { + PyErr_SetString(PyExc_ValueError, + "_CopyMode enum is not allowed for astype function. " + "Use true/false instead."); + return NPY_FAIL; + } + else { + npy_bool bool_copymode; + if (!PyArray_BoolConverter(obj, &bool_copymode)) { + return NPY_FAIL; + } + int_copymode = (int)bool_copymode; + } + + *copymode = (NPY_ASTYPECOPYMODE)int_copymode; return NPY_SUCCEED; } diff --git a/numpy/_core/src/multiarray/conversion_utils.h b/numpy/_core/src/multiarray/conversion_utils.h index 974e3ebe5edd..f138c3b98529 100644 --- a/numpy/_core/src/multiarray/conversion_utils.h +++ b/numpy/_core/src/multiarray/conversion_utils.h @@ -13,13 +13,21 @@ NPY_NO_EXPORT int PyArray_OptionalIntpConverter(PyObject *obj, PyArray_Dims *seq); typedef enum { - NPY_COPY_IF_NEEDED = 0, + NPY_COPY_NEVER = 0, NPY_COPY_ALWAYS = 1, - NPY_COPY_NEVER = 2, -} _PyArray_CopyMode; + NPY_COPY_IF_NEEDED = 2, +} NPY_COPYMODE; + +typedef enum { + NPY_AS_TYPE_COPY_IF_NEEDED = 0, + NPY_AS_TYPE_COPY_ALWAYS = 1, +} NPY_ASTYPECOPYMODE; + +NPY_NO_EXPORT int +PyArray_CopyConverter(PyObject *obj, NPY_COPYMODE *copyflag); NPY_NO_EXPORT int -PyArray_CopyConverter(PyObject *obj, _PyArray_CopyMode *copyflag); +PyArray_AsTypeCopyConverter(PyObject *obj, NPY_ASTYPECOPYMODE *copyflag); NPY_NO_EXPORT int PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf); diff --git a/numpy/_core/src/multiarray/ctors.c b/numpy/_core/src/multiarray/ctors.c index a5a6448d16e0..4b9c3add3f5e 100644 --- a/numpy/_core/src/multiarray/ctors.c +++ b/numpy/_core/src/multiarray/ctors.c @@ -1477,16 +1477,7 @@ _array_from_array_like(PyObject *op, } } - /* - * If op supplies the __array__ function. - * The documentation says this should produce a copy, so - * we skip this method if writeable is true, because the intent - * of writeable is to modify the operand. - * XXX: If the implementation is wrong, and/or if actual - * usage requires this behave differently, - * this should be changed! - */ - if (!writeable && tmp == Py_NotImplemented) { + if (tmp == Py_NotImplemented) { tmp = PyArray_FromArrayAttr_int(op, requested_dtype, never_copy); if (tmp == NULL) { return NULL; @@ -2418,9 +2409,8 @@ PyArray_FromInterface(PyObject *origin) * @param descr The desired `arr.dtype`, passed into the `__array__` call, * as information but is not checked/enforced! * @param never_copy Specifies 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. + * NOTE: For false it passes `op.__array__(copy=None)`, + * for true: `op.__array__(copy=False)`. * @returns NotImplemented if `__array__` is not defined or a NumPy array * (or subclass). On error, return NULL. */ @@ -2438,15 +2428,6 @@ PyArray_FromArrayAttr_int( } 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__")) { /* @@ -2458,12 +2439,33 @@ PyArray_FromArrayAttr_int( Py_DECREF(array_meth); return Py_NotImplemented; } - if (descr == NULL) { - new = PyObject_CallFunction(array_meth, NULL); - } - else { - new = PyObject_CallFunction(array_meth, "O", descr); + + PyObject *copy = never_copy ? Py_False : Py_None; + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "copy", copy); + PyObject *args = descr != NULL ? PyTuple_Pack(1, descr) : PyTuple_New(0); + + new = PyObject_Call(array_meth, args, kwargs); + + if (PyErr_Occurred()) { + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + if (PyUnicode_Check(value) && PyUnicode_CompareWithASCIIString(value, + "__array__() got an unexpected keyword argument 'copy'") == 0) { + Py_DECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + if (PyErr_WarnEx(PyExc_UserWarning, + "__array__ should implement 'dtype' and 'copy' keywords", 1) < 0) { + return NULL; + } + Py_SETREF(new, PyObject_Call(array_meth, args, NULL)); + } else { + PyErr_Restore(type, value, traceback); + return NULL; + } } + Py_DECREF(array_meth); if (new == NULL) { return NULL; diff --git a/numpy/_core/src/multiarray/iterators.c b/numpy/_core/src/multiarray/iterators.c index 3a1cbd8a5a5c..3576db795281 100644 --- a/numpy/_core/src/multiarray/iterators.c +++ b/numpy/_core/src/multiarray/iterators.c @@ -1071,7 +1071,7 @@ static PyMappingMethods iter_as_mapping = { * ignored. */ static PyArrayObject * -iter_array(PyArrayIterObject *it, PyObject *NPY_UNUSED(op)) +iter_array(PyArrayIterObject *it, PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds)) { PyArrayObject *ret; @@ -1120,7 +1120,7 @@ static PyMethodDef iter_methods[] = { /* to get array */ {"__array__", (PyCFunction)iter_array, - METH_VARARGS, NULL}, + METH_VARARGS | METH_KEYWORDS, NULL}, {"copy", (PyCFunction)iter_copy, METH_VARARGS, NULL}, @@ -1132,7 +1132,7 @@ iter_richcompare(PyArrayIterObject *self, PyObject *other, int cmp_op) { PyArrayObject *new; PyObject *ret; - new = (PyArrayObject *)iter_array(self, NULL); + new = (PyArrayObject *)iter_array(self, NULL, NULL); if (new == NULL) { return NULL; } diff --git a/numpy/_core/src/multiarray/methods.c b/numpy/_core/src/multiarray/methods.c index 92fa30927e13..bf48f29f3d7e 100644 --- a/numpy/_core/src/multiarray/methods.c +++ b/numpy/_core/src/multiarray/methods.c @@ -782,7 +782,7 @@ array_astype(PyArrayObject *self, npy_dtype_info dt_info = {NULL, NULL}; NPY_CASTING casting = NPY_UNSAFE_CASTING; NPY_ORDER order = NPY_KEEPORDER; - _PyArray_CopyMode forcecopy = 1; + NPY_ASTYPECOPYMODE forcecopy = 1; int subok = 1; NPY_PREPARE_ARGPARSER; @@ -791,7 +791,7 @@ array_astype(PyArrayObject *self, "|order", &PyArray_OrderConverter, &order, "|casting", &PyArray_CastingConverter, &casting, "|subok", &PyArray_PythonPyIntFromInt, &subok, - "|copy", &PyArray_CopyConverter, &forcecopy, + "|copy", &PyArray_AsTypeCopyConverter, &forcecopy, NULL, NULL, NULL) < 0) { Py_XDECREF(dt_info.descr); Py_XDECREF(dt_info.dtype); @@ -813,7 +813,7 @@ array_astype(PyArrayObject *self, * and it's not a subtype if subok is False, then we * can skip the copy. */ - if (forcecopy != NPY_COPY_ALWAYS && + if (forcecopy != NPY_AS_TYPE_COPY_ALWAYS && (order == NPY_KEEPORDER || (order == NPY_ANYORDER && (PyArray_IS_C_CONTIGUOUS(self) || @@ -829,13 +829,6 @@ array_astype(PyArrayObject *self, return (PyObject *)self; } - if (forcecopy == NPY_COPY_NEVER) { - PyErr_SetString(PyExc_ValueError, - "Unable to avoid copy while casting in never copy mode."); - Py_DECREF(dtype); - return NULL; - } - if (!PyArray_CanCastArrayTo(self, dtype, casting)) { PyErr_Clear(); npy_set_invalid_cast_error( @@ -931,13 +924,16 @@ array_wraparray(PyArrayObject *self, PyObject *args) static PyObject * -array_getarray(PyArrayObject *self, PyObject *args) +array_getarray(PyArrayObject *self, PyObject *args, PyObject *kwds) { PyArray_Descr *newtype = NULL; + NPY_COPYMODE copy = NPY_COPY_IF_NEEDED; + static char *kwlist[] = {"dtype", "copy", NULL}; PyObject *ret; - if (!PyArg_ParseTuple(args, "|O&:__array__", - PyArray_DescrConverter, &newtype)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&$O&:__array__", kwlist, + PyArray_DescrConverter, &newtype, + PyArray_CopyConverter, ©)) { Py_XDECREF(newtype); return NULL; } @@ -967,13 +963,27 @@ array_getarray(PyArrayObject *self, PyObject *args) Py_INCREF(self); } - if ((newtype == NULL) || PyArray_EquivTypes(PyArray_DESCR(self), newtype)) { - return (PyObject *)self; - } - else { + if (copy == NPY_COPY_ALWAYS) { + if (newtype == NULL) { + newtype = PyArray_DESCR(self); + } ret = PyArray_CastToType(self, newtype, 0); Py_DECREF(self); return ret; + } else { // copy == NPY_COPY_IF_NEEDED || copy == NPY_COPY_NEVER + if (newtype == NULL || PyArray_EquivTypes(PyArray_DESCR(self), newtype)) { + return (PyObject *)self; + } + if (copy == NPY_COPY_IF_NEEDED) { + ret = PyArray_CastToType(self, newtype, 0); + Py_DECREF(self); + return ret; + } else { // copy == NPY_COPY_NEVER + PyErr_SetString(PyExc_ValueError, + "Unable to avoid copy while creating an array from given array."); + Py_DECREF(self); + return NULL; + } } } @@ -2855,7 +2865,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = { /* for subtypes */ {"__array__", (PyCFunction)array_getarray, - METH_VARARGS, NULL}, + METH_VARARGS | METH_KEYWORDS, NULL}, {"__array_finalize__", (PyCFunction)array_finalizearray, METH_O, NULL}, diff --git a/numpy/_core/src/multiarray/multiarraymodule.c b/numpy/_core/src/multiarray/multiarraymodule.c index 46109c110e64..37a2547a62f2 100644 --- a/numpy/_core/src/multiarray/multiarraymodule.c +++ b/numpy/_core/src/multiarray/multiarraymodule.c @@ -1554,7 +1554,7 @@ _prepend_ones(PyArrayObject *arr, int nd, int ndmin, NPY_ORDER order) static inline PyObject * _array_fromobject_generic( PyObject *op, PyArray_Descr *in_descr, PyArray_DTypeMeta *in_DType, - _PyArray_CopyMode copy, NPY_ORDER order, npy_bool subok, int ndmin) + NPY_COPYMODE copy, NPY_ORDER order, npy_bool subok, int ndmin) { PyArrayObject *oparr = NULL, *ret = NULL; PyArray_Descr *oldtype = NULL; @@ -1655,7 +1655,7 @@ _array_fromobject_generic( if (copy == NPY_COPY_ALWAYS) { flags = NPY_ARRAY_ENSURECOPY; } - else if (copy == NPY_COPY_NEVER ) { + else if (copy == NPY_COPY_NEVER) { flags = NPY_ARRAY_ENSURENOCOPY; } if (order == NPY_CORDER) { @@ -1703,7 +1703,7 @@ array_array(PyObject *NPY_UNUSED(ignored), { PyObject *op; npy_bool subok = NPY_FALSE; - _PyArray_CopyMode copy = NPY_COPY_ALWAYS; + NPY_COPYMODE copy = NPY_COPY_ALWAYS; int ndmin = 0; npy_dtype_info dt_info = {NULL, NULL}; NPY_ORDER order = NPY_KEEPORDER; @@ -1751,6 +1751,7 @@ array_asarray(PyObject *NPY_UNUSED(ignored), PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *op; + NPY_COPYMODE copy = NPY_COPY_IF_NEEDED; npy_dtype_info dt_info = {NULL, NULL}; NPY_ORDER order = NPY_KEEPORDER; NPY_DEVICE device = NPY_DEVICE_CPU; @@ -1763,6 +1764,7 @@ array_asarray(PyObject *NPY_UNUSED(ignored), "|dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info, "|order", &PyArray_OrderConverter, &order, "$device", &PyArray_DeviceConverterOptional, &device, + "$copy", &PyArray_CopyConverter, ©, "$like", NULL, &like, NULL, NULL, NULL) < 0) { Py_XDECREF(dt_info.descr); @@ -1784,7 +1786,7 @@ array_asarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, dt_info.descr, dt_info.dtype, NPY_FALSE, order, NPY_FALSE, 0); + op, dt_info.descr, dt_info.dtype, copy, order, NPY_FALSE, 0); Py_XDECREF(dt_info.descr); Py_XDECREF(dt_info.dtype); return res; @@ -1826,7 +1828,7 @@ array_asanyarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, dt_info.descr, dt_info.dtype, NPY_FALSE, order, NPY_TRUE, 0); + op, dt_info.descr, dt_info.dtype, NPY_COPY_IF_NEEDED, order, NPY_TRUE, 0); Py_XDECREF(dt_info.descr); Py_XDECREF(dt_info.dtype); return res; @@ -1867,7 +1869,7 @@ array_ascontiguousarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, dt_info.descr, dt_info.dtype, NPY_FALSE, NPY_CORDER, NPY_FALSE, + op, dt_info.descr, dt_info.dtype, NPY_COPY_IF_NEEDED, NPY_CORDER, NPY_FALSE, 1); Py_XDECREF(dt_info.descr); Py_XDECREF(dt_info.dtype); @@ -1909,7 +1911,7 @@ array_asfortranarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, dt_info.descr, dt_info.dtype, NPY_FALSE, NPY_FORTRANORDER, + op, dt_info.descr, dt_info.dtype, NPY_COPY_IF_NEEDED, NPY_FORTRANORDER, NPY_FALSE, 1); Py_XDECREF(dt_info.descr); Py_XDECREF(dt_info.dtype); diff --git a/numpy/_core/tests/test_api.py b/numpy/_core/tests/test_api.py index 3269515a758f..5b9bdb60f1b3 100644 --- a/numpy/_core/tests/test_api.py +++ b/numpy/_core/tests/test_api.py @@ -87,8 +87,10 @@ def test_array_array(): assert_equal(bytes(np.array(o).data), bytes(a.data)) # test array - o = type("o", (object,), - dict(__array__=lambda *x: np.array(100.0, dtype=np.float64)))() + def custom__array__(self, dtype=None, copy=None): + return np.array(100.0, dtype=dtype, copy=copy) + + o = type("o", (object,), dict(__array__=custom__array__))() assert_equal(np.array(o, dtype=np.float64), np.array(100.0, np.float64)) # test recursion @@ -177,7 +179,7 @@ def test_array_astype(): assert_equal(a, b) assert_(not (a is b)) - # copy=False parameter can sometimes skip a copy + # copy=False parameter skips a copy b = a.astype('f4', copy=False) assert_(a is b) @@ -536,9 +538,9 @@ def check_contig(a, ccontig, fcontig): check_contig(np.empty((2, 2), order='F'), False, True) # Check that np.array creates correct contiguous flags: - check_contig(np.array(a, copy=False), False, False) - check_contig(np.array(a, copy=False, order='C'), True, False) - check_contig(np.array(a, ndmin=4, copy=False, order='F'), False, True) + check_contig(np.array(a, copy=None), False, False) + check_contig(np.array(a, copy=None, order='C'), True, False) + check_contig(np.array(a, ndmin=4, copy=None, order='F'), False, True) # Check slicing update of flags and : check_contig(a[0], True, True) @@ -570,25 +572,14 @@ def test_astype_copyflag(): arr = np.arange(10, dtype=np.intp) res_true = arr.astype(np.intp, copy=True) - assert not np.may_share_memory(arr, res_true) - res_always = arr.astype(np.intp, copy=np._CopyMode.ALWAYS) - assert not np.may_share_memory(arr, res_always) + assert not np.shares_memory(arr, res_true) res_false = arr.astype(np.intp, copy=False) - # `res_false is arr` currently, but check `may_share_memory`. - assert np.may_share_memory(arr, res_false) - res_if_needed = arr.astype(np.intp, copy=np._CopyMode.IF_NEEDED) - # `res_if_needed is arr` currently, but check `may_share_memory`. - assert np.may_share_memory(arr, res_if_needed) - - res_never = arr.astype(np.intp, copy=np._CopyMode.NEVER) - assert np.may_share_memory(arr, res_never) - - # Simple tests for when a copy is necessary: - res_false = arr.astype(np.float64, copy=False) - assert_array_equal(res_false, arr) - res_if_needed = arr.astype(np.float64, - copy=np._CopyMode.IF_NEEDED) - assert_array_equal(res_if_needed, arr) + assert np.shares_memory(arr, res_false) + + res_false_float = arr.astype(np.float64, copy=False) + assert not np.shares_memory(arr, res_false_float) + + # _CopyMode enum isn't allowed assert_raises(ValueError, arr.astype, np.float64, copy=np._CopyMode.NEVER) diff --git a/numpy/_core/tests/test_array_coercion.py b/numpy/_core/tests/test_array_coercion.py index f056ff04050f..726e8d8252a8 100644 --- a/numpy/_core/tests/test_array_coercion.py +++ b/numpy/_core/tests/test_array_coercion.py @@ -53,7 +53,7 @@ class ArrayDunder(_SequenceLike): def __init__(self, a): self.a = a - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): return self.a yield param(ArrayDunder, id="__array__") @@ -706,7 +706,7 @@ def __array_interface__(self): def __array_struct__(self): pass - def __array__(self): + def __array__(self, dtype=None, copy=None): pass arr = np.array(ArrayLike) @@ -832,7 +832,7 @@ class TestSpecialAttributeLookupFailure: class WeirdArrayLike: @property - def __array__(self): + def __array__(self, dtype=None, copy=None): raise RuntimeError("oops!") class WeirdArrayInterface: diff --git a/numpy/_core/tests/test_deprecations.py b/numpy/_core/tests/test_deprecations.py index 4542e7da8640..f0d4d533cd92 100644 --- a/numpy/_core/tests/test_deprecations.py +++ b/numpy/_core/tests/test_deprecations.py @@ -696,7 +696,7 @@ class TestDeprecatedArrayWrap(_DeprecationTestCase): def test_deprecated(self): class Test1: - def __array__(self,): + def __array__(self, dtype=None, copy=None): return np.arange(4) def __array_wrap__(self, arr, context=None): diff --git a/numpy/_core/tests/test_indexing.py b/numpy/_core/tests/test_indexing.py index 3833f394d65d..bea1c1017fb2 100644 --- a/numpy/_core/tests/test_indexing.py +++ b/numpy/_core/tests/test_indexing.py @@ -420,7 +420,7 @@ def __index__(self): class ArrayLike: # Simple array, should behave like the array - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.array(0) a = np.zeros(()) diff --git a/numpy/_core/tests/test_mem_overlap.py b/numpy/_core/tests/test_mem_overlap.py index e83590d53918..4ea70c044d51 100644 --- a/numpy/_core/tests/test_mem_overlap.py +++ b/numpy/_core/tests/test_mem_overlap.py @@ -553,7 +553,7 @@ class MyArray2: def __init__(self, data): self.data = data - def __array__(self): + def __array__(self, dtype=None, copy=None): return self.data for cls in [MyArray, MyArray2]: diff --git a/numpy/_core/tests/test_multiarray.py b/numpy/_core/tests/test_multiarray.py index 8467636ff59c..06087ac44328 100644 --- a/numpy/_core/tests/test_multiarray.py +++ b/numpy/_core/tests/test_multiarray.py @@ -477,7 +477,14 @@ def test_array_copy_false(self): e = np.array(d, copy=False) d[1] = 3 assert_array_equal(e, [1, 3, 3]) - e = np.array(d, copy=False, order='F') + np.array(d, copy=False, order='F') + + def test_array_copy_if_needed(self): + d = np.array([1, 2, 3]) + e = np.array(d, copy=None) + d[1] = 3 + assert_array_equal(e, [1, 3, 3]) + e = np.array(d, copy=None, order='F') d[1] = 4 assert_array_equal(e, [1, 4, 3]) e[2] = 7 @@ -883,7 +890,7 @@ class TestCreation: """ def test_from_attribute(self): class x: - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): pass assert_raises(ValueError, np.array, x()) @@ -6848,7 +6855,7 @@ def test_huge_vectordot(self, dtype): def test_dtype_discovery_fails(self): # See gh-14247, error checking was missing for failed dtype discovery class BadObject(object): - def __array__(self): + def __array__(self, dtype=None, copy=None): raise TypeError("just this tiny mint leaf") with pytest.raises(TypeError): @@ -8325,7 +8332,8 @@ def __bool__(self): raise ValueError true_vals = [True, np._CopyMode.ALWAYS, np.True_] - false_vals = [False, np._CopyMode.IF_NEEDED, np.False_] + if_needed_vals = [None, np._CopyMode.IF_NEEDED] + false_vals = [False, np._CopyMode.NEVER, np.False_] def test_scalars(self): # Test both numpy and python scalars @@ -8335,17 +8343,16 @@ def test_scalars(self): pyscalar = arr.item(0) # Test never-copy raises error: - assert_raises(ValueError, np.array, scalar, - copy=np._CopyMode.NEVER) - assert_raises(ValueError, np.array, pyscalar, - copy=np._CopyMode.NEVER) assert_raises(ValueError, np.array, pyscalar, copy=self.RaiseOnBool()) assert_raises(ValueError, _multiarray_tests.npy_ensurenocopy, [1]) - # Casting with a dtype (to unsigned integers) can be special: - with pytest.raises(ValueError): - np.array(pyscalar, dtype=np.int64, copy=np._CopyMode.NEVER) + for copy in self.false_vals: + assert_raises(ValueError, np.array, scalar, copy=copy) + assert_raises(ValueError, np.array, pyscalar, copy=copy) + # Casting with a dtype (to unsigned integers) can be special: + with pytest.raises(ValueError): + np.array(pyscalar, dtype=np.int64, copy=copy) def test_compatible_cast(self): @@ -8370,27 +8377,23 @@ def int_types(byteswap=False): if int1 == int2: # Casting is not necessary, base check is sufficient here - for copy in self.false_vals: + for copy in self.if_needed_vals: res = np.array(arr, copy=copy, dtype=int2) assert res is arr or res.base is arr - res = np.array(arr, - copy=np._CopyMode.NEVER, - dtype=int2) - assert res is arr or res.base is arr + for copy in self.false_vals: + res = np.array(arr, copy=copy, dtype=int2) + assert res is arr or res.base is arr else: # Casting is necessary, assert copy works: - for copy in self.false_vals: + for copy in self.if_needed_vals: res = np.array(arr, copy=copy, dtype=int2) assert res is not arr and res.flags.owndata assert_array_equal(res, arr) assert_raises(ValueError, np.array, - arr, copy=np._CopyMode.NEVER, - dtype=int2) - assert_raises(ValueError, np.array, - arr, copy=None, + arr, copy=False, dtype=int2) def test_buffer_interface(self): @@ -8429,7 +8432,7 @@ def test___array__(self): base_arr = np.arange(10) class ArrayLike: - def __array__(self): + def __array__(self, dtype=None, copy=None): # __array__ should return a copy, numpy cannot know this # however. return base_arr @@ -8444,13 +8447,23 @@ def __array__(self): # may be open for change: assert res is not base_arr - for copy in self.false_vals: - res = np.array(arr, copy=False) + for copy in self.if_needed_vals + self.false_vals: + res = np.array(arr, copy=copy) assert_array_equal(res, base_arr) assert res is base_arr # numpy trusts the ArrayLike + def test___array__copy_arg(self): + a = np.ones((10, 10), dtype=int) + + assert np.shares_memory(a, a.__array__()) + assert not np.shares_memory(a, a.__array__(float)) + assert not np.shares_memory(a, a.__array__(float, copy=None)) + assert not np.shares_memory(a, a.__array__(copy=True)) + assert np.shares_memory(a, a.__array__(copy=None)) + assert np.shares_memory(a, a.__array__(copy=False)) + assert np.shares_memory(a, a.__array__(int, copy=False)) with pytest.raises(ValueError): - np.array(arr, copy=np._CopyMode.NEVER) + np.shares_memory(a, a.__array__(float, copy=False)) @pytest.mark.parametrize( "arr", [np.ones(()), np.arange(81).reshape((9, 9))]) @@ -8490,26 +8503,18 @@ def test_order_mismatch(self, arr, order1, order2): assert_array_equal(arr, res) if no_copy_necessary: - for copy in self.false_vals: + for copy in self.if_needed_vals + self.false_vals: res = np.array(view, copy=copy, order=order2) # res.base.obj refers to the memoryview if not IS_PYPY: assert res is arr or res.base.obj is arr - - res = np.array(view, copy=np._CopyMode.NEVER, - order=order2) - if not IS_PYPY: - assert res is arr or res.base.obj is arr else: - for copy in self.false_vals: + for copy in self.if_needed_vals: res = np.array(arr, copy=copy, order=order2) assert_array_equal(arr, res) - assert_raises(ValueError, np.array, - view, copy=np._CopyMode.NEVER, - order=order2) - assert_raises(ValueError, np.array, - view, copy=None, - order=order2) + for copy in self.false_vals: + assert_raises(ValueError, np.array, + view, copy=copy, order=order2) def test_striding_not_ok(self): arr = np.array([[1, 2, 4], [3, 4, 5]]) @@ -9701,7 +9706,7 @@ def test_no_loop_gives_all_true_or_false(dt1, dt2): operator.gt]) def test_comparisons_forwards_error(op): class NotArray: - def __array__(self): + def __array__(self, dtype=None, copy=None): raise TypeError("run you fools") with pytest.raises(TypeError, match="run you fools"): diff --git a/numpy/_core/tests/test_overrides.py b/numpy/_core/tests/test_overrides.py index e03fe8bd15ab..025cd001ff0a 100644 --- a/numpy/_core/tests/test_overrides.py +++ b/numpy/_core/tests/test_overrides.py @@ -712,7 +712,7 @@ def test_function_like(): assert type(np.mean) is np._core._multiarray_umath._ArrayFunctionDispatcher class MyClass: - def __array__(self): + def __array__(self, dtype=None, copy=None): # valid argument to mean: return np.arange(3) diff --git a/numpy/_core/tests/test_protocols.py b/numpy/_core/tests/test_protocols.py index 55a2bcf72fad..7cab1223bfe1 100644 --- a/numpy/_core/tests/test_protocols.py +++ b/numpy/_core/tests/test_protocols.py @@ -34,7 +34,8 @@ def __repr__(self): def test_array_called(): class Wrapper: val = '0' * 100 - def __array__(self, result=None): + + def __array__(self, result=None, copy=None): return np.array([self.val], dtype=object) diff --git a/numpy/_core/tests/test_regression.py b/numpy/_core/tests/test_regression.py index 0f7574d11732..749fbd77011a 100644 --- a/numpy/_core/tests/test_regression.py +++ b/numpy/_core/tests/test_regression.py @@ -2434,7 +2434,7 @@ class T: def test_2d__array__shape(self): class T: - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.ndarray(shape=(0,0)) # Make sure __array__ is used instead of Sequence methods. diff --git a/numpy/_core/tests/test_scalarmath.py b/numpy/_core/tests/test_scalarmath.py index f1292c4bd156..057f84d17633 100644 --- a/numpy/_core/tests/test_scalarmath.py +++ b/numpy/_core/tests/test_scalarmath.py @@ -703,7 +703,8 @@ def test_no_seq_repeat_basic_array_like(self): class ArrayLike: def __init__(self, arr): self.arr = arr - def __array__(self): + + def __array__(self, dtype=None, copy=None): return self.arr # Test for simple ArrayLike above and memoryviews (original report) diff --git a/numpy/_core/tests/test_umath.py b/numpy/_core/tests/test_umath.py index 35919b24772d..e01e6dd6346b 100644 --- a/numpy/_core/tests/test_umath.py +++ b/numpy/_core/tests/test_umath.py @@ -3013,7 +3013,7 @@ class TestSpecialMethods: def test_wrap(self): class with_wrap: - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.zeros(1) def __array_wrap__(self, arr, context, return_scalar): @@ -3122,7 +3122,7 @@ def __new__(cls): def test_priority(self): class A: - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.zeros(1) def __array_wrap__(self, arr, context, return_scalar): @@ -3165,7 +3165,7 @@ class C(A): def test_failing_wrap(self): class A: - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.zeros(2) def __array_wrap__(self, arr, context, return_scalar): @@ -3197,7 +3197,7 @@ def test_none_wrap(self): # Tests that issue #8507 is resolved. Previously, this would segfault class A: - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.zeros(1) def __array_wrap__(self, arr, context=None, return_scalar=False): @@ -3211,7 +3211,7 @@ def test_default_prepare(self): class with_wrap: __array_priority__ = 10 - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.zeros(1) def __array_wrap__(self, arr, context, return_scalar): @@ -3225,7 +3225,7 @@ def __array_wrap__(self, arr, context, return_scalar): def test_array_too_many_args(self): class A: - def __array__(self, dtype, context): + def __array__(self, dtype, context, copy=None): return np.zeros(1) a = A() diff --git a/numpy/_globals.py b/numpy/_globals.py index 416a20f5e11b..a1474177fef8 100644 --- a/numpy/_globals.py +++ b/numpy/_globals.py @@ -81,15 +81,15 @@ class _CopyMode(enum.Enum): """ ALWAYS = True - IF_NEEDED = False - NEVER = 2 + NEVER = False + IF_NEEDED = 2 def __bool__(self): # For backwards compatibility if self == _CopyMode.ALWAYS: return True - if self == _CopyMode.IF_NEEDED: + if self == _CopyMode.NEVER: return False raise ValueError(f"{self} is neither True nor False.") diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py index dfbcf3ea28d3..2f674d020eaa 100644 --- a/numpy/array_api/_array_object.py +++ b/numpy/array_api/_array_object.py @@ -122,14 +122,15 @@ def __repr__(self: Array, /) -> str: # This function is not required by the spec, but we implement it here for # convenience so that np.asarray(np.array_api.Array) will work. - def __array__(self, dtype: None | np.dtype[Any] = None) -> npt.NDArray[Any]: + def __array__(self, dtype: None | np.dtype[Any] = None, + copy: None | float = None) -> npt.NDArray[Any]: """ Warning: this method is NOT part of the array API spec. Implementers of other libraries need not include it, and users should not assume it will be present in other implementations. """ - return np.asarray(self._array, dtype=dtype) + return np.asarray(self._array, dtype=dtype, copy=copy) # These are various helper functions to make the array behavior match the # spec in places where it either deviates from or is more strict than diff --git a/numpy/lib/_arrayterator_impl.py b/numpy/lib/_arrayterator_impl.py index 14f45d79e838..8b21a6086638 100644 --- a/numpy/lib/_arrayterator_impl.py +++ b/numpy/lib/_arrayterator_impl.py @@ -125,7 +125,7 @@ def __getitem__(self, index): out.stop[i] = min(stop, out.stop[i]) return out - def __array__(self): + def __array__(self, dtype=None, copy=None): """ Return corresponding data. diff --git a/numpy/lib/_arrayterator_impl.pyi b/numpy/lib/_arrayterator_impl.pyi index 037c50d70997..fb9c42dd2bbe 100644 --- a/numpy/lib/_arrayterator_impl.pyi +++ b/numpy/lib/_arrayterator_impl.pyi @@ -41,8 +41,8 @@ class Arrayterator(ndarray[_Shape, _DType]): self, var: ndarray[_Shape, _DType], buf_size: None | int = ... ) -> None: ... @overload - def __array__(self, dtype: None = ...) -> ndarray[Any, _DType]: ... + def __array__(self, dtype: None = ..., copy: None | bool = ...) -> ndarray[Any, _DType]: ... @overload - def __array__(self, dtype: DTypeLike) -> NDArray[Any]: ... + def __array__(self, dtype: DTypeLike, copy: None | bool = ...) -> NDArray[Any]: ... def __getitem__(self, index: _Index) -> Arrayterator[Any, _DType]: ... def __iter__(self) -> Generator[ndarray[Any, _DType], None, None]: ... diff --git a/numpy/lib/_function_base_impl.py b/numpy/lib/_function_base_impl.py index 7759d0851ce0..65bc7c592b29 100644 --- a/numpy/lib/_function_base_impl.py +++ b/numpy/lib/_function_base_impl.py @@ -2696,7 +2696,7 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, if X.shape[0] == 0: return np.array([]).reshape(0, 0) if y is not None: - y = array(y, copy=False, ndmin=2, dtype=dtype) + y = array(y, copy=None, ndmin=2, dtype=dtype) if not rowvar and y.shape[0] != 1: y = y.T X = np.concatenate((X, y), axis=0) @@ -5648,7 +5648,7 @@ def insert(arr, obj, values, axis=None): # There are some object array corner cases here, but we cannot avoid # that: - values = array(values, copy=False, ndmin=arr.ndim, dtype=arr.dtype) + values = array(values, copy=None, ndmin=arr.ndim, dtype=arr.dtype) if indices.ndim == 0: # broadcasting is very different here, since a[:,0,:] = ... behaves # very different from a[:,[0],:] = ...! This changes values so that diff --git a/numpy/lib/_index_tricks_impl.py b/numpy/lib/_index_tricks_impl.py index 2ea961879e65..62f1d213b29f 100644 --- a/numpy/lib/_index_tricks_impl.py +++ b/numpy/lib/_index_tricks_impl.py @@ -372,7 +372,7 @@ def __getitem__(self, key): else: newobj = _nx.arange(start, stop, step) if ndmin > 1: - newobj = array(newobj, copy=False, ndmin=ndmin) + newobj = array(newobj, copy=None, ndmin=ndmin) if trans1d != -1: newobj = newobj.swapaxes(-1, trans1d) elif isinstance(item, str): @@ -404,7 +404,7 @@ def __getitem__(self, key): newobj = item else: item_ndim = np.ndim(item) - newobj = array(item, copy=False, subok=True, ndmin=ndmin) + newobj = array(item, copy=None, subok=True, ndmin=ndmin) if trans1d != -1 and item_ndim < ndmin: k2 = ndmin - item_ndim k1 = trans1d @@ -425,7 +425,7 @@ def __getitem__(self, key): if len(result_type_objs) != 0: final_dtype = _nx.result_type(*result_type_objs) # concatenate could do cast, but that can be overridden: - objs = [array(obj, copy=False, subok=True, + objs = [array(obj, copy=None, subok=True, ndmin=ndmin, dtype=final_dtype) for obj in objs] res = self.concatenate(tuple(objs), axis=axis) diff --git a/numpy/lib/_polynomial_impl.py b/numpy/lib/_polynomial_impl.py index 9e0aae839690..63c12f438240 100644 --- a/numpy/lib/_polynomial_impl.py +++ b/numpy/lib/_polynomial_impl.py @@ -1240,11 +1240,11 @@ def __init__(self, c_or_r, r=False, variable=None): variable = 'x' self._variable = variable - def __array__(self, t=None): + def __array__(self, t=None, copy=None): if t: - return NX.asarray(self.coeffs, t) + return NX.asarray(self.coeffs, t, copy=copy) else: - return NX.asarray(self.coeffs) + return NX.asarray(self.coeffs, copy=copy) def __repr__(self): vals = repr(self.coeffs) diff --git a/numpy/lib/_shape_base_impl.py b/numpy/lib/_shape_base_impl.py index e9c8ee465955..68453095db7e 100644 --- a/numpy/lib/_shape_base_impl.py +++ b/numpy/lib/_shape_base_impl.py @@ -657,7 +657,7 @@ def column_stack(tup): for v in tup: arr = asanyarray(v) if arr.ndim < 2: - arr = array(arr, copy=False, subok=True, ndmin=2).T + arr = array(arr, copy=None, subok=True, ndmin=2).T arrays.append(arr) return _nx.concatenate(arrays, 1) @@ -1155,7 +1155,7 @@ def kron(a, b): # 5. Reshape the result to kron's shape, which is same as # product of shapes of the two arrays. b = asanyarray(b) - a = array(a, copy=False, subok=True, ndmin=b.ndim) + a = array(a, copy=None, subok=True, ndmin=b.ndim) is_any_mat = isinstance(a, matrix) or isinstance(b, matrix) ndb, nda = b.ndim, a.ndim nd = max(ndb, nda) @@ -1273,7 +1273,7 @@ def tile(A, reps): else: # Note that no copy of zero-sized arrays is made. However since they # have no data there is no risk of an inadvertent overwrite. - c = _nx.array(A, copy=False, subok=True, ndmin=d) + c = _nx.array(A, copy=None, subok=True, ndmin=d) if (d < c.ndim): tup = (1,)*(c.ndim-d) + tup shape_out = tuple(s*t for s, t in zip(c.shape, tup)) diff --git a/numpy/lib/_stride_tricks_impl.py b/numpy/lib/_stride_tricks_impl.py index 53bb5ea51fb5..0cfbbcfe9c81 100644 --- a/numpy/lib/_stride_tricks_impl.py +++ b/numpy/lib/_stride_tricks_impl.py @@ -101,7 +101,7 @@ def as_strided(x, shape=None, strides=None, subok=False, writeable=True): possible. """ # first convert input to array, possibly keeping subclass - x = np.array(x, copy=False, subok=subok) + x = np.array(x, copy=None, subok=subok) interface = dict(x.__array_interface__) if shape is not None: interface['shape'] = tuple(shape) @@ -312,7 +312,7 @@ def sliding_window_view(x, window_shape, axis=None, *, if np.iterable(window_shape) else (window_shape,)) # first convert input to array, possibly keeping subclass - x = np.array(x, copy=False, subok=subok) + x = np.array(x, copy=None, subok=subok) window_shape_array = np.array(window_shape) if np.any(window_shape_array < 0): @@ -348,7 +348,7 @@ def sliding_window_view(x, window_shape, axis=None, *, def _broadcast_to(array, shape, subok, readonly): shape = tuple(shape) if np.iterable(shape) else (shape,) - array = np.array(array, copy=False, subok=subok) + array = np.array(array, copy=None, subok=subok) if not shape and array.shape: raise ValueError('cannot broadcast a non-scalar to a scalar array') if any(size < 0 for size in shape): @@ -546,7 +546,7 @@ def broadcast_arrays(*args, subok=False): # return np.nditer(args, flags=['multi_index', 'zerosize_ok'], # order='C').itviews - args = tuple(np.array(_m, copy=False, subok=subok) for _m in args) + args = tuple(np.array(_m, copy=None, subok=subok) for _m in args) shape = _broadcast_shape(*args) diff --git a/numpy/lib/recfunctions.py b/numpy/lib/recfunctions.py index 346c0ac37808..bc5c5de095a8 100644 --- a/numpy/lib/recfunctions.py +++ b/numpy/lib/recfunctions.py @@ -692,7 +692,7 @@ def append_fields(base, names, data, dtypes=None, data = [data, ] # if dtypes is None: - data = [np.array(a, copy=False, subok=True) for a in data] + data = [np.array(a, copy=None, subok=True) for a in data] data = [a.view([(name, a.dtype)]) for (name, a) in zip(names, data)] else: if not isinstance(dtypes, (tuple, list)): @@ -703,7 +703,7 @@ def append_fields(base, names, data, dtypes=None, else: msg = "The dtypes argument must be None, a dtype, or a list." raise ValueError(msg) - data = [np.array(a, copy=False, subok=True, dtype=d).view([(n, d)]) + data = [np.array(a, copy=None, subok=True, dtype=d).view([(n, d)]) for (a, n, d) in zip(data, names, dtypes)] # base = merge_arrays(base, usemask=usemask, fill_value=fill_value) diff --git a/numpy/lib/tests/test_arraysetops.py b/numpy/lib/tests/test_arraysetops.py index dfe1013020de..f537621482c0 100644 --- a/numpy/lib/tests/test_arraysetops.py +++ b/numpy/lib/tests/test_arraysetops.py @@ -35,7 +35,7 @@ def test_intersect1d(self): def test_intersect1d_array_like(self): # See gh-11772 class Test: - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.arange(3) a = Test() diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 927c8fd281e2..8316b481e827 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -468,7 +468,7 @@ def _check_fill_value(fill_value, ndtype): elif ndtype.names is not None: if isinstance(fill_value, (ndarray, np.void)): try: - fill_value = np.array(fill_value, copy=False, dtype=ndtype) + fill_value = np.asarray(fill_value, dtype=ndtype) except ValueError as e: err_msg = "Unable to transform %s to dtype %s" raise ValueError(err_msg % (fill_value, ndtype)) from e @@ -485,7 +485,7 @@ def _check_fill_value(fill_value, ndtype): # In case we want to convert 1e20 to int. # Also in case of converting string arrays. try: - fill_value = np.array(fill_value, copy=False, dtype=ndtype) + fill_value = np.asarray(fill_value, dtype=ndtype) except (OverflowError, ValueError) as e: # Raise TypeError instead of OverflowError or ValueError. # OverflowError is seldom used, and the real problem here is @@ -735,7 +735,7 @@ def getdata(a, subok=True): try: data = a._data except AttributeError: - data = np.array(a, copy=False, subok=subok) + data = np.array(a, copy=None, subok=subok) if not subok: return data.view(ndarray) return data @@ -1656,6 +1656,7 @@ def make_mask(m, copy=False, shrink=True, dtype=MaskType): return np.ones(m.shape, dtype=dtype) # Fill the mask in case there are missing data; turn it into an ndarray. + copy = None if not copy else True result = np.array(filled(m, True), copy=copy, dtype=dtype, subok=True) # Bas les masques ! if shrink: @@ -2378,7 +2379,7 @@ def masked_invalid(a, copy=True): fill_value=1e+20) """ - a = np.array(a, copy=False, subok=True) + a = np.array(a, copy=None, subok=True) res = masked_where(~(np.isfinite(a)), a, copy=copy) # masked_invalid previously never returned nomask as a mask and doing so # threw off matplotlib (gh-22842). So use shrink=False: @@ -2839,6 +2840,7 @@ def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, """ # Process data. + copy = None if not copy else True _data = np.array(data, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin) _baseclass = getattr(data, '_baseclass', type(_data)) @@ -3497,7 +3499,7 @@ def __setmask__(self, mask, copy=False): else: # Named fields w/ mdtype = current_mask.dtype - mask = np.array(mask, copy=False) + mask = np.asarray(mask) # Mask is a singleton if not mask.ndim: # It's a boolean : make a record @@ -3511,6 +3513,7 @@ def __setmask__(self, mask, copy=False): else: # Make sure the new mask is a ndarray with the proper dtype try: + copy = None if not copy else True mask = np.array(mask, copy=copy, dtype=mdtype) # Or assume it's a sequence of bool/int except TypeError: @@ -4875,8 +4878,8 @@ def put(self, indices, values, mode='raise'): # Hard mask: Get rid of the values/indices that fall on masked data if self._hardmask and self._mask is not nomask: mask = self._mask[indices] - indices = narray(indices, copy=False) - values = narray(values, copy=False, subok=True) + indices = narray(indices, copy=None) + values = narray(values, copy=None, subok=True) values.resize(indices.shape) indices = indices[~mask] values = values[~mask] @@ -5116,7 +5119,7 @@ def nonzero(self): (array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2])) """ - return narray(self.filled(0), copy=False).nonzero() + return np.asarray(self.filled(0)).nonzero() def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): """ @@ -6465,6 +6468,7 @@ class mvoid(MaskedArray): def __new__(self, data, mask=nomask, dtype=None, fill_value=None, hardmask=False, copy=False, subok=True): + copy = None if not copy else True _data = np.array(data, copy=copy, subok=subok, dtype=dtype) _data = _data.view(self) _data._hardmask = hardmask @@ -6866,7 +6870,7 @@ def __call__(self, a, b): def reduce(self, target, axis=np._NoValue): "Reduce target along the given axis." - target = narray(target, copy=False, subok=True) + target = narray(target, copy=None, subok=True) m = getmask(target) if axis is np._NoValue and target.ndim > 1: @@ -7390,7 +7394,7 @@ def put(a, indices, values, mode='raise'): try: return a.put(indices, values, mode=mode) except AttributeError: - return narray(a, copy=False).put(indices, values, mode=mode) + return np.asarray(a).put(indices, values, mode=mode) def putmask(a, mask, values): # , mode='raise'): @@ -7484,7 +7488,7 @@ def transpose(a, axes=None): try: return a.transpose(axes) except AttributeError: - return narray(a, copy=False).transpose(axes).view(MaskedArray) + return np.asarray(a).transpose(axes).view(MaskedArray) def reshape(a, new_shape, order='C'): @@ -7502,7 +7506,7 @@ def reshape(a, new_shape, order='C'): try: return a.reshape(new_shape, order=order) except AttributeError: - _tmp = narray(a, copy=False).reshape(new_shape, order=order) + _tmp = np.asarray(a).reshape(new_shape, order=order) return _tmp.view(MaskedArray) diff --git a/numpy/ma/mrecords.py b/numpy/ma/mrecords.py index 1a96f4a09ad0..4eb92b6bd7b0 100644 --- a/numpy/ma/mrecords.py +++ b/numpy/ma/mrecords.py @@ -318,8 +318,8 @@ def __getitem__(self, indx): return obj # We want some elements. # First, the data. - obj = np.array(_data[indx], copy=False).view(mrecarray) - obj._mask = np.array(_mask[indx], copy=False).view(recarray) + obj = np.asarray(_data[indx]).view(mrecarray) + obj._mask = np.asarray(_mask[indx]).view(recarray) return obj def __setitem__(self, indx, value): @@ -595,7 +595,7 @@ def fromrecords(reclist, dtype=None, shape=None, formats=None, names=None, mrec.fill_value = fill_value # Now, let's deal w/ the mask if mask is not nomask: - mask = np.array(mask, copy=False) + mask = np.asarray(mask) maskrecordlength = len(mask.dtype) if maskrecordlength: mrec._mask.flat = mask diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index b0ebc65c3931..99e869685e60 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -4627,7 +4627,7 @@ def test_masked_invalid_pandas(self): class Series(): _data = "nonsense" - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.array([5, np.nan, np.inf]) arr = np.ma.masked_invalid(Series()) @@ -5580,7 +5580,7 @@ def test_astype_mask_ordering(): assert x_a2.mask.dtype.names == np.dtype(descr).names assert_equal(x, x_a2) - assert_(x is np.array(x, dtype=descr, copy=False, subok=True)) + assert_(x is np.array(x, dtype=descr, copy=None, subok=True)) x_f2 = np.array(x, dtype=x.dtype, order='F', subok=True) assert_(x_f2.flags.f_contiguous) diff --git a/numpy/ma/tests/test_subclassing.py b/numpy/ma/tests/test_subclassing.py index 716e94fdef3a..a627245ffbb3 100644 --- a/numpy/ma/tests/test_subclassing.py +++ b/numpy/ma/tests/test_subclassing.py @@ -164,7 +164,7 @@ def __init__(self, array, **attrs): def __repr__(self): return f"{self.__class__.__name__}(\n{self._array}\n{self.attrs}\n)" - def __array__(self): + def __array__(self, dtype=None, copy=None): return np.asarray(self._array) def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): diff --git a/numpy/matrixlib/defmatrix.py b/numpy/matrixlib/defmatrix.py index c0bd3b79e11b..866f867c8eaa 100644 --- a/numpy/matrixlib/defmatrix.py +++ b/numpy/matrixlib/defmatrix.py @@ -144,6 +144,7 @@ def __new__(subtype, data, dtype=None, copy=True): data = _convert_from_string(data) # now convert data to an array + copy = None if not copy else True arr = N.array(data, dtype=dtype, copy=copy) ndim = arr.ndim shape = arr.shape diff --git a/numpy/polynomial/chebyshev.py b/numpy/polynomial/chebyshev.py index 69e989d00d45..20ee10c9980d 100644 --- a/numpy/polynomial/chebyshev.py +++ b/numpy/polynomial/chebyshev.py @@ -1423,7 +1423,7 @@ def chebvander(x, deg): if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=False, ndmin=1) + 0.0 + x = np.array(x, copy=None, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) diff --git a/numpy/polynomial/hermite.py b/numpy/polynomial/hermite.py index d27cfdc496bb..4671f93244bd 100644 --- a/numpy/polynomial/hermite.py +++ b/numpy/polynomial/hermite.py @@ -869,7 +869,7 @@ def hermval(x, c, tensor=True): [115., 203.]]) """ - c = np.array(c, ndmin=1, copy=False) + c = np.array(c, ndmin=1, copy=None) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -1196,7 +1196,7 @@ def hermvander(x, deg): if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=False, ndmin=1) + 0.0 + x = np.array(x, copy=None, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) diff --git a/numpy/polynomial/hermite_e.py b/numpy/polynomial/hermite_e.py index ae977a365c04..f50b9d2449f3 100644 --- a/numpy/polynomial/hermite_e.py +++ b/numpy/polynomial/hermite_e.py @@ -862,7 +862,7 @@ def hermeval(x, c, tensor=True): [31., 54.]]) """ - c = np.array(c, ndmin=1, copy=False) + c = np.array(c, ndmin=1, copy=None) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -1145,7 +1145,7 @@ def hermevander(x, deg): if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=False, ndmin=1) + 0.0 + x = np.array(x, copy=None, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) diff --git a/numpy/polynomial/laguerre.py b/numpy/polynomial/laguerre.py index e0d0d6393e9b..11e2ac7229ca 100644 --- a/numpy/polynomial/laguerre.py +++ b/numpy/polynomial/laguerre.py @@ -867,7 +867,7 @@ def lagval(x, c, tensor=True): [-4.5, -2. ]]) """ - c = np.array(c, ndmin=1, copy=False) + c = np.array(c, ndmin=1, copy=None) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -1181,7 +1181,7 @@ def lagvander(x, deg): if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=False, ndmin=1) + 0.0 + x = np.array(x, copy=None, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) diff --git a/numpy/polynomial/legendre.py b/numpy/polynomial/legendre.py index 8925e43391c9..cfbf1486d486 100644 --- a/numpy/polynomial/legendre.py +++ b/numpy/polynomial/legendre.py @@ -889,7 +889,7 @@ def legval(x, c, tensor=True): The evaluation uses Clenshaw recursion, aka synthetic division. """ - c = np.array(c, ndmin=1, copy=False) + c = np.array(c, ndmin=1, copy=None) if c.dtype.char in '?bBhHiIlLqQpP': c = c.astype(np.double) if isinstance(x, (tuple, list)): @@ -1163,7 +1163,7 @@ def legvander(x, deg): if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=False, ndmin=1) + 0.0 + x = np.array(x, copy=None, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) diff --git a/numpy/polynomial/polynomial.py b/numpy/polynomial/polynomial.py index 89db1ebb14f9..2241c49235a4 100644 --- a/numpy/polynomial/polynomial.py +++ b/numpy/polynomial/polynomial.py @@ -750,7 +750,7 @@ def polyval(x, c, tensor=True): array([2., 7.]) """ - c = np.array(c, ndmin=1, copy=False) + c = np.array(c, ndmin=1, copy=None) if c.dtype.char in '?bBhHiIlLqQpP': # astype fails with NA c = c + 0.0 @@ -841,7 +841,7 @@ def polyvalfromroots(x, r, tensor=True): array([-0., 0.]) """ - r = np.array(r, ndmin=1, copy=False) + r = np.array(r, ndmin=1, copy=None) if r.dtype.char in '?bBhHiIlLqQpP': r = r.astype(np.double) if isinstance(x, (tuple, list)): @@ -1149,7 +1149,7 @@ def polyvander(x, deg): if ideg < 0: raise ValueError("deg must be non-negative") - x = np.array(x, copy=False, ndmin=1) + 0.0 + x = np.array(x, copy=None, ndmin=1) + 0.0 dims = (ideg + 1,) + x.shape dtyp = x.dtype v = np.empty(dims, dtype=dtyp) diff --git a/numpy/polynomial/polyutils.py b/numpy/polynomial/polyutils.py index 0b1dac0f67ac..54ffe5937e8c 100644 --- a/numpy/polynomial/polyutils.py +++ b/numpy/polynomial/polyutils.py @@ -113,7 +113,7 @@ def as_series(alist, trim=True): [array([2.]), array([1.1, 0. ])] """ - arrays = [np.array(a, ndmin=1, copy=False) for a in alist] + arrays = [np.array(a, ndmin=1, copy=None) for a in alist] for a in arrays: if a.size == 0: raise ValueError("Coefficient array is empty") @@ -413,7 +413,7 @@ def _vander_nd(vander_fs, points, degrees): raise ValueError("Unable to guess a dtype or shape when no points are given") # convert to the same shape and type - points = tuple(np.array(tuple(points), copy=False) + 0.0) + points = tuple(np.asarray(tuple(points)) + 0.0) # produce the vandermonde matrix for each dimension, placing the last # axis of each in an independent trailing axis of the output diff --git a/numpy/random/_bounded_integers.pyx.in b/numpy/random/_bounded_integers.pyx.in index 6743001d6d58..a8a2729535be 100644 --- a/numpy/random/_bounded_integers.pyx.in +++ b/numpy/random/_bounded_integers.pyx.in @@ -334,8 +334,8 @@ cdef object _rand_{{nptype}}(object low, object high, object size, if (np.prod(size) == 0): return np.empty(size, dtype=np.{{otype}}) - low_arr = np.array(low, copy=False) - high_arr = np.array(high, copy=False) + low_arr = np.asarray(low) + high_arr = np.asarray(high) low_ndim = np.PyArray_NDIM(low_arr) high_ndim = np.PyArray_NDIM(high_arr) if low_ndim == 0 and high_ndim == 0: diff --git a/numpy/random/_generator.pyx b/numpy/random/_generator.pyx index fb37c7835983..ab8a15555ae3 100644 --- a/numpy/random/_generator.pyx +++ b/numpy/random/_generator.pyx @@ -847,7 +847,7 @@ cdef class Generator: cdef uint64_t[::1] hash_set # Format and Verify input a_original = a - a = np.array(a, copy=False) + a = np.asarray(a) if a.ndim == 0: try: # __index__ must return an integer by python rules. @@ -907,7 +907,7 @@ cdef class Generator: uniform_samples = self.random(shape) idx = cdf.searchsorted(uniform_samples, side='right') # searchsorted returns a scalar - idx = np.array(idx, copy=False, dtype=np.int64) + idx = np.asarray(idx, dtype=np.int64) else: idx = self.integers(0, pop_size, size=shape, dtype=np.int64) else: diff --git a/numpy/random/mtrand.pyx b/numpy/random/mtrand.pyx index 381a4775005f..d67e4533f663 100644 --- a/numpy/random/mtrand.pyx +++ b/numpy/random/mtrand.pyx @@ -954,7 +954,7 @@ cdef class RandomState: """ # Format and Verify input - a = np.array(a, copy=False) + a = np.asarray(a) if a.ndim == 0: try: # __index__ must return an integer by python rules. @@ -1013,7 +1013,7 @@ cdef class RandomState: idx = cdf.searchsorted(uniform_samples, side='right') # searchsorted returns a scalar # force cast to int for LLP64 - idx = np.array(idx, copy=False).astype(np.long, casting='unsafe') + idx = np.asarray(idx).astype(np.long, casting='unsafe') else: idx = self.randint(0, pop_size, size=shape) else: diff --git a/numpy/random/tests/test_generator_mt19937_regressions.py b/numpy/random/tests/test_generator_mt19937_regressions.py index f16af2b293ce..d451c6acd16d 100644 --- a/numpy/random/tests/test_generator_mt19937_regressions.py +++ b/numpy/random/tests/test_generator_mt19937_regressions.py @@ -140,7 +140,7 @@ class N(np.ndarray): class M: a = np.arange(5) - def __array__(self): + def __array__(self, dtype=None, copy=None): return self.a mt19937 = Generator(MT19937(1)) diff --git a/numpy/random/tests/test_randomstate_regression.py b/numpy/random/tests/test_randomstate_regression.py index 6e55cf5f1f29..3fd8776c7f96 100644 --- a/numpy/random/tests/test_randomstate_regression.py +++ b/numpy/random/tests/test_randomstate_regression.py @@ -143,7 +143,7 @@ class N(np.ndarray): class M: a = np.arange(5) - def __array__(self): + def __array__(self, dtype=None, copy=None): return self.a random.seed(1) diff --git a/numpy/random/tests/test_regression.py b/numpy/random/tests/test_regression.py index 8bf419875b3f..f7b02dc4f7d7 100644 --- a/numpy/random/tests/test_regression.py +++ b/numpy/random/tests/test_regression.py @@ -139,7 +139,7 @@ class N(np.ndarray): class M: a = np.arange(5) - def __array__(self): + def __array__(self, dtype=None, copy=None): return self.a np.random.seed(1) diff --git a/numpy/typing/tests/data/pass/arithmetic.py b/numpy/typing/tests/data/pass/arithmetic.py index e69228cd1e9d..496586821582 100644 --- a/numpy/typing/tests/data/pass/arithmetic.py +++ b/numpy/typing/tests/data/pass/arithmetic.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any +from typing import Any, Optional import numpy as np import pytest @@ -26,7 +26,8 @@ class Object: - def __array__(self) -> np.ndarray[Any, np.dtype[np.object_]]: + def __array__(self, dtype: Optional[np.typing.DTypeLike] = None, + copy: Optional[bool] = None) -> np.ndarray[Any, np.dtype[np.object_]]: ret = np.empty((), dtype=object) ret[()] = self return ret diff --git a/numpy/typing/tests/data/pass/array_constructors.py b/numpy/typing/tests/data/pass/array_constructors.py index 98c0a291ebdb..17b6fab93ad8 100644 --- a/numpy/typing/tests/data/pass/array_constructors.py +++ b/numpy/typing/tests/data/pass/array_constructors.py @@ -27,7 +27,7 @@ def func(i: int, j: int, **kwargs: Any) -> SubClass: np.ndarray([Index()]) np.array(1, dtype=float) -np.array(1, copy=False) +np.array(1, copy=None) np.array(1, order='F') np.array(1, order=None) np.array(1, subok=True) diff --git a/numpy/typing/tests/data/pass/ufunclike.py b/numpy/typing/tests/data/pass/ufunclike.py index 7eac89e8f9aa..4baa0334a404 100644 --- a/numpy/typing/tests/data/pass/ufunclike.py +++ b/numpy/typing/tests/data/pass/ufunclike.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Any +from typing import Any, Optional import numpy as np @@ -13,7 +13,8 @@ def __floor__(self) -> Object: def __ge__(self, value: object) -> bool: return True - def __array__(self) -> np.ndarray[Any, np.dtype[np.object_]]: + def __array__(self, dtype: Optional[np.typing.DTypeLike] = None, + copy: Optional[bool] = None) -> np.ndarray[Any, np.dtype[np.object_]]: ret = np.empty((), dtype=object) ret[()] = self return ret diff --git a/tools/ci/array-api-skips.txt b/tools/ci/array-api-skips.txt index 1da724359f5a..fec7750098c5 100644 --- a/tools/ci/array-api-skips.txt +++ b/tools/ci/array-api-skips.txt @@ -1,6 +1,3 @@ -# copy not implemented -array_api_tests/test_creation_functions.py::test_asarray_arrays - # 'unique_inverse' output array is 1-D for 0-D input array_api_tests/test_set_functions.py::test_unique_all array_api_tests/test_set_functions.py::test_unique_inverse @@ -28,11 +25,6 @@ array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(x1_i is -infinity array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(isfinite(x1_i) and x1_i > 0 and x2_i is -infinity) -> -0] array_api_tests/test_special_cases.py::test_iop[__ifloordiv__(isfinite(x1_i) and x1_i < 0 and x2_i is +infinity) -> -0] -# asarray() got an unexpected keyword argument 'copy' -array_api_tests/test_array_object.py::test_setitem -# array_api_tests/test_array_object.py::test_setitem_masking -array_api_tests/test_creation_functions.py::test_asarray_scalars - # fft test suite is buggy as of 83f0bcdc array_api_tests/test_fft.py @@ -54,8 +46,5 @@ array_api_tests/test_signatures.py::test_func_signature[reshape] array_api_tests/test_signatures.py::test_func_signature[argsort] array_api_tests/test_signatures.py::test_func_signature[sort] -# asarray() got an unexpected keyword argument 'copy' -array_api_tests/test_special_cases.py::test_iop - # assertionError: out.dtype=float32, but should be float64 [sum(float32)] array_api_tests/test_statistical_functions.py::test_sum pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy