array-api-strict 2.2__py3-none-any.whl → 2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. array_api_strict/__init__.py +10 -7
  2. array_api_strict/_array_object.py +31 -19
  3. array_api_strict/_creation_functions.py +2 -0
  4. array_api_strict/_data_type_functions.py +34 -3
  5. array_api_strict/_dtypes.py +1 -0
  6. array_api_strict/_elementwise_functions.py +140 -471
  7. array_api_strict/_fft.py +31 -5
  8. array_api_strict/_flags.py +10 -8
  9. array_api_strict/_helpers.py +37 -0
  10. array_api_strict/_linear_algebra_functions.py +1 -1
  11. array_api_strict/_manipulation_functions.py +5 -0
  12. array_api_strict/_searching_functions.py +44 -5
  13. array_api_strict/_statistical_functions.py +42 -5
  14. array_api_strict/_version.py +16 -16
  15. array_api_strict/tests/test_array_object.py +146 -74
  16. array_api_strict/tests/test_creation_functions.py +7 -0
  17. array_api_strict/tests/test_data_type_functions.py +24 -4
  18. array_api_strict/tests/test_elementwise_functions.py +66 -8
  19. array_api_strict/tests/test_flags.py +29 -18
  20. array_api_strict/tests/test_manipulation_functions.py +1 -1
  21. array_api_strict/tests/test_searching_functions.py +24 -0
  22. array_api_strict/tests/test_statistical_functions.py +19 -1
  23. array_api_strict/tests/test_validation.py +1 -1
  24. array_api_strict-2.3.dist-info/METADATA +68 -0
  25. array_api_strict-2.3.dist-info/RECORD +43 -0
  26. {array_api_strict-2.2.dist-info → array_api_strict-2.3.dist-info}/WHEEL +1 -1
  27. array_api_strict-2.2.dist-info/METADATA +0 -37
  28. array_api_strict-2.2.dist-info/RECORD +0 -41
  29. {array_api_strict-2.2.dist-info → array_api_strict-2.3.dist-info}/LICENSE +0 -0
  30. {array_api_strict-2.2.dist-info → array_api_strict-2.3.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,7 @@ from numpy.testing import assert_raises, suppress_warnings
5
5
  import numpy as np
6
6
  import pytest
7
7
 
8
- from .. import ones, asarray, result_type, all, equal
8
+ from .. import ones, arange, reshape, asarray, result_type, all, equal
9
9
  from .._array_object import Array, CPU_DEVICE, Device
10
10
  from .._dtypes import (
11
11
  _all_dtypes,
@@ -45,35 +45,46 @@ def test_validate_index():
45
45
  a = ones((3, 4))
46
46
 
47
47
  # Out of bounds slices are not allowed
48
- assert_raises(IndexError, lambda: a[:4])
49
- assert_raises(IndexError, lambda: a[:-4])
50
- assert_raises(IndexError, lambda: a[:3:-1])
51
- assert_raises(IndexError, lambda: a[:-5:-1])
52
- assert_raises(IndexError, lambda: a[4:])
53
- assert_raises(IndexError, lambda: a[-4:])
54
- assert_raises(IndexError, lambda: a[4::-1])
55
- assert_raises(IndexError, lambda: a[-4::-1])
56
-
57
- assert_raises(IndexError, lambda: a[...,:5])
58
- assert_raises(IndexError, lambda: a[...,:-5])
59
- assert_raises(IndexError, lambda: a[...,:5:-1])
60
- assert_raises(IndexError, lambda: a[...,:-6:-1])
61
- assert_raises(IndexError, lambda: a[...,5:])
62
- assert_raises(IndexError, lambda: a[...,-5:])
63
- assert_raises(IndexError, lambda: a[...,5::-1])
64
- assert_raises(IndexError, lambda: a[...,-5::-1])
48
+ assert_raises(IndexError, lambda: a[:4, 0])
49
+ assert_raises(IndexError, lambda: a[:-4, 0])
50
+ assert_raises(IndexError, lambda: a[:3:-1]) # XXX raises for a wrong reason
51
+ assert_raises(IndexError, lambda: a[:-5:-1, 0])
52
+ assert_raises(IndexError, lambda: a[4:, 0])
53
+ assert_raises(IndexError, lambda: a[-4:, 0])
54
+ assert_raises(IndexError, lambda: a[4::-1, 0])
55
+ assert_raises(IndexError, lambda: a[-4::-1, 0])
56
+
57
+ assert_raises(IndexError, lambda: a[..., :5])
58
+ assert_raises(IndexError, lambda: a[..., :-5])
59
+ assert_raises(IndexError, lambda: a[..., :5:-1])
60
+ assert_raises(IndexError, lambda: a[..., :-6:-1])
61
+ assert_raises(IndexError, lambda: a[..., 5:])
62
+ assert_raises(IndexError, lambda: a[..., -5:])
63
+ assert_raises(IndexError, lambda: a[..., 5::-1])
64
+ assert_raises(IndexError, lambda: a[..., -5::-1])
65
65
 
66
66
  # Boolean indices cannot be part of a larger tuple index
67
- assert_raises(IndexError, lambda: a[a[:,0]==1,0])
68
- assert_raises(IndexError, lambda: a[a[:,0]==1,...])
69
- assert_raises(IndexError, lambda: a[..., a[0]==1])
67
+ assert_raises(IndexError, lambda: a[a[:, 0] == 1, 0])
68
+ assert_raises(IndexError, lambda: a[a[:, 0] == 1, ...])
69
+ assert_raises(IndexError, lambda: a[..., a[0] == 1])
70
70
  assert_raises(IndexError, lambda: a[[True, True, True]])
71
71
  assert_raises(IndexError, lambda: a[(True, True, True),])
72
72
 
73
- # Integer array indices are not allowed (except for 0-D)
74
- idx = asarray([[0, 1]])
75
- assert_raises(IndexError, lambda: a[idx])
76
- assert_raises(IndexError, lambda: a[idx,])
73
+ # Mixing 1D integer array indices with slices, ellipsis or booleans is not allowed
74
+ idx = asarray([0, 1])
75
+ assert_raises(IndexError, lambda: a[..., idx])
76
+ assert_raises(IndexError, lambda: a[:, idx])
77
+ assert_raises(IndexError, lambda: a[asarray([True, True]), idx])
78
+
79
+ # 1D integer array indices must have the same length
80
+ idx1 = asarray([0, 1])
81
+ idx2 = asarray([0, 1, 1])
82
+ assert_raises(IndexError, lambda: a[idx1, idx2])
83
+
84
+ # Non-integer array indices are not allowed
85
+ assert_raises(IndexError, lambda: a[ones(2), 0])
86
+
87
+ # Array-likes (lists, tuples) are not allowed as indices
77
88
  assert_raises(IndexError, lambda: a[[0, 1]])
78
89
  assert_raises(IndexError, lambda: a[(0, 1), (0, 1)])
79
90
  assert_raises(IndexError, lambda: a[[0, 1]])
@@ -87,6 +98,45 @@ def test_validate_index():
87
98
  assert_raises(IndexError, lambda: a[0,])
88
99
  assert_raises(IndexError, lambda: a[0])
89
100
  assert_raises(IndexError, lambda: a[:])
101
+ assert_raises(IndexError, lambda: a[idx])
102
+
103
+
104
+ def test_indexing_arrays():
105
+ # indexing with 1D integer arrays and mixes of integers and 1D integer are allowed
106
+
107
+ # 1D array
108
+ a = arange(5)
109
+ idx = asarray([1, 0, 1, 2, -1])
110
+ a_idx = a[idx]
111
+
112
+ a_idx_loop = asarray([a[idx[i]] for i in range(idx.shape[0])])
113
+ assert all(a_idx == a_idx_loop)
114
+
115
+ # setitem with arrays is not allowed
116
+ with assert_raises(IndexError):
117
+ a[idx] = 42
118
+
119
+ # mixed array and integer indexing
120
+ a = reshape(arange(3*4), (3, 4))
121
+ idx = asarray([1, 0, 1, 2, -1])
122
+ a_idx = a[idx, 1]
123
+
124
+ a_idx_loop = asarray([a[idx[i], 1] for i in range(idx.shape[0])])
125
+ assert all(a_idx == a_idx_loop)
126
+
127
+ # index with two arrays
128
+ a_idx = a[idx, idx]
129
+ a_idx_loop = asarray([a[idx[i], idx[i]] for i in range(idx.shape[0])])
130
+ assert all(a_idx == a_idx_loop)
131
+
132
+ # setitem with arrays is not allowed
133
+ with assert_raises(IndexError):
134
+ a[idx, idx] = 42
135
+
136
+ # smoke test indexing with ndim > 1 arrays
137
+ idx = idx[..., None]
138
+ a[idx, idx]
139
+
90
140
 
91
141
  def test_promoted_scalar_inherits_device():
92
142
  device1 = Device("device1")
@@ -96,12 +146,68 @@ def test_promoted_scalar_inherits_device():
96
146
 
97
147
  assert y.device == device1
98
148
 
149
+
150
+ BIG_INT = int(1e30)
151
+
152
+ def _check_op_array_scalar(dtypes, a, s, func, func_name, BIG_INT=BIG_INT):
153
+ # Test array op scalar. From the spec, the following combinations
154
+ # are supported:
155
+
156
+ # - Python bool for a bool array dtype,
157
+ # - a Python int within the bounds of the given dtype for integer array dtypes,
158
+ # - a Python int or float for real floating-point array dtypes
159
+ # - a Python int, float, or complex for complex floating-point array dtypes
160
+
161
+ # an exception: complex scalar <op> floating array
162
+ scalar_types_for_float = [float, int]
163
+ if not (func_name.startswith("__i")
164
+ or (func_name in ["__floordiv__", "__rfloordiv__", "__mod__", "__rmod__"]
165
+ and type(s) == complex)
166
+ ):
167
+ scalar_types_for_float += [complex]
168
+
169
+ if ((dtypes == "all"
170
+ or dtypes == "numeric" and a.dtype in _numeric_dtypes
171
+ or dtypes == "real numeric" and a.dtype in _real_numeric_dtypes
172
+ or dtypes == "integer" and a.dtype in _integer_dtypes
173
+ or dtypes == "integer or boolean" and a.dtype in _integer_or_boolean_dtypes
174
+ or dtypes == "boolean" and a.dtype in _boolean_dtypes
175
+ or dtypes == "floating-point" and a.dtype in _floating_dtypes
176
+ or dtypes == "real floating-point" and a.dtype in _real_floating_dtypes
177
+ )
178
+ # bool is a subtype of int, which is why we avoid
179
+ # isinstance here.
180
+ and (a.dtype in _boolean_dtypes and type(s) == bool
181
+ or a.dtype in _integer_dtypes and type(s) == int
182
+ or a.dtype in _real_floating_dtypes and type(s) in scalar_types_for_float
183
+ or a.dtype in _complex_floating_dtypes and type(s) in [complex, float, int]
184
+ )):
185
+ if a.dtype in _integer_dtypes and s == BIG_INT:
186
+ with assert_raises(OverflowError):
187
+ func(s)
188
+ return False
189
+
190
+ else:
191
+ # Only test for no error
192
+ with suppress_warnings() as sup:
193
+ # ignore warnings from pow(BIG_INT)
194
+ sup.filter(RuntimeWarning,
195
+ "invalid value encountered in power")
196
+ func(s)
197
+ return True
198
+
199
+ else:
200
+ with assert_raises(TypeError):
201
+ func(s)
202
+ return False
203
+
204
+
99
205
  def test_operators():
100
206
  # For every operator, we test that it works for the required type
101
207
  # combinations and raises TypeError otherwise
102
208
  binary_op_dtypes = {
103
209
  "__add__": "numeric",
104
- "__and__": "integer_or_boolean",
210
+ "__and__": "integer or boolean",
105
211
  "__eq__": "all",
106
212
  "__floordiv__": "real numeric",
107
213
  "__ge__": "real numeric",
@@ -112,12 +218,12 @@ def test_operators():
112
218
  "__mod__": "real numeric",
113
219
  "__mul__": "numeric",
114
220
  "__ne__": "all",
115
- "__or__": "integer_or_boolean",
221
+ "__or__": "integer or boolean",
116
222
  "__pow__": "numeric",
117
223
  "__rshift__": "integer",
118
224
  "__sub__": "numeric",
119
- "__truediv__": "floating",
120
- "__xor__": "integer_or_boolean",
225
+ "__truediv__": "floating-point",
226
+ "__xor__": "integer or boolean",
121
227
  }
122
228
  # Recompute each time because of in-place ops
123
229
  def _array_vals():
@@ -128,8 +234,6 @@ def test_operators():
128
234
  for d in _floating_dtypes:
129
235
  yield asarray(1.0, dtype=d)
130
236
 
131
-
132
- BIG_INT = int(1e30)
133
237
  for op, dtypes in binary_op_dtypes.items():
134
238
  ops = [op]
135
239
  if op not in ["__eq__", "__ne__", "__le__", "__ge__", "__lt__", "__gt__"]:
@@ -139,40 +243,7 @@ def test_operators():
139
243
  for s in [1, 1.0, 1j, BIG_INT, False]:
140
244
  for _op in ops:
141
245
  for a in _array_vals():
142
- # Test array op scalar. From the spec, the following combinations
143
- # are supported:
144
-
145
- # - Python bool for a bool array dtype,
146
- # - a Python int within the bounds of the given dtype for integer array dtypes,
147
- # - a Python int or float for real floating-point array dtypes
148
- # - a Python int, float, or complex for complex floating-point array dtypes
149
-
150
- if ((dtypes == "all"
151
- or dtypes == "numeric" and a.dtype in _numeric_dtypes
152
- or dtypes == "real numeric" and a.dtype in _real_numeric_dtypes
153
- or dtypes == "integer" and a.dtype in _integer_dtypes
154
- or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes
155
- or dtypes == "boolean" and a.dtype in _boolean_dtypes
156
- or dtypes == "floating" and a.dtype in _floating_dtypes
157
- )
158
- # bool is a subtype of int, which is why we avoid
159
- # isinstance here.
160
- and (a.dtype in _boolean_dtypes and type(s) == bool
161
- or a.dtype in _integer_dtypes and type(s) == int
162
- or a.dtype in _real_floating_dtypes and type(s) in [float, int]
163
- or a.dtype in _complex_floating_dtypes and type(s) in [complex, float, int]
164
- )):
165
- if a.dtype in _integer_dtypes and s == BIG_INT:
166
- assert_raises(OverflowError, lambda: getattr(a, _op)(s))
167
- else:
168
- # Only test for no error
169
- with suppress_warnings() as sup:
170
- # ignore warnings from pow(BIG_INT)
171
- sup.filter(RuntimeWarning,
172
- "invalid value encountered in power")
173
- getattr(a, _op)(s)
174
- else:
175
- assert_raises(TypeError, lambda: getattr(a, _op)(s))
246
+ _check_op_array_scalar(dtypes, a, s, getattr(a, _op), _op)
176
247
 
177
248
  # Test array op array.
178
249
  for _op in ops:
@@ -203,10 +274,10 @@ def test_operators():
203
274
  or (dtypes == "real numeric" and x.dtype in _real_numeric_dtypes and y.dtype in _real_numeric_dtypes)
204
275
  or (dtypes == "numeric" and x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes)
205
276
  or dtypes == "integer" and x.dtype in _integer_dtypes and y.dtype in _integer_dtypes
206
- or dtypes == "integer_or_boolean" and (x.dtype in _integer_dtypes and y.dtype in _integer_dtypes
277
+ or dtypes == "integer or boolean" and (x.dtype in _integer_dtypes and y.dtype in _integer_dtypes
207
278
  or x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes)
208
279
  or dtypes == "boolean" and x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes
209
- or dtypes == "floating" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes
280
+ or dtypes == "floating-point" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes
210
281
  ):
211
282
  getattr(x, _op)(y)
212
283
  else:
@@ -214,7 +285,7 @@ def test_operators():
214
285
 
215
286
  unary_op_dtypes = {
216
287
  "__abs__": "numeric",
217
- "__invert__": "integer_or_boolean",
288
+ "__invert__": "integer or boolean",
218
289
  "__neg__": "numeric",
219
290
  "__pos__": "numeric",
220
291
  }
@@ -223,7 +294,7 @@ def test_operators():
223
294
  if (
224
295
  dtypes == "numeric"
225
296
  and a.dtype in _numeric_dtypes
226
- or dtypes == "integer_or_boolean"
297
+ or dtypes == "integer or boolean"
227
298
  and a.dtype in _integer_or_boolean_dtypes
228
299
  ):
229
300
  # Only test for no error
@@ -438,10 +509,10 @@ def test_array_keys_use_private_array():
438
509
  def test_array_namespace():
439
510
  a = ones((3, 3))
440
511
  assert a.__array_namespace__() == array_api_strict
441
- assert array_api_strict.__array_api_version__ == "2023.12"
512
+ assert array_api_strict.__array_api_version__ == "2024.12"
442
513
 
443
514
  assert a.__array_namespace__(api_version=None) is array_api_strict
444
- assert array_api_strict.__array_api_version__ == "2023.12"
515
+ assert array_api_strict.__array_api_version__ == "2024.12"
445
516
 
446
517
  assert a.__array_namespace__(api_version="2022.12") is array_api_strict
447
518
  assert array_api_strict.__array_api_version__ == "2022.12"
@@ -454,11 +525,12 @@ def test_array_namespace():
454
525
  assert array_api_strict.__array_api_version__ == "2021.12"
455
526
 
456
527
  with pytest.warns(UserWarning):
457
- assert a.__array_namespace__(api_version="2024.12") is array_api_strict
458
- assert array_api_strict.__array_api_version__ == "2024.12"
528
+ assert a.__array_namespace__(api_version="2025.12") is array_api_strict
529
+ assert array_api_strict.__array_api_version__ == "2025.12"
530
+
459
531
 
460
532
  pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2021.11"))
461
- pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2025.12"))
533
+ pytest.raises(ValueError, lambda: a.__array_namespace__(api_version="2026.12"))
462
534
 
463
535
  def test_iter():
464
536
  pytest.raises(TypeError, lambda: iter(asarray(3)))
@@ -236,3 +236,10 @@ def from_dlpack_2023_12(api_version):
236
236
  pytest.raises(exception, lambda: from_dlpack(capsule, copy=False))
237
237
  pytest.raises(exception, lambda: from_dlpack(capsule, copy=True))
238
238
  pytest.raises(exception, lambda: from_dlpack(capsule, copy=None))
239
+
240
+
241
+ def test_from_dlpack_default_device():
242
+ x = asarray([1, 2, 3])
243
+ y = from_dlpack(x)
244
+ z = from_dlpack(np.asarray([1, 2, 3]))
245
+ assert x.device == y.device == z.device == CPU_DEVICE
@@ -6,9 +6,9 @@ from numpy.testing import assert_raises
6
6
  import numpy as np
7
7
 
8
8
  from .._creation_functions import asarray
9
- from .._data_type_functions import astype, can_cast, isdtype
9
+ from .._data_type_functions import astype, can_cast, isdtype, result_type
10
10
  from .._dtypes import (
11
- bool, int8, int16, uint8, float64,
11
+ bool, int8, int16, uint8, float64, int64
12
12
  )
13
13
  from .._flags import set_array_api_strict_flags
14
14
 
@@ -31,15 +31,17 @@ def test_can_cast(from_, to, expected):
31
31
  def test_isdtype_strictness():
32
32
  assert_raises(TypeError, lambda: isdtype(float64, 64))
33
33
  assert_raises(ValueError, lambda: isdtype(float64, 'f8'))
34
-
35
34
  assert_raises(TypeError, lambda: isdtype(float64, (('integral',),)))
35
+ assert_raises(TypeError, lambda: isdtype(float64, None))
36
+ assert_raises(TypeError, lambda: isdtype(np.float64, float64))
37
+ assert_raises(TypeError, lambda: isdtype(asarray(1.0), float64))
38
+
36
39
  with assert_raises(TypeError), warnings.catch_warnings(record=True) as w:
37
40
  warnings.simplefilter("always")
38
41
  isdtype(float64, np.object_)
39
42
  assert len(w) == 1
40
43
  assert issubclass(w[-1].category, UserWarning)
41
44
 
42
- assert_raises(TypeError, lambda: isdtype(float64, None))
43
45
  with assert_raises(TypeError), warnings.catch_warnings(record=True) as w:
44
46
  warnings.simplefilter("always")
45
47
  isdtype(float64, np.float64)
@@ -68,3 +70,21 @@ def astype_device(api_version):
68
70
  else:
69
71
  pytest.raises(TypeError, lambda: astype(a, int8, device=None))
70
72
  pytest.raises(TypeError, lambda: astype(a, int8, device=a.device))
73
+
74
+
75
+ @pytest.mark.parametrize("api_version", ['2023.12', '2024.12'])
76
+ def test_result_type_py_scalars(api_version):
77
+ if api_version <= '2023.12':
78
+ set_array_api_strict_flags(api_version=api_version)
79
+
80
+ with pytest.raises(TypeError):
81
+ result_type(int16, 3)
82
+ else:
83
+ set_array_api_strict_flags(api_version=api_version)
84
+
85
+ assert result_type(int8, 3) == int8
86
+ assert result_type(uint8, 3) == uint8
87
+ assert result_type(float64, 3) == float64
88
+
89
+ with pytest.raises(TypeError):
90
+ result_type(int64, True)
@@ -1,8 +1,8 @@
1
1
  from inspect import signature, getmodule
2
2
 
3
- from numpy.testing import assert_raises
3
+ from pytest import raises as assert_raises
4
+ from numpy.testing import suppress_warnings
4
5
 
5
- import pytest
6
6
 
7
7
  from .. import asarray, _elementwise_functions
8
8
  from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift
@@ -19,6 +19,8 @@ from .._dtypes import (
19
19
  )
20
20
  from .._flags import set_array_api_strict_flags
21
21
 
22
+ from .test_array_object import _check_op_array_scalar, BIG_INT
23
+
22
24
  import array_api_strict
23
25
 
24
26
 
@@ -49,7 +51,7 @@ elementwise_function_input_types = {
49
51
  "bitwise_xor": "integer or boolean",
50
52
  "ceil": "real numeric",
51
53
  "clip": "real numeric",
52
- "conj": "complex floating-point",
54
+ "conj": "numeric",
53
55
  "copysign": "real floating-point",
54
56
  "cos": "floating-point",
55
57
  "cosh": "floating-point",
@@ -85,7 +87,7 @@ elementwise_function_input_types = {
85
87
  "not_equal": "all",
86
88
  "positive": "numeric",
87
89
  "pow": "numeric",
88
- "real": "complex floating-point",
90
+ "real": "numeric",
89
91
  "reciprocal": "floating-point",
90
92
  "remainder": "real numeric",
91
93
  "round": "numeric",
@@ -120,6 +122,7 @@ def test_missing_functions():
120
122
  # Ensure the above dictionary is complete.
121
123
  import array_api_strict._elementwise_functions as mod
122
124
  mod_funcs = [n for n in dir(mod) if getmodule(getattr(mod, n)) is mod]
125
+ mod_funcs = [n for n in mod_funcs if not n.startswith("_")]
123
126
  assert set(mod_funcs) == set(elementwise_function_input_types)
124
127
 
125
128
 
@@ -130,8 +133,7 @@ def test_function_device_persists():
130
133
  yield asarray(1., dtype=d)
131
134
 
132
135
  # Use the latest version of the standard so all functions are included
133
- with pytest.warns(UserWarning):
134
- set_array_api_strict_flags(api_version="2024.12")
136
+ set_array_api_strict_flags(api_version="2024.12")
135
137
 
136
138
  for func_name, types in elementwise_function_input_types.items():
137
139
  dtypes = _dtype_categories[types]
@@ -167,8 +169,7 @@ def test_function_types():
167
169
  yield asarray(1.0, dtype=d)
168
170
 
169
171
  # Use the latest version of the standard so all functions are included
170
- with pytest.warns(UserWarning):
171
- set_array_api_strict_flags(api_version="2024.12")
172
+ set_array_api_strict_flags(api_version="2024.12")
172
173
 
173
174
  for x in _array_vals():
174
175
  for func_name, types in elementwise_function_input_types.items():
@@ -202,3 +203,60 @@ def test_bitwise_shift_error():
202
203
  assert_raises(
203
204
  ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))
204
205
  )
206
+
207
+
208
+
209
+ def test_scalars():
210
+ # mirror test_array_object.py::test_operators()
211
+ #
212
+ # Also check that binary functions accept (array, scalar) and (scalar, array)
213
+ # arguments, and reject (scalar, scalar) arguments.
214
+
215
+ # Use the latest version of the standard so that scalars are actually allowed
216
+ set_array_api_strict_flags(api_version="2024.12")
217
+
218
+ def _array_vals():
219
+ for d in _integer_dtypes:
220
+ yield asarray(1, dtype=d)
221
+ for d in _boolean_dtypes:
222
+ yield asarray(False, dtype=d)
223
+ for d in _floating_dtypes:
224
+ yield asarray(1.0, dtype=d)
225
+
226
+
227
+ for func_name, dtypes in elementwise_function_input_types.items():
228
+ func = getattr(_elementwise_functions, func_name)
229
+ if nargs(func) != 2:
230
+ continue
231
+
232
+ nocomplex = [
233
+ 'atan2', 'copysign', 'floor_divide', 'hypot', 'logaddexp', 'nextafter',
234
+ 'remainder',
235
+ 'greater', 'less', 'greater_equal', 'less_equal', 'maximum', 'minimum',
236
+ ]
237
+
238
+ for s in [1, 1.0, 1j, BIG_INT, False]:
239
+ for a in _array_vals():
240
+ for func1 in [lambda s: func(a, s), lambda s: func(s, a)]:
241
+
242
+ if func_name in nocomplex and type(s) == complex:
243
+ allowed = False
244
+ else:
245
+ allowed = _check_op_array_scalar(dtypes, a, s, func1, func_name)
246
+
247
+ # only check `func(array, scalar) == `func(array, array)` if
248
+ # the former is legal under the promotion rules
249
+ if allowed:
250
+ conv_scalar = a._promote_scalar(s)
251
+
252
+ with suppress_warnings() as sup:
253
+ # ignore warnings from pow(BIG_INT)
254
+ sup.filter(RuntimeWarning,
255
+ "invalid value encountered in power")
256
+ assert func(s, a) == func(conv_scalar, a)
257
+ assert func(a, s) == func(a, conv_scalar)
258
+
259
+ with assert_raises(TypeError):
260
+ func(s, s)
261
+
262
+
@@ -19,7 +19,7 @@ import pytest
19
19
  def test_flag_defaults():
20
20
  flags = get_array_api_strict_flags()
21
21
  assert flags == {
22
- 'api_version': '2023.12',
22
+ 'api_version': '2024.12',
23
23
  'boolean_indexing': True,
24
24
  'data_dependent_shapes': True,
25
25
  'enabled_extensions': ('linalg', 'fft'),
@@ -36,7 +36,7 @@ def test_reset_flags():
36
36
  reset_array_api_strict_flags()
37
37
  flags = get_array_api_strict_flags()
38
38
  assert flags == {
39
- 'api_version': '2023.12',
39
+ 'api_version': '2024.12',
40
40
  'boolean_indexing': True,
41
41
  'data_dependent_shapes': True,
42
42
  'enabled_extensions': ('linalg', 'fft'),
@@ -47,7 +47,7 @@ def test_setting_flags():
47
47
  set_array_api_strict_flags(data_dependent_shapes=False)
48
48
  flags = get_array_api_strict_flags()
49
49
  assert flags == {
50
- 'api_version': '2023.12',
50
+ 'api_version': '2024.12',
51
51
  'boolean_indexing': True,
52
52
  'data_dependent_shapes': False,
53
53
  'enabled_extensions': ('linalg', 'fft'),
@@ -55,7 +55,7 @@ def test_setting_flags():
55
55
  set_array_api_strict_flags(enabled_extensions=('fft',))
56
56
  flags = get_array_api_strict_flags()
57
57
  assert flags == {
58
- 'api_version': '2023.12',
58
+ 'api_version': '2024.12',
59
59
  'boolean_indexing': True,
60
60
  'data_dependent_shapes': False,
61
61
  'enabled_extensions': ('fft',),
@@ -98,15 +98,26 @@ def test_flags_api_version_2023_12():
98
98
  }
99
99
 
100
100
  def test_flags_api_version_2024_12():
101
- # Make sure setting the version to 2024.12 issues a warning.
101
+ set_array_api_strict_flags(api_version='2024.12')
102
+ flags = get_array_api_strict_flags()
103
+ assert flags == {
104
+ 'api_version': '2024.12',
105
+ 'boolean_indexing': True,
106
+ 'data_dependent_shapes': True,
107
+ 'enabled_extensions': ('linalg', 'fft'),
108
+ }
109
+
110
+
111
+ def test_flags_api_version_2025_12():
112
+ # Make sure setting the version to 2025.12 issues a warning.
102
113
  with pytest.warns(UserWarning) as record:
103
- set_array_api_strict_flags(api_version='2024.12')
114
+ set_array_api_strict_flags(api_version='2025.12')
104
115
  assert len(record) == 1
105
- assert '2024.12' in str(record[0].message)
116
+ assert '2025.12' in str(record[0].message)
106
117
  assert 'draft' in str(record[0].message)
107
118
  flags = get_array_api_strict_flags()
108
119
  assert flags == {
109
- 'api_version': '2024.12',
120
+ 'api_version': '2025.12',
110
121
  'boolean_indexing': True,
111
122
  'data_dependent_shapes': True,
112
123
  'enabled_extensions': ('linalg', 'fft'),
@@ -125,9 +136,12 @@ def test_setting_flags_invalid():
125
136
 
126
137
  def test_api_version():
127
138
  # Test defaults
128
- assert xp.__array_api_version__ == '2023.12'
139
+ assert xp.__array_api_version__ == '2024.12'
129
140
 
130
141
  # Test setting the version
142
+ set_array_api_strict_flags(api_version='2023.12')
143
+ assert xp.__array_api_version__ == '2023.12'
144
+
131
145
  set_array_api_strict_flags(api_version='2022.12')
132
146
  assert xp.__array_api_version__ == '2022.12'
133
147
 
@@ -307,14 +321,16 @@ api_version_2024_12_examples = {
307
321
  'reciprocal': lambda: xp.reciprocal(xp.asarray([2.])),
308
322
  'take_along_axis': lambda: xp.take_along_axis(xp.zeros((2, 3)),
309
323
  xp.zeros((1, 4), dtype=xp.int64)),
324
+ 'count_nonzero': lambda: xp.count_nonzero(xp.arange(3)),
325
+ 'cumulative_prod': lambda: xp.cumulative_prod(xp.arange(1, 5)),
310
326
  }
311
327
 
312
328
  @pytest.mark.parametrize('func_name', api_version_2024_12_examples.keys())
313
329
  def test_api_version_2024_12(func_name):
314
330
  func = api_version_2024_12_examples[func_name]
315
331
 
316
- # By default, these functions should error
317
- pytest.raises(RuntimeError, func)
332
+ # By default, these functions should not error
333
+ func()
318
334
 
319
335
  # In 2022.12 and 2023.12, these functions should error
320
336
  set_array_api_strict_flags(api_version='2022.12')
@@ -322,11 +338,6 @@ def test_api_version_2024_12(func_name):
322
338
  set_array_api_strict_flags(api_version='2023.12')
323
339
  pytest.raises(RuntimeError, func)
324
340
 
325
- # They should not error in 2024.12
326
- with pytest.warns(UserWarning):
327
- set_array_api_strict_flags(api_version='2024.12')
328
- func()
329
-
330
341
  # Test the behavior gets updated properly
331
342
  set_array_api_strict_flags(api_version='2023.12')
332
343
  pytest.raises(RuntimeError, func)
@@ -433,9 +444,9 @@ def test_environment_variables():
433
444
  # ARRAY_API_STRICT_API_VERSION
434
445
  ('''\
435
446
  import array_api_strict as xp
436
- assert xp.__array_api_version__ == '2023.12'
447
+ assert xp.__array_api_version__ == '2024.12'
437
448
 
438
- assert xp.get_array_api_strict_flags()['api_version'] == '2023.12'
449
+ assert xp.get_array_api_strict_flags()['api_version'] == '2024.12'
439
450
 
440
451
  ''', {}),
441
452
  *[
@@ -11,7 +11,7 @@ from .._manipulation_functions import (
11
11
 
12
12
 
13
13
  def test_concat_errors():
14
- assert_raises(TypeError, lambda: concat((1, 1), axis=None))
14
+ assert_raises((TypeError, ValueError), lambda: concat((1, 1), axis=None))
15
15
  assert_raises(TypeError, lambda: concat([asarray([1], dtype=int8),
16
16
  asarray([1], dtype=float64)]))
17
17
 
@@ -0,0 +1,24 @@
1
+ import pytest
2
+
3
+ import array_api_strict as xp
4
+
5
+ from array_api_strict import ArrayAPIStrictFlags
6
+
7
+
8
+ def test_where_with_scalars():
9
+ x = xp.asarray([1, 2, 3, 1])
10
+
11
+ # Versions up to and including 2023.12 don't support scalar arguments
12
+ with ArrayAPIStrictFlags(api_version='2023.12'):
13
+ with pytest.raises(AttributeError, match="object has no attribute 'dtype'"):
14
+ xp.where(x == 1, 42, 44)
15
+
16
+ # Versions after 2023.12 support scalar arguments
17
+ x_where = xp.where(x == 1, xp.asarray(42), 44)
18
+
19
+ expected = xp.asarray([42, 44, 44, 42])
20
+ assert xp.all(x_where == expected)
21
+
22
+ # The spec does not allow both x1 and x2 to be scalars
23
+ with pytest.raises(ValueError, match="One of"):
24
+ xp.where(x == 1, 42, 44)