array-api-strict 2.3__py3-none-any.whl → 2.4__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 (35) hide show
  1. array_api_strict/__init__.py +11 -5
  2. array_api_strict/_array_object.py +226 -200
  3. array_api_strict/_constants.py +1 -1
  4. array_api_strict/_creation_functions.py +97 -119
  5. array_api_strict/_data_type_functions.py +73 -64
  6. array_api_strict/_dtypes.py +35 -24
  7. array_api_strict/_elementwise_functions.py +138 -482
  8. array_api_strict/_fft.py +28 -32
  9. array_api_strict/_flags.py +65 -26
  10. array_api_strict/_helpers.py +23 -14
  11. array_api_strict/_indexing_functions.py +3 -8
  12. array_api_strict/_info.py +64 -69
  13. array_api_strict/_linalg.py +57 -53
  14. array_api_strict/_linear_algebra_functions.py +11 -9
  15. array_api_strict/_manipulation_functions.py +30 -30
  16. array_api_strict/_searching_functions.py +22 -33
  17. array_api_strict/_set_functions.py +4 -6
  18. array_api_strict/_sorting_functions.py +6 -6
  19. array_api_strict/_statistical_functions.py +57 -64
  20. array_api_strict/_typing.py +30 -50
  21. array_api_strict/_utility_functions.py +11 -13
  22. array_api_strict/_version.py +2 -2
  23. array_api_strict/py.typed +0 -0
  24. array_api_strict/tests/test_array_object.py +163 -56
  25. array_api_strict/tests/test_creation_functions.py +14 -12
  26. array_api_strict/tests/test_data_type_functions.py +40 -6
  27. array_api_strict/tests/test_elementwise_functions.py +94 -45
  28. array_api_strict/tests/test_searching_functions.py +79 -1
  29. array_api_strict/tests/test_validation.py +1 -3
  30. {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/METADATA +5 -4
  31. array_api_strict-2.4.dist-info/RECORD +44 -0
  32. {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/WHEEL +1 -1
  33. array_api_strict-2.3.dist-info/RECORD +0 -43
  34. {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/LICENSE +0 -0
  35. {array_api_strict-2.3.dist-info → array_api_strict-2.4.dist-info}/top_level.txt +0 -0
@@ -4,4 +4,4 @@ e = np.e
4
4
  inf = np.inf
5
5
  nan = np.nan
6
6
  pi = np.pi
7
- newaxis = np.newaxis
7
+ newaxis: None = np.newaxis
@@ -1,51 +1,47 @@
1
1
  from __future__ import annotations
2
2
 
3
- from contextlib import contextmanager
4
- from typing import TYPE_CHECKING, List, Optional, Tuple, Union
3
+ from enum import Enum
4
+ from typing import TYPE_CHECKING, Literal
5
5
 
6
- if TYPE_CHECKING:
7
- from ._typing import (
8
- Array,
9
- Device,
10
- Dtype,
11
- NestedSequence,
12
- SupportsBufferProtocol,
13
- )
14
- from ._dtypes import _DType, _all_dtypes
6
+ import numpy as np
7
+
8
+ from ._dtypes import DType, _all_dtypes, _np_dtype
15
9
  from ._flags import get_array_api_strict_flags
10
+ from ._typing import NestedSequence, SupportsBufferProtocol, SupportsDLPack
16
11
 
17
- import numpy as np
12
+ if TYPE_CHECKING:
13
+ # TODO import from typing (requires Python >=3.13)
14
+ from typing_extensions import TypeIs
18
15
 
19
- @contextmanager
20
- def allow_array():
21
- """
22
- Temporarily enable Array.__array__. This is needed for np.array to parse
23
- list of lists of Array objects.
24
- """
25
- from . import _array_object
26
- original_value = _array_object._allow_array
27
- try:
28
- _array_object._allow_array = True
29
- yield
30
- finally:
31
- _array_object._allow_array = original_value
16
+ # Circular import
17
+ from ._array_object import Array, Device
32
18
 
33
- def _check_valid_dtype(dtype):
19
+
20
+ class Undef(Enum):
21
+ UNDEF = 0
22
+
23
+
24
+ _undef = Undef.UNDEF
25
+
26
+
27
+ def _check_valid_dtype(dtype: DType | None) -> None:
34
28
  # Note: Only spelling dtypes as the dtype objects is supported.
35
29
  if dtype not in (None,) + _all_dtypes:
36
30
  raise ValueError(f"dtype must be one of the supported dtypes, got {dtype!r}")
37
31
 
38
- def _supports_buffer_protocol(obj):
32
+
33
+ def _supports_buffer_protocol(obj: object) -> TypeIs[SupportsBufferProtocol]:
39
34
  try:
40
- memoryview(obj)
35
+ memoryview(obj) # type: ignore[arg-type]
41
36
  except TypeError:
42
37
  return False
43
38
  return True
44
39
 
45
- def _check_device(device):
40
+
41
+ def _check_device(device: Device | None) -> None:
46
42
  # _array_object imports in this file are inside the functions to avoid
47
43
  # circular imports
48
- from ._array_object import Device, ALL_DEVICES
44
+ from ._array_object import ALL_DEVICES, Device
49
45
 
50
46
  if device is not None and not isinstance(device, Device):
51
47
  raise ValueError(f"Unsupported device {device!r}")
@@ -53,20 +49,14 @@ def _check_device(device):
53
49
  if device is not None and device not in ALL_DEVICES:
54
50
  raise ValueError(f"Unsupported device {device!r}")
55
51
 
52
+
56
53
  def asarray(
57
- obj: Union[
58
- Array,
59
- bool,
60
- int,
61
- float,
62
- NestedSequence[bool | int | float],
63
- SupportsBufferProtocol,
64
- ],
54
+ obj: Array | complex | NestedSequence[complex] | SupportsBufferProtocol,
65
55
  /,
66
56
  *,
67
- dtype: Optional[Dtype] = None,
68
- device: Optional[Device] = None,
69
- copy: Optional[bool] = None,
57
+ dtype: DType | None = None,
58
+ device: Device | None = None,
59
+ copy: bool | None = None,
70
60
  ) -> Array:
71
61
  """
72
62
  Array API compatible wrapper for :py:func:`np.asarray <numpy.asarray>`.
@@ -108,23 +98,27 @@ def asarray(
108
98
 
109
99
  if isinstance(obj, Array):
110
100
  return Array._new(np.array(obj._array, copy=copy, dtype=_np_dtype), device=device)
101
+ elif isinstance(obj, list | tuple):
102
+ if any(isinstance(x, Array) for x in obj):
103
+ raise TypeError("Nested Arrays are not allowed. Use `stack` instead.")
104
+
111
105
  if dtype is None and isinstance(obj, int) and (obj > 2 ** 64 or obj < -(2 ** 63)):
112
106
  # Give a better error message in this case. NumPy would convert this
113
107
  # to an object array. TODO: This won't handle large integers in lists.
114
108
  raise OverflowError("Integer out of bounds for array dtypes")
115
- with allow_array():
116
- res = np.array(obj, dtype=_np_dtype, copy=copy)
109
+
110
+ res = np.array(obj, dtype=_np_dtype, copy=copy)
117
111
  return Array._new(res, device=device)
118
112
 
119
113
 
120
114
  def arange(
121
- start: Union[int, float],
115
+ start: float,
122
116
  /,
123
- stop: Optional[Union[int, float]] = None,
124
- step: Union[int, float] = 1,
117
+ stop: float | None = None,
118
+ step: float = 1,
125
119
  *,
126
- dtype: Optional[Dtype] = None,
127
- device: Optional[Device] = None,
120
+ dtype: DType | None = None,
121
+ device: Device | None = None,
128
122
  ) -> Array:
129
123
  """
130
124
  Array API compatible wrapper for :py:func:`np.arange <numpy.arange>`.
@@ -136,16 +130,17 @@ def arange(
136
130
  _check_valid_dtype(dtype)
137
131
  _check_device(device)
138
132
 
139
- if dtype is not None:
140
- dtype = dtype._np_dtype
141
- return Array._new(np.arange(start, stop=stop, step=step, dtype=dtype), device=device)
133
+ return Array._new(
134
+ np.arange(start, stop, step, dtype=_np_dtype(dtype)),
135
+ device=device,
136
+ )
142
137
 
143
138
 
144
139
  def empty(
145
- shape: Union[int, Tuple[int, ...]],
140
+ shape: int | tuple[int, ...],
146
141
  *,
147
- dtype: Optional[Dtype] = None,
148
- device: Optional[Device] = None,
142
+ dtype: DType | None = None,
143
+ device: Device | None = None,
149
144
  ) -> Array:
150
145
  """
151
146
  Array API compatible wrapper for :py:func:`np.empty <numpy.empty>`.
@@ -157,13 +152,11 @@ def empty(
157
152
  _check_valid_dtype(dtype)
158
153
  _check_device(device)
159
154
 
160
- if dtype is not None:
161
- dtype = dtype._np_dtype
162
- return Array._new(np.empty(shape, dtype=dtype), device=device)
155
+ return Array._new(np.empty(shape, dtype=_np_dtype(dtype)), device=device)
163
156
 
164
157
 
165
158
  def empty_like(
166
- x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None
159
+ x: Array, /, *, dtype: DType | None = None, device: Device | None = None
167
160
  ) -> Array:
168
161
  """
169
162
  Array API compatible wrapper for :py:func:`np.empty_like <numpy.empty_like>`.
@@ -177,19 +170,17 @@ def empty_like(
177
170
  if device is None:
178
171
  device = x.device
179
172
 
180
- if dtype is not None:
181
- dtype = dtype._np_dtype
182
- return Array._new(np.empty_like(x._array, dtype=dtype), device=device)
173
+ return Array._new(np.empty_like(x._array, dtype=_np_dtype(dtype)), device=device)
183
174
 
184
175
 
185
176
  def eye(
186
177
  n_rows: int,
187
- n_cols: Optional[int] = None,
178
+ n_cols: int | None = None,
188
179
  /,
189
180
  *,
190
181
  k: int = 0,
191
- dtype: Optional[Dtype] = None,
192
- device: Optional[Device] = None,
182
+ dtype: DType | None = None,
183
+ device: Device | None = None,
193
184
  ) -> Array:
194
185
  """
195
186
  Array API compatible wrapper for :py:func:`np.eye <numpy.eye>`.
@@ -201,45 +192,43 @@ def eye(
201
192
  _check_valid_dtype(dtype)
202
193
  _check_device(device)
203
194
 
204
- if dtype is not None:
205
- dtype = dtype._np_dtype
206
- return Array._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype), device=device)
207
-
195
+ return Array._new(
196
+ np.eye(n_rows, M=n_cols, k=k, dtype=_np_dtype(dtype)), device=device
197
+ )
208
198
 
209
- _default = object()
210
199
 
211
200
  def from_dlpack(
212
- x: object,
201
+ x: SupportsDLPack,
213
202
  /,
214
203
  *,
215
- device: Optional[Device] = _default,
216
- copy: Optional[bool] = _default,
204
+ device: Device | Undef | None = _undef,
205
+ copy: bool | Undef | None = _undef,
217
206
  ) -> Array:
218
207
  from ._array_object import Array
219
208
 
220
209
  if get_array_api_strict_flags()['api_version'] < '2023.12':
221
- if device is not _default:
210
+ if device is not _undef:
222
211
  raise ValueError("The device argument to from_dlpack requires at least version 2023.12 of the array API")
223
- if copy is not _default:
212
+ if copy is not _undef:
224
213
  raise ValueError("The copy argument to from_dlpack requires at least version 2023.12 of the array API")
225
214
 
226
215
  # Going to wait for upstream numpy support
227
- if device is not _default:
216
+ if device is not _undef:
228
217
  _check_device(device)
229
218
  else:
230
219
  device = None
231
- if copy not in [_default, None]:
220
+ if copy not in [_undef, None]:
232
221
  raise NotImplementedError("The copy argument to from_dlpack is not yet implemented")
233
222
 
234
223
  return Array._new(np.from_dlpack(x), device=device)
235
224
 
236
225
 
237
226
  def full(
238
- shape: Union[int, Tuple[int, ...]],
239
- fill_value: Union[int, float],
227
+ shape: int | tuple[int, ...],
228
+ fill_value: complex,
240
229
  *,
241
- dtype: Optional[Dtype] = None,
242
- device: Optional[Device] = None,
230
+ dtype: DType | None = None,
231
+ device: Device | None = None,
243
232
  ) -> Array:
244
233
  """
245
234
  Array API compatible wrapper for :py:func:`np.full <numpy.full>`.
@@ -253,10 +242,8 @@ def full(
253
242
 
254
243
  if isinstance(fill_value, Array) and fill_value.ndim == 0:
255
244
  fill_value = fill_value._array
256
- if dtype is not None:
257
- dtype = dtype._np_dtype
258
- res = np.full(shape, fill_value, dtype=dtype)
259
- if _DType(res.dtype) not in _all_dtypes:
245
+ res = np.full(shape, fill_value, dtype=_np_dtype(dtype))
246
+ if DType(res.dtype) not in _all_dtypes:
260
247
  # This will happen if the fill value is not something that NumPy
261
248
  # coerces to one of the acceptable dtypes.
262
249
  raise TypeError("Invalid input to full")
@@ -266,10 +253,10 @@ def full(
266
253
  def full_like(
267
254
  x: Array,
268
255
  /,
269
- fill_value: Union[int, float],
256
+ fill_value: complex,
270
257
  *,
271
- dtype: Optional[Dtype] = None,
272
- device: Optional[Device] = None,
258
+ dtype: DType | None = None,
259
+ device: Device | None = None,
273
260
  ) -> Array:
274
261
  """
275
262
  Array API compatible wrapper for :py:func:`np.full_like <numpy.full_like>`.
@@ -283,10 +270,8 @@ def full_like(
283
270
  if device is None:
284
271
  device = x.device
285
272
 
286
- if dtype is not None:
287
- dtype = dtype._np_dtype
288
- res = np.full_like(x._array, fill_value, dtype=dtype)
289
- if _DType(res.dtype) not in _all_dtypes:
273
+ res = np.full_like(x._array, fill_value, dtype=_np_dtype(dtype))
274
+ if DType(res.dtype) not in _all_dtypes:
290
275
  # This will happen if the fill value is not something that NumPy
291
276
  # coerces to one of the acceptable dtypes.
292
277
  raise TypeError("Invalid input to full_like")
@@ -294,13 +279,13 @@ def full_like(
294
279
 
295
280
 
296
281
  def linspace(
297
- start: Union[int, float],
298
- stop: Union[int, float],
282
+ start: complex,
283
+ stop: complex,
299
284
  /,
300
285
  num: int,
301
286
  *,
302
- dtype: Optional[Dtype] = None,
303
- device: Optional[Device] = None,
287
+ dtype: DType | None = None,
288
+ device: Device | None = None,
304
289
  endpoint: bool = True,
305
290
  ) -> Array:
306
291
  """
@@ -313,12 +298,13 @@ def linspace(
313
298
  _check_valid_dtype(dtype)
314
299
  _check_device(device)
315
300
 
316
- if dtype is not None:
317
- dtype = dtype._np_dtype
318
- return Array._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint), device=device)
301
+ return Array._new(
302
+ np.linspace(start, stop, num, dtype=_np_dtype(dtype), endpoint=endpoint),
303
+ device=device,
304
+ )
319
305
 
320
306
 
321
- def meshgrid(*arrays: Array, indexing: str = "xy") -> List[Array]:
307
+ def meshgrid(*arrays: Array, indexing: Literal["xy", "ij"] = "xy") -> list[Array]:
322
308
  """
323
309
  Array API compatible wrapper for :py:func:`np.meshgrid <numpy.meshgrid>`.
324
310
 
@@ -348,10 +334,10 @@ def meshgrid(*arrays: Array, indexing: str = "xy") -> List[Array]:
348
334
 
349
335
 
350
336
  def ones(
351
- shape: Union[int, Tuple[int, ...]],
337
+ shape: int | tuple[int, ...],
352
338
  *,
353
- dtype: Optional[Dtype] = None,
354
- device: Optional[Device] = None,
339
+ dtype: DType | None = None,
340
+ device: Device | None = None,
355
341
  ) -> Array:
356
342
  """
357
343
  Array API compatible wrapper for :py:func:`np.ones <numpy.ones>`.
@@ -363,13 +349,11 @@ def ones(
363
349
  _check_valid_dtype(dtype)
364
350
  _check_device(device)
365
351
 
366
- if dtype is not None:
367
- dtype = dtype._np_dtype
368
- return Array._new(np.ones(shape, dtype=dtype), device=device)
352
+ return Array._new(np.ones(shape, dtype=_np_dtype(dtype)), device=device)
369
353
 
370
354
 
371
355
  def ones_like(
372
- x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None
356
+ x: Array, /, *, dtype: DType | None = None, device: Device | None = None
373
357
  ) -> Array:
374
358
  """
375
359
  Array API compatible wrapper for :py:func:`np.ones_like <numpy.ones_like>`.
@@ -383,9 +367,7 @@ def ones_like(
383
367
  if device is None:
384
368
  device = x.device
385
369
 
386
- if dtype is not None:
387
- dtype = dtype._np_dtype
388
- return Array._new(np.ones_like(x._array, dtype=dtype), device=device)
370
+ return Array._new(np.ones_like(x._array, dtype=_np_dtype(dtype)), device=device)
389
371
 
390
372
 
391
373
  def tril(x: Array, /, *, k: int = 0) -> Array:
@@ -417,10 +399,10 @@ def triu(x: Array, /, *, k: int = 0) -> Array:
417
399
 
418
400
 
419
401
  def zeros(
420
- shape: Union[int, Tuple[int, ...]],
402
+ shape: int | tuple[int, ...],
421
403
  *,
422
- dtype: Optional[Dtype] = None,
423
- device: Optional[Device] = None,
404
+ dtype: DType | None = None,
405
+ device: Device | None = None,
424
406
  ) -> Array:
425
407
  """
426
408
  Array API compatible wrapper for :py:func:`np.zeros <numpy.zeros>`.
@@ -432,13 +414,11 @@ def zeros(
432
414
  _check_valid_dtype(dtype)
433
415
  _check_device(device)
434
416
 
435
- if dtype is not None:
436
- dtype = dtype._np_dtype
437
- return Array._new(np.zeros(shape, dtype=dtype), device=device)
417
+ return Array._new(np.zeros(shape, dtype=_np_dtype(dtype)), device=device)
438
418
 
439
419
 
440
420
  def zeros_like(
441
- x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None
421
+ x: Array, /, *, dtype: DType | None = None, device: Device | None = None
442
422
  ) -> Array:
443
423
  """
444
424
  Array API compatible wrapper for :py:func:`np.zeros_like <numpy.zeros_like>`.
@@ -452,6 +432,4 @@ def zeros_like(
452
432
  if device is None:
453
433
  device = x.device
454
434
 
455
- if dtype is not None:
456
- dtype = dtype._np_dtype
457
- return Array._new(np.zeros_like(x._array, dtype=dtype), device=device)
435
+ return Array._new(np.zeros_like(x._array, dtype=_np_dtype(dtype)), device=device)
@@ -1,38 +1,35 @@
1
- from __future__ import annotations
1
+ from dataclasses import dataclass
2
+
3
+ import numpy as np
2
4
 
3
- from ._array_object import Array
4
- from ._creation_functions import _check_device
5
+ from ._array_object import Array, Device
6
+ from ._creation_functions import Undef, _check_device, _undef
5
7
  from ._dtypes import (
6
- _DType,
8
+ DType,
7
9
  _all_dtypes,
8
10
  _boolean_dtypes,
9
- _signed_integer_dtypes,
10
- _unsigned_integer_dtypes,
11
- _integer_dtypes,
12
- _real_floating_dtypes,
13
11
  _complex_floating_dtypes,
12
+ _integer_dtypes,
14
13
  _numeric_dtypes,
14
+ _real_floating_dtypes,
15
15
  _result_type,
16
+ _signed_integer_dtypes,
17
+ _unsigned_integer_dtypes,
16
18
  )
17
19
  from ._flags import get_array_api_strict_flags
18
20
 
19
- from dataclasses import dataclass
20
- from typing import TYPE_CHECKING
21
-
22
- if TYPE_CHECKING:
23
- from typing import List, Tuple, Union, Optional
24
- from ._typing import Dtype, Device
25
-
26
- import numpy as np
27
-
28
- # Use to emulate the asarray(device) argument not existing in 2022.12
29
- _default = object()
30
21
 
31
22
  # Note: astype is a function, not an array method as in NumPy.
32
23
  def astype(
33
- x: Array, dtype: Dtype, /, *, copy: bool = True, device: Optional[Device] = _default
24
+ x: Array,
25
+ dtype: DType,
26
+ /,
27
+ *,
28
+ copy: bool = True,
29
+ # _default is used to emulate the device argument not existing in 2022.12
30
+ device: Device | Undef | None = _undef,
34
31
  ) -> Array:
35
- if device is not _default:
32
+ if device is not _undef:
36
33
  if get_array_api_strict_flags()['api_version'] >= '2023.12':
37
34
  _check_device(device)
38
35
  else:
@@ -52,7 +49,7 @@ def astype(
52
49
  return Array._new(x._array.astype(dtype=dtype._np_dtype, copy=copy), device=device)
53
50
 
54
51
 
55
- def broadcast_arrays(*arrays: Array) -> List[Array]:
52
+ def broadcast_arrays(*arrays: Array) -> list[Array]:
56
53
  """
57
54
  Array API compatible wrapper for :py:func:`np.broadcast_arrays <numpy.broadcast_arrays>`.
58
55
 
@@ -65,7 +62,7 @@ def broadcast_arrays(*arrays: Array) -> List[Array]:
65
62
  ]
66
63
 
67
64
 
68
- def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array:
65
+ def broadcast_to(x: Array, /, shape: tuple[int, ...]) -> Array:
69
66
  """
70
67
  Array API compatible wrapper for :py:func:`np.broadcast_to <numpy.broadcast_to>`.
71
68
 
@@ -76,7 +73,7 @@ def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array:
76
73
  return Array._new(np.broadcast_to(x._array, shape), device=x.device)
77
74
 
78
75
 
79
- def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool:
76
+ def can_cast(from_: DType | Array, to: DType, /) -> bool:
80
77
  """
81
78
  Array API compatible wrapper for :py:func:`np.can_cast <numpy.can_cast>`.
82
79
 
@@ -103,7 +100,9 @@ def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool:
103
100
 
104
101
  # These are internal objects for the return types of finfo and iinfo, since
105
102
  # the NumPy versions contain extra data that isn't part of the spec.
106
- @dataclass
103
+ # There should be no expectation for them to be comparable, hashable, or writeable.
104
+
105
+ @dataclass(frozen=True, eq=False)
107
106
  class finfo_object:
108
107
  bits: int
109
108
  # Note: The types of the float data here are float, whereas in NumPy they
@@ -112,26 +111,35 @@ class finfo_object:
112
111
  max: float
113
112
  min: float
114
113
  smallest_normal: float
115
- dtype: Dtype
114
+ dtype: DType
115
+
116
+ __hash__ = NotImplemented
116
117
 
117
118
 
118
- @dataclass
119
+ @dataclass(frozen=True, eq=False)
119
120
  class iinfo_object:
120
121
  bits: int
121
122
  max: int
122
123
  min: int
123
- dtype: Dtype
124
+ dtype: DType
124
125
 
126
+ __hash__ = NotImplemented
125
127
 
126
- def finfo(type: Union[Dtype, Array], /) -> finfo_object:
128
+
129
+ def finfo(type: DType | Array, /) -> finfo_object:
127
130
  """
128
131
  Array API compatible wrapper for :py:func:`np.finfo <numpy.finfo>`.
129
132
 
130
133
  See its docstring for more information.
131
134
  """
132
- if isinstance(type, _DType):
133
- type = type._np_dtype
134
- fi = np.finfo(type)
135
+ if isinstance(type, Array):
136
+ np_type = type._dtype._np_dtype
137
+ elif isinstance(type, DType):
138
+ np_type = type._np_dtype
139
+ else:
140
+ raise TypeError(f"'type' must be a dtype or array, not {type!r}")
141
+
142
+ fi = np.finfo(np_type)
135
143
  # Note: The types of the float data here are float, whereas in NumPy they
136
144
  # are scalars of the corresponding float dtype.
137
145
  return finfo_object(
@@ -140,35 +148,39 @@ def finfo(type: Union[Dtype, Array], /) -> finfo_object:
140
148
  float(fi.max),
141
149
  float(fi.min),
142
150
  float(fi.smallest_normal),
143
- fi.dtype,
151
+ DType(fi.dtype),
144
152
  )
145
153
 
146
154
 
147
- def iinfo(type: Union[Dtype, Array], /) -> iinfo_object:
155
+ def iinfo(type: DType | Array, /) -> iinfo_object:
148
156
  """
149
157
  Array API compatible wrapper for :py:func:`np.iinfo <numpy.iinfo>`.
150
158
 
151
159
  See its docstring for more information.
152
160
  """
153
- if isinstance(type, _DType):
154
- type = type._np_dtype
155
- ii = np.iinfo(type)
156
- return iinfo_object(ii.bits, ii.max, ii.min, ii.dtype)
161
+ if isinstance(type, Array):
162
+ np_type = type._dtype._np_dtype
163
+ elif isinstance(type, DType):
164
+ np_type = type._np_dtype
165
+ else:
166
+ raise TypeError(f"'type' must be a dtype or array, not {type!r}")
167
+
168
+ ii = np.iinfo(np_type)
169
+ return iinfo_object(ii.bits, ii.max, ii.min, DType(ii.dtype))
157
170
 
158
171
 
159
172
  # Note: isdtype is a new function from the 2022.12 array API specification.
160
- def isdtype(
161
- dtype: Dtype, kind: Union[Dtype, str, Tuple[Union[Dtype, str], ...]]
162
- ) -> bool:
173
+ def isdtype(dtype: DType, kind: DType | str | tuple[DType | str, ...]) -> bool:
163
174
  """
164
- Returns a boolean indicating whether a provided dtype is of a specified data type ``kind``.
175
+ Returns a boolean indicating whether a provided dtype is of a specified
176
+ data type ``kind``.
165
177
 
166
178
  See
167
179
  https://data-apis.org/array-api/latest/API_specification/generated/array_api.isdtype.html
168
180
  for more details
169
181
  """
170
- if not isinstance(dtype, _DType):
171
- raise TypeError(f"'dtype' must be a dtype, not a {type(dtype)!r}")
182
+ if not isinstance(dtype, DType):
183
+ raise TypeError(f"'dtype' must be a dtype, not a {type(dtype)!r}")
172
184
 
173
185
  if isinstance(kind, tuple):
174
186
  # Disallow nested tuples
@@ -197,7 +209,8 @@ def isdtype(
197
209
  else:
198
210
  raise TypeError(f"'kind' must be a dtype, str, or tuple of dtypes and strs, not {type(kind).__name__}")
199
211
 
200
- def result_type(*arrays_and_dtypes: Union[Array, Dtype, int, float, complex, bool]) -> Dtype:
212
+
213
+ def result_type(*arrays_and_dtypes: Array | DType | complex) -> DType:
201
214
  """
202
215
  Array API compatible wrapper for :py:func:`np.result_type <numpy.result_type>`.
203
216
 
@@ -207,35 +220,31 @@ def result_type(*arrays_and_dtypes: Union[Array, Dtype, int, float, complex, boo
207
220
  # required by the spec rather than using np.result_type. NumPy implements
208
221
  # too many extra type promotions like int64 + uint64 -> float64, and does
209
222
  # value-based casting on scalar arrays.
210
- A = []
223
+ dtypes = []
211
224
  scalars = []
212
225
  for a in arrays_and_dtypes:
213
- if isinstance(a, Array):
214
- a = a.dtype
226
+ if isinstance(a, DType):
227
+ dtypes.append(a)
228
+ elif isinstance(a, Array):
229
+ dtypes.append(a.dtype)
215
230
  elif isinstance(a, (bool, int, float, complex)):
216
231
  scalars.append(a)
217
- elif isinstance(a, np.ndarray) or a not in _all_dtypes:
218
- raise TypeError("result_type() inputs must be array_api arrays or dtypes")
219
- A.append(a)
220
-
221
- # remove python scalars
222
- A = [a for a in A if not isinstance(a, (bool, int, float, complex))]
232
+ else:
233
+ raise TypeError(
234
+ "result_type() inputs must be Array API arrays, dtypes, or scalars"
235
+ )
223
236
 
224
- if len(A) == 0:
237
+ if not dtypes:
225
238
  raise ValueError("at least one array or dtype is required")
226
- elif len(A) == 1:
227
- result = A[0]
228
- else:
229
- t = A[0]
230
- for t2 in A[1:]:
231
- t = _result_type(t, t2)
232
- result = t
239
+ result = dtypes[0]
240
+ for t2 in dtypes[1:]:
241
+ result = _result_type(result, t2)
233
242
 
234
- if len(scalars) == 0:
243
+ if not scalars:
235
244
  return result
236
245
 
237
246
  if get_array_api_strict_flags()['api_version'] <= '2023.12':
238
- raise TypeError("result_type() inputs must be array_api arrays or dtypes")
247
+ raise TypeError("result_type() inputs must be Array API arrays or dtypes")
239
248
 
240
249
  # promote python scalars given the result_type for all arrays/dtypes
241
250
  from ._creation_functions import empty