Skip to content

Commit 923996c

Browse files
committed
Add protocols for array object methods
- DLPack export via `HasDLPack` - Indexing via `HasGetItem` - Indexed assignment via `HasSetItem` - Device transfer via `HasToDevice` Also composes these protocols into `Array`, exports them from the top-level package, and adds NumPy integration assignments for the new protocol surfaces.
1 parent a53c176 commit 923996c

3 files changed

Lines changed: 97 additions & 27 deletions

File tree

src/array_api_typing/__init__.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,9 @@
44
"Array",
55
"HasArrayNamespace",
66
"HasDType",
7-
"HasDevice",
8-
"HasMatrixTranspose",
9-
"HasNDim",
10-
"HasShape",
11-
"HasSize",
12-
"HasTranspose",
137
"__version__",
148
"__version_tuple__",
159
)
1610

17-
from ._array import (
18-
Array,
19-
HasArrayNamespace,
20-
HasDevice,
21-
HasDType,
22-
HasMatrixTranspose,
23-
HasNDim,
24-
HasShape,
25-
HasSize,
26-
HasTranspose,
27-
)
11+
from ._array import Array, HasArrayNamespace, HasDType
2812
from ._version import version as __version__, version_tuple as __version_tuple__

src/array_api_typing/_array.py

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
__all__ = (
22
"Array",
33
"HasArrayNamespace",
4-
"HasDType",
5-
"HasDevice",
6-
"HasMatrixTranspose",
7-
"HasNDim",
8-
"HasShape",
9-
"HasSize",
10-
"HasTranspose",
114
)
125

136
from types import ModuleType
@@ -17,6 +10,8 @@
1710
NamespaceT_co = TypeVar("NamespaceT_co", covariant=True, default=ModuleType)
1811
DTypeT_co = TypeVar("DTypeT_co", covariant=True)
1912
DeviceT_co = TypeVar("DeviceT_co", covariant=True, default=object)
13+
KeyT_contra = TypeVar("KeyT_contra", contravariant=True, default=object)
14+
ValueT_contra = TypeVar("ValueT_contra", contravariant=True, default=object)
2015

2116

2217
class HasArrayNamespace(Protocol[NamespaceT_co]):
@@ -66,6 +61,26 @@ def __array_namespace__(
6661
...
6762

6863

64+
class HasDLPack(Protocol):
65+
"""Protocol for array classes that support DLPack export."""
66+
67+
def __dlpack__(
68+
self,
69+
/,
70+
*,
71+
stream: object | None = None,
72+
max_version: tuple[int, int] | None = None,
73+
dl_device: tuple[int, int] | None = None,
74+
copy: bool | None = None,
75+
) -> object:
76+
"""Export the array as a DLPack capsule."""
77+
...
78+
79+
def __dlpack_device__(self, /) -> tuple[int, int]:
80+
"""Return the DLPack device type and device ID."""
81+
...
82+
83+
6984
class HasDType(Protocol[DTypeT_co]):
7085
"""Protocol for array classes that have a data type attribute."""
7186

@@ -84,6 +99,14 @@ def device(self) -> DeviceT_co:
8499
...
85100

86101

102+
class HasGetItem(Protocol[KeyT_contra]):
103+
"""Protocol for array classes that support indexing."""
104+
105+
def __getitem__(self, key: KeyT_contra, /) -> Self:
106+
"""Return ``self[key]``."""
107+
...
108+
109+
87110
class HasMatrixTranspose(Protocol):
88111
"""Protocol for array classes that have a matrix transpose attribute."""
89112

@@ -140,6 +163,14 @@ def shape(self) -> tuple[int | None, ...]:
140163
...
141164

142165

166+
class HasSetItem(Protocol[KeyT_contra, ValueT_contra]):
167+
"""Protocol for mutable array classes that support indexed assignment."""
168+
169+
def __setitem__(self, key: KeyT_contra, value: ValueT_contra, /) -> None:
170+
"""Set ``self[key]`` to ``value``."""
171+
...
172+
173+
143174
class HasSize(Protocol):
144175
"""Protocol for array classes that have a size attribute."""
145176

@@ -159,6 +190,14 @@ def size(self) -> int | None:
159190
...
160191

161192

193+
class HasToDevice(Protocol):
194+
"""Protocol for array classes that support device transfer."""
195+
196+
def to_device(self, device: object, /, *, stream: object | None = None) -> Self:
197+
"""Copy the array to the specified device."""
198+
...
199+
200+
162201
class HasTranspose(Protocol):
163202
"""Protocol for array classes that support the transpose operation."""
164203

@@ -191,16 +230,26 @@ def T(self) -> Self: # noqa: N802
191230

192231
class Array(
193232
# ------ Attributes -------
233+
HasDevice[DeviceT_co],
194234
HasDType[DTypeT_co],
235+
HasMatrixTranspose,
236+
HasNDim,
237+
HasShape,
238+
HasSize,
239+
HasTranspose,
195240
# ------- Methods ---------
196241
HasArrayNamespace[NamespaceT_co],
242+
HasDLPack,
243+
HasGetItem,
244+
HasSetItem,
245+
HasToDevice,
197246
# -------------------------
198-
Protocol[DTypeT_co, NamespaceT_co],
247+
Protocol[DTypeT_co, NamespaceT_co, DeviceT_co],
199248
):
200249
"""Array API specification for array object attributes and methods.
201250
202-
The type is: ``Array[+DTypeT, +NamespaceT = ModuleType] = Array[DTypeT,
203-
NamespaceT]`` where:
251+
The type is: ``Array[+DTypeT, +NamespaceT = ModuleType, +DeviceT = object] =
252+
Array[DTypeT, NamespaceT, DeviceT]`` where:
204253
205254
- `DTypeT` is the data type of the array elements.
206255
- `NamespaceT` is the type of the array namespace. It defaults to
@@ -209,6 +258,7 @@ class Array(
209258
`types.SimpleNamespace`, to allow for wrapper libraries to
210259
semi-dynamically define their own array namespaces based on the wrapped
211260
array type.
261+
- `DeviceT` is the type of the hardware device the array data resides on.
212262
213263
This type is intended for use in static typing to ensure that an object has
214264
the attributes and methods defined in the array API specification. It should

tests/integration/test_numpy2p0.pyi

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ ns: ModuleType = a_ns.__array_namespace__()
3636
# backpropagated to the type of `a_ns`
3737
_: xpt.HasArrayNamespace[dict[str, int]] = nparr # not caught
3838

39+
# =========================================================
40+
# `xpt.HasDLPack`
41+
42+
_: xpt.HasDLPack = nparr
43+
_: xpt.HasDLPack = nparr_i32
44+
_: xpt.HasDLPack = nparr_f32
45+
_: xpt.HasDLPack = nparr_b
46+
47+
a_dlpack: xpt.HasDLPack = nparr
48+
assert_type(a_dlpack.__dlpack__(), object)
49+
assert_type(a_dlpack.__dlpack_device__(), tuple[int, int])
50+
3951
# =========================================================
4052
# `xpt.HasDType`
4153

@@ -53,6 +65,14 @@ _: xpt.HasDevice = nparr_i32
5365
_: xpt.HasDevice = nparr_f32
5466
_: xpt.HasDevice = nparr_b
5567

68+
# =========================================================
69+
# `xpt.HasGetItem`
70+
71+
_: xpt.HasGetItem = nparr
72+
_: xpt.HasGetItem = nparr_i32
73+
_: xpt.HasGetItem = nparr_f32
74+
_: xpt.HasGetItem = nparr_b
75+
5676
# =========================================================
5777
# `xpt.HasMatrixTranspose`
5878

@@ -77,6 +97,14 @@ _: xpt.HasShape = nparr_i32
7797
_: xpt.HasShape = nparr_f32
7898
_: xpt.HasShape = nparr_b
7999

100+
# =========================================================
101+
# `xpt.HasSetItem`
102+
103+
_: xpt.HasSetItem = nparr
104+
_: xpt.HasSetItem = nparr_i32
105+
_: xpt.HasSetItem = nparr_f32
106+
_: xpt.HasSetItem = nparr_b
107+
80108
# =========================================================
81109
# `xpt.HasSize`
82110

@@ -85,6 +113,14 @@ _: xpt.HasSize = nparr_i32
85113
_: xpt.HasSize = nparr_f32
86114
_: xpt.HasSize = nparr_b
87115

116+
# =========================================================
117+
# `xpt.HasToDevice`
118+
119+
_: xpt.HasToDevice = nparr
120+
_: xpt.HasToDevice = nparr_i32
121+
_: xpt.HasToDevice = nparr_f32
122+
_: xpt.HasToDevice = nparr_b
123+
88124
# =========================================================
89125
# `xpt.HasTranspose`
90126

0 commit comments

Comments
 (0)