diff --git a/array-api-tests-xfails.txt b/array-api-tests-xfails.txt index 7e539db..68c7fdb 100644 --- a/array-api-tests-xfails.txt +++ b/array-api-tests-xfails.txt @@ -1,7 +1,3 @@ -# copy=False is not yet implemented -# https://github.com/numpy/numpy/pull/25168 -array_api_tests/test_creation_functions.py::test_asarray_arrays - # Known special case issue in NumPy. Not worth working around here # https://github.com/numpy/numpy/issues/21213 array_api_tests/test_special_cases.py::test_iop[__ipow__(x1_i is -infinity and x2_i > 0 and not (x2_i.is_integer() and x2_i % 2 == 1)) -> +infinity] diff --git a/array_api_strict/_creation_functions.py b/array_api_strict/_creation_functions.py index 90dc4e0..ad7ec82 100644 --- a/array_api_strict/_creation_functions.py +++ b/array_api_strict/_creation_functions.py @@ -11,7 +11,6 @@ NestedSequence, SupportsBufferProtocol, ) - from collections.abc import Sequence from ._dtypes import _DType, _all_dtypes import numpy as np @@ -22,6 +21,12 @@ def _check_valid_dtype(dtype): if dtype not in (None,) + _all_dtypes: raise ValueError("dtype must be one of the supported dtypes") +def _supports_buffer_protocol(obj): + try: + memoryview(obj) + except TypeError: + return False + return True def asarray( obj: Union[ @@ -36,7 +41,7 @@ def asarray( *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, - copy: Optional[Union[bool, np._CopyMode]] = None, + copy: Optional[bool] = None, ) -> Array: """ Array API compatible wrapper for :py:func:`np.asarray `. @@ -53,20 +58,37 @@ def asarray( _np_dtype = dtype._np_dtype if device not in [CPU_DEVICE, None]: raise ValueError(f"Unsupported device {device!r}") - if copy in (False, np._CopyMode.IF_NEEDED): - # Note: copy=False is not yet implemented in np.asarray - raise NotImplementedError("copy=False is not yet implemented") + + if np.__version__[0] < '2': + if copy is False: + # Note: copy=False is not yet implemented in np.asarray for + # NumPy 1 + + # Work around it by creating the new array and seeing if NumPy + # copies it. + if isinstance(obj, Array): + new_array = np.array(obj._array, copy=copy, dtype=_np_dtype) + if new_array is not obj._array: + raise ValueError("Unable to avoid copy while creating an array from given array.") + return Array._new(new_array) + elif _supports_buffer_protocol(obj): + # Buffer protocol will always support no-copy + return Array._new(np.array(obj, copy=copy, dtype=_np_dtype)) + else: + # No-copy is unsupported for Python built-in types. + raise ValueError("Unable to avoid copy while creating an array from given object.") + + if copy is None: + # NumPy 1 treats copy=False the same as the standard copy=None + copy = False + if isinstance(obj, Array): - if dtype is not None and obj.dtype != dtype: - copy = True - if copy in (True, np._CopyMode.ALWAYS): - return Array._new(np.array(obj._array, copy=True, dtype=_np_dtype)) - return obj + return Array._new(np.array(obj._array, copy=copy, dtype=_np_dtype)) if dtype is None and isinstance(obj, int) and (obj > 2 ** 64 or obj < -(2 ** 63)): # Give a better error message in this case. NumPy would convert this # to an object array. TODO: This won't handle large integers in lists. raise OverflowError("Integer out of bounds for array dtypes") - res = np.asarray(obj, dtype=_np_dtype) + res = np.array(obj, dtype=_np_dtype, copy=copy) return Array._new(res) diff --git a/array_api_strict/tests/test_creation_functions.py b/array_api_strict/tests/test_creation_functions.py index ee022c0..78d4c80 100644 --- a/array_api_strict/tests/test_creation_functions.py +++ b/array_api_strict/tests/test_creation_functions.py @@ -50,19 +50,49 @@ def test_asarray_copy(): a[0] = 0 assert all(b[0] == 1) assert all(a[0] == 0) + a = asarray([1]) - b = asarray(a, copy=np._CopyMode.ALWAYS) + b = asarray(a, copy=False) a[0] = 0 - assert all(b[0] == 1) - assert all(a[0] == 0) + assert all(b[0] == 0) + + a = asarray([1]) + assert_raises(ValueError, lambda: asarray(a, copy=False, dtype=float64)) + + a = asarray([1]) + b = asarray(a, copy=None) + a[0] = 0 + assert all(b[0] == 0) + a = asarray([1]) - b = asarray(a, copy=np._CopyMode.NEVER) + b = asarray(a, dtype=float64, copy=None) + a[0] = 0 + assert all(b[0] == 1.0) + + # Python built-in types + for obj in [True, 0, 0.0, 0j, [0], [[0]]]: + asarray(obj, copy=True) # No error + asarray(obj, copy=None) # No error + assert_raises(ValueError, lambda: asarray(obj, copy=False)) + + # Buffer protocol + a = np.array([1]) + b = asarray(a, copy=True) + assert isinstance(b, Array) + a[0] = 0 + assert all(b[0] == 1) + + a = np.array([1]) + b = asarray(a, copy=False) + assert isinstance(b, Array) a[0] = 0 assert all(b[0] == 0) - assert_raises(NotImplementedError, lambda: asarray(a, copy=False)) - assert_raises(NotImplementedError, - lambda: asarray(a, copy=np._CopyMode.IF_NEEDED)) + a = np.array([1]) + b = asarray(a, copy=None) + assert isinstance(b, Array) + a[0] = 0 + assert all(b[0] == 0) def test_arange_errors(): arange(1, device=CPU_DEVICE) # Doesn't error