Skip to content

Commit 1f35b27

Browse files
committed
DOC: Update __array__ copy keyword docs
1 parent 44fddc2 commit 1f35b27

File tree

4 files changed

+40
-19
lines changed

4 files changed

+40
-19
lines changed

doc/source/numpy_2_0_migration_guide.rst

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -412,20 +412,23 @@ The :ref:`copy keyword behavior changes <copy-keyword-changes-2.0>` in
412412
`~numpy.asarray`, `~numpy.array` and `ndarray.__array__
413413
<numpy.ndarray.__array__>` may require these changes:
414414

415-
1. Code using ``np.array(..., copy=False)`` can in most cases be changed to
416-
``np.asarray(...)``. Older code tended to use ``np.array`` like this because
417-
it had less overhead than the default ``np.asarray`` copy-if-needed
418-
behavior. This is no longer true, and ``np.asarray`` is the preferred function.
419-
2. For code that explicitly needs to pass ``None``/``False`` meaning "copy if
420-
needed" in a way that's compatible with NumPy 1.x and 2.x, see
421-
`scipy#20172 <https://github.com/scipy/scipy/pull/20172>`__ for an example
422-
of how to do so.
423-
3. For any ``__array__`` method on a non-NumPy array-like object, a
424-
``copy=None`` keyword can be added to the signature - this will work with
425-
older NumPy versions as well. If ``copy`` keyword is considered in
426-
the ``__array__`` method implementation, then for ``copy=True`` always
427-
return a new copy.
428-
415+
* Code using ``np.array(..., copy=False)`` can in most cases be changed to
416+
``np.asarray(...)``. Older code tended to use ``np.array`` like this because
417+
it had less overhead than the default ``np.asarray`` copy-if-needed
418+
behavior. This is no longer true, and ``np.asarray`` is the preferred function.
419+
* For code that explicitly needs to pass ``None``/``False`` meaning "copy if
420+
needed" in a way that's compatible with NumPy 1.x and 2.x, see
421+
`scipy#20172 <https://github.com/scipy/scipy/pull/20172>`__ for an example
422+
of how to do so.
423+
* For any ``__array__`` method on a non-NumPy array-like object, ``dtype=None``
424+
and ``copy=None`` keywords must be added to the signature - this will work with older
425+
NumPy versions as well (although older numpy versions will never pass in ``copy`` keyword).
426+
If the keywords are actually used in the ``__array__`` method implementation, then for:
427+
428+
* ``copy=True`` and any ``dtype`` value always return a new copy,
429+
* ``copy=None`` create a copy if required (for example by ``dtype``),
430+
* ``copy=False`` a copy must never be made. If a copy is needed to return a numpy array
431+
or satisfy ``dtype``, then raise an exception (``ValueError``).
429432

430433
Writing numpy-version-dependent code
431434
------------------------------------

doc/source/user/basics.dispatch.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ example that has rather narrow utility but illustrates the concepts involved.
2323
... def __repr__(self):
2424
... return f"{self.__class__.__name__}(N={self._N}, value={self._i})"
2525
... def __array__(self, dtype=None, copy=None):
26+
... if copy is False:
27+
... raise ValueError(
28+
... "`copy=False` isn't supported. A copy is always created."
29+
... )
2630
... return self._i * np.eye(self._N, dtype=dtype)
2731

2832
Our custom array can be instantiated like:
@@ -85,6 +89,10 @@ For this example we will only handle the method ``__call__``
8589
... def __repr__(self):
8690
... return f"{self.__class__.__name__}(N={self._N}, value={self._i})"
8791
... def __array__(self, dtype=None, copy=None):
92+
... if copy is False:
93+
... raise ValueError(
94+
... "`copy=False` isn't supported. A copy is always created."
95+
... )
8896
... return self._i * np.eye(self._N, dtype=dtype)
8997
... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
9098
... if method == '__call__':
@@ -136,6 +144,10 @@ conveniently by inheriting from the mixin
136144
... def __repr__(self):
137145
... return f"{self.__class__.__name__}(N={self._N}, value={self._i})"
138146
... def __array__(self, dtype=None, copy=None):
147+
... if copy is False:
148+
... raise ValueError(
149+
... "`copy=False` isn't supported. A copy is always created."
150+
... )
139151
... return self._i * np.eye(self._N, dtype=dtype)
140152
... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
141153
... if method == '__call__':
@@ -174,6 +186,10 @@ functions to our custom variants.
174186
... def __repr__(self):
175187
... return f"{self.__class__.__name__}(N={self._N}, value={self._i})"
176188
... def __array__(self, dtype=None, copy=None):
189+
... if copy is False:
190+
... raise ValueError(
191+
... "`copy=False` isn't supported. A copy is always created."
192+
... )
177193
... return self._i * np.eye(self._N, dtype=dtype)
178194
... def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
179195
... if method == '__call__':

numpy/_core/_add_newdocs.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2943,10 +2943,11 @@
29432943

29442944
add_newdoc('numpy._core.multiarray', 'ndarray', ('__array__',
29452945
"""
2946-
a.__array__([dtype], /, *, copy=None)
2946+
a.__array__([dtype], *, copy=None)
29472947
2948-
For ``dtype`` parameter it returns either a new reference to self if
2949-
``dtype`` is not given or a new array of provided data type if ``dtype``
2948+
For ``dtype`` parameter it returns a new reference to self if
2949+
``dtype`` is not given or it matches array's data type.
2950+
A new array of provided data type is returned if ``dtype``
29502951
is different from the current data type of the array.
29512952
For ``copy`` parameter it returns a new reference to self if
29522953
``copy=False`` or ``copy=None`` and copying isn't enforced by ``dtype``

numpy/_core/src/multiarray/ctors.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2459,8 +2459,9 @@ check_or_clear_and_warn_error_if_due_to_copy_kwarg(PyObject *kwnames)
24592459
Py_DECREF(type);
24602460
Py_DECREF(value);
24612461
Py_XDECREF(traceback);
2462-
if (DEPRECATE("__array__ should implement the 'dtype' and "
2463-
"'copy' keyword argument") < 0) {
2462+
if (DEPRECATE("__array__ implementation doesn't accept a copy keyword, "
2463+
"so passing copy=False failed. __array__ must implement "
2464+
"'dtype' and 'copy' keyword arguments.") < 0) {
24642465
return -1;
24652466
}
24662467
return 0;

0 commit comments

Comments
 (0)