1
1
from __future__ import annotations
2
2
3
3
import sys
4
- from collections .abc import Callable , Iterable
4
+ from collections .abc import Callable , Iterable , Sequence
5
5
from typing import (
6
6
TYPE_CHECKING ,
7
7
Any ,
8
8
Literal ,
9
9
Protocol ,
10
- TypeAlias ,
10
+ SupportsIndex ,
11
+ runtime_checkable ,
11
12
)
12
13
13
14
import numpy as np
14
15
import numpy .typing as npt
15
16
17
+ from zarr .common import ChunkCoords
18
+
16
19
if TYPE_CHECKING :
17
20
from typing_extensions import Self
18
21
19
22
from zarr .codecs .bytes import Endian
20
23
from zarr .common import BytesLike
21
24
22
- # TODO: create a protocol for the attributes we need, for now we alias Numpy's ndarray
23
- # both for the array-like and ndarray-like
24
- ArrayLike : TypeAlias = npt .NDArray [Any ]
25
- NDArrayLike : TypeAlias = npt .NDArray [Any ]
25
+
26
+ @runtime_checkable
27
+ class ArrayLike (Protocol ):
28
+ """Protocol for the array-like type that underlie Buffer"""
29
+
30
+ @property
31
+ def dtype (self ) -> np .dtype [Any ]: ...
32
+
33
+ @property
34
+ def ndim (self ) -> int : ...
35
+
36
+ @property
37
+ def size (self ) -> int : ...
38
+
39
+ def __getitem__ (self , key : slice ) -> Self : ...
40
+
41
+ def __setitem__ (self , key : slice , value : Any ) -> None : ...
42
+
43
+
44
+ @runtime_checkable
45
+ class NDArrayLike (Protocol ):
46
+ """Protocol for the nd-array-like type that underlie NDBuffer"""
47
+
48
+ @property
49
+ def dtype (self ) -> np .dtype [Any ]: ...
50
+
51
+ @property
52
+ def ndim (self ) -> int : ...
53
+
54
+ @property
55
+ def size (self ) -> int : ...
56
+
57
+ @property
58
+ def shape (self ) -> ChunkCoords : ...
59
+
60
+ def __len__ (self ) -> int : ...
61
+
62
+ def __getitem__ (self , key : slice ) -> Self : ...
63
+
64
+ def __setitem__ (self , key : slice , value : Any ) -> None : ...
65
+
66
+ def reshape (self , shape : ChunkCoords , * , order : Literal ["A" , "C" , "F" ] = ...) -> Self : ...
67
+
68
+ def view (self , dtype : npt .DTypeLike ) -> Self : ...
69
+
70
+ def astype (self , dtype : npt .DTypeLike , order : Literal ["K" , "A" , "C" , "F" ] = ...) -> Self : ...
71
+
72
+ def fill (self , value : Any ) -> None : ...
73
+
74
+ def copy (self ) -> Self : ...
75
+
76
+ def transpose (self , axes : SupportsIndex | Sequence [SupportsIndex ] | None ) -> Self : ...
77
+
78
+ def ravel (self , order : Literal ["K" , "A" , "C" , "F" ] = "C" ) -> Self : ...
79
+
80
+ def all (self ) -> bool : ...
81
+
82
+ def __eq__ (self , other : Any ) -> Self : # type: ignore
83
+ """Element-wise equal
84
+
85
+ Notice
86
+ ------
87
+ Type checkers such as mypy complains because the return type isn't a bool like
88
+ its supertype "object", which violates the Liskov substitution principle.
89
+ This is true, but since NumPy's ndarray is defined as an element-wise equal,
90
+ our hands are tied.
91
+ """
26
92
27
93
28
94
def check_item_key_is_1d_contiguous (key : Any ) -> None :
@@ -124,7 +190,7 @@ def create_zero_length(cls) -> Self:
124
190
return cls (np .array ([], dtype = "b" ))
125
191
126
192
@classmethod
127
- def from_array_like (cls , array_like : NDArrayLike ) -> Self :
193
+ def from_array_like (cls , array_like : ArrayLike ) -> Self :
128
194
"""Create a new buffer of a array-like object
129
195
130
196
Parameters
@@ -153,7 +219,7 @@ def from_bytes(cls, bytes_like: BytesLike) -> Self:
153
219
"""
154
220
return cls .from_array_like (np .frombuffer (bytes_like , dtype = "b" ))
155
221
156
- def as_array_like (self ) -> NDArrayLike :
222
+ def as_array_like (self ) -> ArrayLike :
157
223
"""Return the underlying array (host or device memory) of this buffer
158
224
159
225
This will never copy data.
@@ -164,22 +230,6 @@ def as_array_like(self) -> NDArrayLike:
164
230
"""
165
231
return self ._data
166
232
167
- def as_nd_buffer (self , * , dtype : npt .DTypeLike ) -> NDBuffer :
168
- """Create a new NDBuffer from this one.
169
-
170
- This will never copy data.
171
-
172
- Parameters
173
- ----------
174
- dtype
175
- The datatype of the returned buffer (reinterpretation of the bytes)
176
-
177
- Return
178
- ------
179
- New NDbuffer representing `self.as_array_like()`
180
- """
181
- return NDBuffer .from_ndarray_like (self ._data .view (dtype = dtype ))
182
-
183
233
def as_numpy_array (self ) -> npt .NDArray [Any ]:
184
234
"""Return the buffer as a NumPy array (host memory).
185
235
@@ -223,17 +273,8 @@ def __add__(self, other: Buffer) -> Self:
223
273
224
274
other_array = other .as_array_like ()
225
275
assert other_array .dtype == np .dtype ("b" )
226
- return self .__class__ (np .concatenate ((self ._data , other_array )))
227
-
228
- def __eq__ (self , other : Any ) -> bool :
229
- if isinstance (other , bytes | bytearray ):
230
- # Many of the tests compares `Buffer` with `bytes` so we
231
- # convert the bytes to a Buffer and try again
232
- return self == self .from_bytes (other )
233
- if isinstance (other , Buffer ):
234
- return (self ._data == other .as_array_like ()).all ()
235
- raise ValueError (
236
- f"equal operator not supported between { self .__class__ } and { other .__class__ } "
276
+ return self .__class__ (
277
+ np .concatenate ((np .asanyarray (self ._data ), np .asanyarray (other_array )))
237
278
)
238
279
239
280
@@ -345,22 +386,6 @@ def as_ndarray_like(self) -> NDArrayLike:
345
386
"""
346
387
return self ._data
347
388
348
- def as_buffer (self ) -> Buffer :
349
- """Create a new Buffer from this one.
350
-
351
- Warning
352
- -------
353
- Copies data if the buffer is non-contiguous.
354
-
355
- Return
356
- ------
357
- The new buffer (might be data copy)
358
- """
359
- data = self ._data
360
- if not self ._data .flags .contiguous :
361
- data = np .ascontiguousarray (self ._data )
362
- return Buffer (data .reshape (- 1 ).view (dtype = "b" )) # Flatten the array without copy
363
-
364
389
def as_numpy_array (self ) -> npt .NDArray [Any ]:
365
390
"""Return the buffer as a NumPy array (host memory).
366
391
@@ -393,8 +418,8 @@ def byteorder(self) -> Endian:
393
418
else :
394
419
return Endian (sys .byteorder )
395
420
396
- def reshape (self , newshape : Iterable [ int ] ) -> Self :
397
- return self .__class__ (self ._data .reshape (tuple ( newshape ) ))
421
+ def reshape (self , newshape : ChunkCoords ) -> Self :
422
+ return self .__class__ (self ._data .reshape (newshape ))
398
423
399
424
def astype (self , dtype : npt .DTypeLike , order : Literal ["K" , "A" , "C" , "F" ] = "K" ) -> Self :
400
425
return self .__class__ (self ._data .astype (dtype = dtype , order = order ))
@@ -419,8 +444,8 @@ def fill(self, value: Any) -> None:
419
444
def copy (self ) -> Self :
420
445
return self .__class__ (self ._data .copy ())
421
446
422
- def transpose (self , * axes : np . SupportsIndex ) -> Self : # type: ignore[name-defined]
423
- return self .__class__ (self ._data .transpose (* axes ))
447
+ def transpose (self , axes : SupportsIndex | Sequence [ SupportsIndex ] | None ) -> Self :
448
+ return self .__class__ (self ._data .transpose (axes ))
424
449
425
450
426
451
def as_numpy_array_wrapper (func : Callable [[npt .NDArray [Any ]], bytes ], buf : Buffer ) -> Buffer :
0 commit comments