11from __future__ import annotations
22
33import sys
4- from collections .abc import Callable , Iterable
4+ from collections .abc import Callable , Iterable , Sequence
55from typing import (
66 TYPE_CHECKING ,
77 Any ,
88 Literal ,
99 Protocol ,
10- TypeAlias ,
10+ SupportsIndex ,
11+ runtime_checkable ,
1112)
1213
1314import numpy as np
1415import numpy .typing as npt
1516
17+ from zarr .common import ChunkCoords
18+
1619if TYPE_CHECKING :
1720 from typing_extensions import Self
1821
1922 from zarr .codecs .bytes import Endian
2023 from zarr .common import BytesLike
2124
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+ """
2692
2793
2894def check_item_key_is_1d_contiguous (key : Any ) -> None :
@@ -124,7 +190,7 @@ def create_zero_length(cls) -> Self:
124190 return cls (np .array ([], dtype = "b" ))
125191
126192 @classmethod
127- def from_array_like (cls , array_like : NDArrayLike ) -> Self :
193+ def from_array_like (cls , array_like : ArrayLike ) -> Self :
128194 """Create a new buffer of a array-like object
129195
130196 Parameters
@@ -153,7 +219,7 @@ def from_bytes(cls, bytes_like: BytesLike) -> Self:
153219 """
154220 return cls .from_array_like (np .frombuffer (bytes_like , dtype = "b" ))
155221
156- def as_array_like (self ) -> NDArrayLike :
222+ def as_array_like (self ) -> ArrayLike :
157223 """Return the underlying array (host or device memory) of this buffer
158224
159225 This will never copy data.
@@ -164,22 +230,6 @@ def as_array_like(self) -> NDArrayLike:
164230 """
165231 return self ._data
166232
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-
183233 def as_numpy_array (self ) -> npt .NDArray [Any ]:
184234 """Return the buffer as a NumPy array (host memory).
185235
@@ -223,17 +273,8 @@ def __add__(self, other: Buffer) -> Self:
223273
224274 other_array = other .as_array_like ()
225275 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 )))
237278 )
238279
239280
@@ -345,22 +386,6 @@ def as_ndarray_like(self) -> NDArrayLike:
345386 """
346387 return self ._data
347388
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-
364389 def as_numpy_array (self ) -> npt .NDArray [Any ]:
365390 """Return the buffer as a NumPy array (host memory).
366391
@@ -393,8 +418,8 @@ def byteorder(self) -> Endian:
393418 else :
394419 return Endian (sys .byteorder )
395420
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 ))
398423
399424 def astype (self , dtype : npt .DTypeLike , order : Literal ["K" , "A" , "C" , "F" ] = "K" ) -> Self :
400425 return self .__class__ (self ._data .astype (dtype = dtype , order = order ))
@@ -419,8 +444,8 @@ def fill(self, value: Any) -> None:
419444 def copy (self ) -> Self :
420445 return self .__class__ (self ._data .copy ())
421446
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 ))
424449
425450
426451def as_numpy_array_wrapper (func : Callable [[npt .NDArray [Any ]], bytes ], buf : Buffer ) -> Buffer :
0 commit comments