8000 API: Move back to original semantics for np.astype · numpy/numpy@92cb661 · GitHub
[go: up one dir, main page]

Skip to content

Commit 92cb661

Browse files
committed
API: Move back to original semantics for np.astype
1 parent dfd46e6 commit 92cb661

20 files changed

+135
-110
lines changed

benchmarks/benchmarks/bench_array_coercion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ def time_array_dtype_not_kwargs(self, array_like):
2323
np.array(array_like, self.int64)
2424

2525
def time_array_no_copy(self, array_like):
26-
np.array(array_like, copy=False)
26+
np.array(array_like, copy=None)
2727

2828
def time_array_subok(self, array_like):
2929
np.array(array_like, subok=True)
3030

3131
def time_array_all_kwargs(self, array_like):
32-
np.array(array_like, dtype=self.int64, copy=False, order="F",
32+
np.array(array_like, dtype=self.int64, copy=None, order="F",
3333
subok=False, ndmin=2)
3434

3535
def time_asarray(self, array_like):
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
New ``copy`` keyword meaning for `numpy.array` and `numpy.asarray`
2+
------------------------------------------------------------------
3+
Now `numpy.array` and `numpy.asarray` support three values for ``copy`` parameter:
4+
* ``None`` - A copy will only be made if it is necessary.
5+
* ``True`` - Always make a copy.
6+
* ``False`` - Never make a copy. If a copy is required a ``ValueError`` is raised.
7+
8+
The meaning of ``False`` changed as it now raises an exception if a copy is needed.

numpy/_core/_add_newdocs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@
807807
a default ``dtype`` that can represent the values (by applying promotion
808808
rules when necessary.)
809809
copy : bool, optional
810-
If ``True`` (default), then the object is copied. If ``False``, a copy
810+
If ``True`` (default), then the object is copied. If ``None``, a copy
811811
will only be made if ``__array__`` returns a copy, if obj is a nested
812812
sequence, or if a copy is needed to satisfy any of the other
813813
requirements (``dtype``, ``order``, etc.). For ``False`` it raises

numpy/_core/function_base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,9 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None,
181181
_nx.floor(y, out=y)
182182

183183
if retstep:
184-
return y.astype(dtype, copy=None), step
184+
return y.astype(dtype, copy=False), step
185185
else:
186-
return y.astype(dtype, copy=None)
186+
return y.astype(dtype, copy=False)
187187

188188

189189
def _logspace_dispatcher(start, stop, num=None, endpoint=None, base=None,
@@ -300,7 +300,7 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None,
300300
base = np.expand_dims(base, axis=axis)
301301
if dtype is None:
302302
return _nx.power(base, y)
303-
return _nx.power(base, y).astype(dtype, copy=None)
303+
return _nx.power(base, y).astype(dtype, copy=False)
304304

305305

306306
def _geomspace_dispatcher(start, stop, num=None, endpoint=None, dtype=None,
@@ -463,7 +463,7 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
463463
if axis != 0:
464464
result = _nx.moveaxis(result, 0, axis)
465465

466-
return result.astype(dtype, copy=None)
466+
return result.astype(dtype, copy=False)
467467

468468

469469
def _needs_add_docstring(obj):

numpy/_core/numeric.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ def count_nonzero(a, axis=None, *, keepdims=False):
478478
if np.issubdtype(a.dtype, np.character):
479479
a_bool = a != a.dtype.type()
480480
else:
481-
a_bool = a.astype(np.bool, copy=None)
481+
a_bool = a.astype(np.bool, copy=False)
482482

483483
return a_bool.sum(axis=axis, dtype=np.intp, keepdims=keepdims)
484484

numpy/_core/src/multiarray/conversion_utils.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,31 @@ PyArray_CopyConverter(PyObject *obj, NPY_COPYMODE *copymode) {
260260
return NPY_SUCCEED;
261261
}
262262

263+
NPY_NO_EXPORT int
264+
PyArray_AsTypeCopyConverter(PyObject *obj, NPY_ASTYPECOPYMODE *copymode)
265+
{
266+
int int_copymode;
267+
static PyObject* numpy_CopyMode = NULL;
268+
npy_cache_import("numpy", "_CopyMode", &numpy_CopyMode);
269+
270+
if (numpy_CopyMode != NULL && (PyObject *)Py_TYPE(obj) == numpy_CopyMode) {
271+
PyErr_SetString(PyExc_ValueError,
272+
"_CopyMode enum is not allowed for astype function. "
273+
"Use true/false instead.");
274+
return NPY_FAIL;
275+
}
276+
else {
277+
npy_bool bool_copymode;
278+
if (!PyArray_BoolConverter(obj, &bool_copymode)) {
279+
return NPY_FAIL;
280+
}
281+
int_copymode = (int)bool_copymode;
282+
}
283+
284+
*copymode = (NPY_ASTYPECOPYMODE)int_copymode;
285+
return NPY_SUCCEED;
286+
}
287+
263288
/*NUMPY_API
264289
* Get buffer chunk from object
265290
*

numpy/_core/src/multiarray/conversion_utils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ typedef enum {
1818
NPY_COPY_IF_NEEDED = 2,
1919
} NPY_COPYMODE;
2020

21+
typedef enum {
22+
NPY_AS_TYPE_COPY_IF_NEEDED = 0,
23+
NPY_AS_TYPE_COPY_ALWAYS = 1,
24+
} NPY_ASTYPECOPYMODE;
25+
2126
NPY_NO_EXPORT int
2227
PyArray_CopyConverter(PyObject *obj, NPY_COPYMODE *copyflag);
2328

29+
NPY_NO_EXPORT int
30+
PyArray_AsTypeCopyConverter(PyObject *obj, NPY_ASTYPECOPYMODE *copyflag);
31+
2432
NPY_NO_EXPORT int
2533
PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf);
2634

numpy/_core/src/multiarray/methods.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ array_astype(PyArrayObject *self,
782782
npy_dtype_info dt_info = {NULL, NULL};
783783
NPY_CASTING casting = NPY_UNSAFE_CASTING;
784784
NPY_ORDER order = NPY_KEEPORDER;
785-
NPY_COPYMODE forcecopy = 1;
785+
NPY_ASTYPECOPYMODE forcecopy = 1;
786786
int subok = 1;
787787

788788
NPY_PREPARE_ARGPARSER;
@@ -791,7 +791,7 @@ array_astype(PyArrayObject *self,
791791
"|order", &PyArray_OrderConverter, &order,
792792
"|casting", &PyArray_CastingConverter, &casting,
793793
"|subok", &PyArray_PythonPyIntFromInt, &subok,
794-
"|copy", &PyArray_CopyConverter, &forcecopy,
794+
"|copy", &PyArray_AsTypeCopyConverter, &forcecopy,
795795
NULL, NULL, NULL) < 0) {
796796
Py_XDECREF(dt_info.descr);
797797
Py_XDECREF(dt_info.dtype);
@@ -813,7 +813,7 @@ array_astype(PyArrayObject *self,
813813
* and it's not a subtype if subok is False, then we
814814
* can skip the copy.
815815
*/
816-
if (forcecopy != NPY_COPY_ALWAYS &&
816+
if (forcecopy != NPY_AS_TYPE_COPY_ALWAYS &&
817817
(order == NPY_KEEPORDER ||
818818
(order == NPY_ANYORDER &&
819819
(PyArray_IS_C_CONTIGUOUS(self) ||
@@ -829,14 +829,6 @@ array_astype(PyArrayObject *self,
829829
return (PyObject *)self;
830830
}
831831

832-
if (forcecopy == NPY_COPY_NEVER) {
833-
PyErr_SetString(PyExc_ValueError,
834-
"Unable to avoid copy while casting in never copy mode. "
835-
"Use copy=None to copy only if necessary.");
836-
Py_DECREF(dtype);
837-
return NULL;
838-
}
839-
840832
if (!PyArray_CanCastArrayTo(self, dtype, casting)) {
841833
PyErr_Clear();
842834
npy_set_invalid_cast_error(

numpy/_core/tests/test_api.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,12 @@ def test_array_astype():
183183

184184
# order parameter allows overriding of the memory layout,
185185
# forcing a copy if the layout is wrong
186-
b = a.astype('f4', order='F', copy=None)
186+
b = a.astype('f4', order='F', copy=False)
187187
assert_equal(a, b)
188188
assert_(not (a is b))
189189
assert_(b.flags.f_contiguous)
190190

191-
b = a.astype('f4', order='C', copy=None)
191+
b = a.astype('f4', order='C', copy=False)
192192
assert_equal(a, b)
193193
assert_(a is b)
194194
assert_(b.flags.c_contiguous)
@@ -214,12 +214,12 @@ class MyNDArray(np.ndarray):
214214
assert_(a is b)
215215

216216
# subok=True is default, and creates a subtype on a cast
217-
b = a.astype('i4', copy=None)
217+
b = a.astype('i4', copy=False)
218218
assert_equal(a, b)
219219
assert_equal(type(b), MyNDArray)
220220

221221
# subok=False never returns a subclass
222-
b = a.astype('f4', subok=False, copy=None)
222+
b = a.astype('f4', subok=False, copy=False)
223223
assert_equal(a, b)
224224
assert_(not (a is b))
225225
assert_(type(b) is not MyNDArray)
@@ -570,22 +570,14 @@ def test_astype_copyflag():
570570
arr = np.arange(10, dtype=np.intp)
571571

572572
res_true = arr.astype(np.intp, copy=True)
573-
assert not np.may_share_memory(arr, res_true)
574-
res_always = arr.astype(np.intp, copy=np._CopyMode.ALWAYS)
575-
assert not np.may_share_memory(arr, res_always)
573+
assert not np.shares_memory(arr, res_true)
576574

577575
res_false = arr.astype(np.intp, copy=False)
578-
# `res_false is arr` currently, but check `may_share_memory`.
579-
assert np.may_share_memory(arr, res_false)
580-
res_if_needed = arr.astype(np.intp, copy=None)
581-
# `res_if_needed is arr` currently, but check `may_share_memory`.
582-
assert np.may_share_memory(arr, res_if_needed)
583-
584-
res_never = arr.astype(np.intp, copy=np._CopyMode.NEVER)
585-
assert np.may_share_memory(arr, res_never)
586-
587-
# Simple tests for when a copy is necessary:
588-
res_if_needed = arr.astype(np.float64, copy=None)
589-
assert_array_equal(res_if_needed, arr)
576+
assert np.shares_memory(arr, res_false)
577+
578+
res_false_float = arr.astype(np.float64, copy=False)
579+
assert not np.shares_memory(arr, res_false_float)
580+
581+
# _CopyMode enum isn't allowed
590582
assert_raises(ValueError, arr.astype, np.float64,
591-
copy=False)
583+
copy=np._CopyMode.NEVER)

numpy/fft/tests/test_pocketfft.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ def test_fft_with_order(dtype, order, fft):
241241
# Check that FFT/IFFT produces identical results for C, Fortran and
242242
# non contiguous arrays
243243
rng = np.random.RandomState(42)
244-
X = rng.rand(8, 7, 13).astype(dtype, copy=None)
244+
X = rng.rand(8, 7, 13).astype(dtype, copy=False)
245245
# See discussion in pull/14178
246246
_tol = 8.0 * np.sqrt(np.log2(X.size)) * np.finfo(X.dtype).eps
247247
if order == 'F':

numpy/lib/_arraypad_impl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ def _as_pairs(x, ndim, as_index=False):
490490

491491
x = np.array(x)
492492
if as_index:
493-
x = np.round(x).astype(np.intp, copy=None)
493+
x = np.round(x).astype(np.intp, copy=False)
494494

495495
if x.ndim < 3:
496496
# Optimization: Possibly use faster paths for cases where `x` has

numpy/lib/_histograms_impl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ def histogram(a, bins=10, range=None, density=None, weights=None):
838838
# This cast ensures no type promotions occur below, which gh-10322
839839
# make unpredictable. Getting it wrong leads to precision errors
840840
# like gh-8123.
841-
tmp_a = tmp_a.astype(bin_edges.dtype, copy=None)
841+
tmp_a = tmp_a.astype(bin_edges.dtype, copy=False)
842842

843843
# Compute the bin indices, and for values that lie exactly on
844844
# last_edge we need to subtract one

numpy/lib/_twodim_base_impl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ def tri(N, M=None, k=0, dtype=float, *, like=None):
415415
arange(-k, M-k, dtype=_min_int(-k, M - k)))
416416

417417
# Avoid making a copy if the requested type is already bool
418-
m = m.astype(dtype, copy=None)
418+
m = m.astype(dtype, copy=False)
419419

420420
return m
421421

numpy/lib/recfunctions.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ def repack_fields(a, align=False, recurse=False):
828828
"""
829829
if not isinstance(a, np.dtype):
830830
dt = repack_fields(a.dtype, align=align, recurse=recurse)
831-
return a.astype(dt, copy=None)
831+
return a.astype(dt, copy=False)
832832

833833
if a.names is None:
834834
return a
@@ -936,7 +936,7 @@ def _structured_to_unstructured_dispatcher(arr, dtype=None, copy=None,
936936
return (arr,)
937937

938938
@array_function_dispatch(_structured_to_unstructured_dispatcher)
939-
def structured_to_unstructured(arr, dtype=None, copy=None, casting='unsafe'):
939+
def structured_to_unstructured(arr, dtype=None, copy=False, casting='unsafe'):
940940
"""
941941
Converts an n-D structured array into an (n+1)-D unstructured array.
942942
@@ -955,11 +955,10 @@ def structured_to_unstructured(arr, dtype=None, copy=None, casting='unsafe'):
955955
dtype : dtype, optional
956956
The dtype of the output unstructured array.
957957
copy : bool, optional
958-
If True, always return a copy. If None, a view is returned if
958+
If true, always return a copy. If false, a view is returned if
959959
possible, such as when the `dtype` and strides of the fields are
960960
suitable and the array subtype is one of `numpy.ndarray`,
961-
`numpy.recarray` or `numpy.memmap`. For False it raises a ValueError
962-
if a copy cannot be avoided.
961+
`numpy.recarray` or `numpy.memmap`.
963962
964963
.. versionchanged:: 1.25.0
965964
A view can now be returned if the fields are separated by a
@@ -1072,7 +1071,7 @@ def _unstructured_to_structured_dispatcher(arr, dtype=None, names=None,
10721071

10731072
@array_function_dispatch(_unstructured_to_structured_dispatcher)
10741073
def unstructured_to_structured(arr, dtype=None, names=None, align=False,
1075-
copy=None, casting='unsafe'):
1074+
copy=False, casting='unsafe'):
10761075
"""
10771076
Converts an n-D unstructured array into an (n-1)-D structured array.
10781077

0 commit comments

Comments
 (0)
0