Skip to content

Commit c5e31fb

Browse files
authored
Merge branch 'v3' into generalize-stateful-store
2 parents ae42bff + 692593b commit c5e31fb

File tree

607 files changed

+292
-29382
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

607 files changed

+292
-29382
lines changed

.github/workflows/releases.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
with:
5656
name: releases
5757
path: dist
58-
- uses: pypa/[email protected].1
58+
- uses: pypa/[email protected].2
5959
with:
6060
user: __token__
6161
password: ${{ secrets.pypi_password }}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,5 @@ fixture/
8484
.DS_Store
8585
tests/.hypothesis
8686
.hypothesis/
87+
88+
zarr/version.py

pyproject.toml

+23-5
Original file line numberDiff line numberDiff line change
@@ -207,18 +207,36 @@ extend-exclude = [
207207

208208
[tool.ruff.lint]
209209
extend-select = [
210-
"B", # flake8-bugbear
211-
"I", # isort
212-
"ISC",
213-
"UP", # pyupgrade
214-
"RSE",
210+
"B", # flake8-bugbear
211+
"I", # isort
212+
"ISC", # flake8-implicit-str-concat
213+
"PGH", # pygrep-hooks
214+
"PYI", # flake8-pyi
215+
"RSE", # flake8-raise
215216
"RUF",
216217
"TCH", # flake8-type-checking
217218
"TRY", # tryceratops
219+
"UP", # pyupgrade
218220
]
219221
ignore = [
222+
"PYI013",
220223
"RUF005",
221224
"TRY003",
225+
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
226+
"W191",
227+
"E111",
228+
"E114",
229+
"E117",
230+
"D206",
231+
"D300",
232+
"Q000",
233+
"Q001",
234+
"Q002",
235+
"Q003",
236+
"COM812",
237+
"COM819",
238+
"ISC001",
239+
"ISC002",
222240
]
223241

224242
[tool.mypy]

src/zarr/abc/store.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from abc import ABC, abstractmethod
22
from asyncio import gather
33
from collections.abc import AsyncGenerator, Iterable
4+
from types import TracebackType
45
from typing import Any, NamedTuple, Protocol, runtime_checkable
56

67
from typing_extensions import Self
@@ -35,7 +36,7 @@ class Store(ABC):
3536
_mode: AccessMode
3637
_is_open: bool
3738

38-
def __init__(self, mode: AccessModeLiteral = "r", *args: Any, **kwargs: Any):
39+
def __init__(self, mode: AccessModeLiteral = "r", *args: Any, **kwargs: Any) -> None:
3940
self._is_open = False
4041
self._mode = AccessMode.from_literal(mode)
4142

@@ -49,7 +50,12 @@ def __enter__(self) -> Self:
4950
"""Enter a context manager that will close the store upon exiting."""
5051
return self
5152

52-
def __exit__(self, *args: Any) -> None:
53+
def __exit__(
54+
self,
55+
exc_type: type[BaseException] | None,
56+
exc_value: BaseException | None,
57+
traceback: TracebackType | None,
58+
) -> None:
5359
"""Close the store."""
5460
self.close()
5561

src/zarr/api/asynchronous.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import asyncio
44
import warnings
5-
from typing import TYPE_CHECKING, Any, Literal, Union, cast
5+
from typing import TYPE_CHECKING, Any, Literal, cast
66

77
import numpy as np
88
import numpy.typing as npt
@@ -25,6 +25,10 @@
2525
from zarr.core.buffer import NDArrayLike
2626
from zarr.core.chunk_key_encodings import ChunkKeyEncoding
2727

28+
# TODO: this type could use some more thought
29+
ArrayLike = AsyncArray | Array | npt.NDArray[Any]
30+
PathLike = str
31+
2832
__all__ = [
2933
"consolidate_metadata",
3034
"copy",
@@ -53,10 +57,6 @@
5357
"zeros_like",
5458
]
5559

56-
# TODO: this type could use some more thought, noqa to avoid "Variable "asynchronous.ArrayLike" is not valid as a type"
57-
ArrayLike = Union[AsyncArray | Array | npt.NDArray[Any]] # noqa
58-
PathLike = str
59-
6060

6161
def _get_shape_chunks(a: ArrayLike | Any) -> tuple[ChunkCoords | None, ChunkCoords | None]:
6262
"""helper function to get the shape and chunks from an array-like object"""

src/zarr/codecs/_v2.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@
88

99
from zarr.abc.codec import ArrayArrayCodec, ArrayBytesCodec
1010
from zarr.core.buffer import Buffer, NDBuffer, default_buffer_prototype
11-
from zarr.core.common import JSON, to_thread
11+
from zarr.core.common import to_thread
1212
from zarr.registry import get_ndbuffer_class
1313

1414
if TYPE_CHECKING:
15+
import numcodecs.abc
16+
1517
from zarr.core.array_spec import ArraySpec
1618

1719

1820
@dataclass(frozen=True)
1921
class V2Compressor(ArrayBytesCodec):
20-
compressor: dict[str, JSON] | None
22+
compressor: numcodecs.abc.Codec | None
2123

2224
is_fixed_size = False
2325

@@ -27,9 +29,8 @@ async def _decode_single(
2729
chunk_spec: ArraySpec,
2830
) -> NDBuffer:
2931
if self.compressor is not None:
30-
compressor = numcodecs.get_codec(self.compressor)
3132
chunk_numpy_array = ensure_ndarray(
32-
await to_thread(compressor.decode, chunk_bytes.as_array_like())
33+
await to_thread(self.compressor.decode, chunk_bytes.as_array_like())
3334
)
3435
else:
3536
chunk_numpy_array = ensure_ndarray(chunk_bytes.as_array_like())
@@ -47,14 +48,13 @@ async def _encode_single(
4748
) -> Buffer | None:
4849
chunk_numpy_array = chunk_array.as_numpy_array()
4950
if self.compressor is not None:
50-
compressor = numcodecs.get_codec(self.compressor)
5151
if (
5252
not chunk_numpy_array.flags.c_contiguous
5353
and not chunk_numpy_array.flags.f_contiguous
5454
):
5555
chunk_numpy_array = chunk_numpy_array.copy(order="A")
5656
encoded_chunk_bytes = ensure_bytes(
57-
await to_thread(compressor.encode, chunk_numpy_array)
57+
await to_thread(self.compressor.encode, chunk_numpy_array)
5858
)
5959
else:
6060
encoded_chunk_bytes = ensure_bytes(chunk_numpy_array)

src/zarr/codecs/transpose.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,14 @@ async def _decode_single(
9696
chunk_spec: ArraySpec,
9797
) -> NDBuffer:
9898
inverse_order = np.argsort(self.order)
99-
chunk_array = chunk_array.transpose(inverse_order)
100-
return chunk_array
99+
return chunk_array.transpose(inverse_order)
101100

102101
async def _encode_single(
103102
self,
104103
chunk_array: NDBuffer,
105104
_chunk_spec: ArraySpec,
106105
) -> NDBuffer | None:
107-
chunk_array = chunk_array.transpose(self.order)
108-
return chunk_array
106+
return chunk_array.transpose(self.order)
109107

110108
def compute_encoded_size(self, input_byte_length: int, _chunk_spec: ArraySpec) -> int:
111109
return input_byte_length

src/zarr/core/array.py

+3-11
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def __init__(
110110
metadata: ArrayMetadata,
111111
store_path: StorePath,
112112
order: Literal["C", "F"] | None = None,
113-
):
113+
) -> None:
114114
metadata_parsed = parse_array_metadata(metadata)
115115
order_parsed = parse_indexing_order(order or config.get("array.order"))
116116

@@ -252,12 +252,6 @@ async def _create_v3(
252252
shape = parse_shapelike(shape)
253253
codecs = list(codecs) if codecs is not None else [BytesCodec()]
254254

255-
if fill_value is None:
256-
if dtype == np.dtype("bool"):
257-
fill_value = False
258-
else:
259-
fill_value = 0
260-
261255
if chunk_key_encoding is None:
262256
chunk_key_encoding = ("default", "/")
263257
assert chunk_key_encoding is not None
@@ -281,7 +275,6 @@ async def _create_v3(
281275
)
282276

283277
array = cls(metadata=metadata, store_path=store_path)
284-
285278
await array._save_metadata(metadata)
286279
return array
287280

@@ -294,7 +287,7 @@ async def _create_v2(
294287
dtype: npt.DTypeLike,
295288
chunks: ChunkCoords,
296289
dimension_separator: Literal[".", "/"] | None = None,
297-
fill_value: None | int | float = None,
290+
fill_value: None | float = None,
298291
order: Literal["C", "F"] | None = None,
299292
filters: list[dict[str, JSON]] | None = None,
300293
compressor: dict[str, JSON] | None = None,
@@ -331,8 +324,7 @@ def from_dict(
331324
data: dict[str, JSON],
332325
) -> AsyncArray:
333326
metadata = parse_array_metadata(data)
334-
async_array = cls(metadata=metadata, store_path=store_path)
335-
return async_array
327+
return cls(metadata=metadata, store_path=store_path)
336328

337329
@classmethod
338330
async def open(

src/zarr/core/array_spec.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from dataclasses import dataclass
44
from typing import TYPE_CHECKING, Any, Literal
55

6+
import numpy as np
7+
68
from zarr.core.common import parse_fill_value, parse_order, parse_shapelike
79

810
if TYPE_CHECKING:
9-
import numpy as np
10-
1111
from zarr.core.buffer import BufferPrototype
1212
from zarr.core.common import ChunkCoords
1313

@@ -29,11 +29,12 @@ def __init__(
2929
prototype: BufferPrototype,
3030
) -> None:
3131
shape_parsed = parse_shapelike(shape)
32+
dtype_parsed = np.dtype(dtype)
3233
fill_value_parsed = parse_fill_value(fill_value)
3334
order_parsed = parse_order(order)
3435

3536
object.__setattr__(self, "shape", shape_parsed)
36-
object.__setattr__(self, "dtype", dtype)
37+
object.__setattr__(self, "dtype", dtype_parsed)
3738
object.__setattr__(self, "fill_value", fill_value_parsed)
3839
object.__setattr__(self, "order", order_parsed)
3940
object.__setattr__(self, "prototype", prototype)

src/zarr/core/attributes.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414

1515
class Attributes(MutableMapping[str, JSON]):
16-
def __init__(self, obj: Array | Group):
16+
def __init__(self, obj: Array | Group) -> None:
1717
# key=".zattrs", read_only=False, cache=True, synchronizer=None
1818
self._obj = obj
1919

@@ -51,3 +51,6 @@ def put(self, d: dict[str, JSON]) -> None:
5151
{'a': 3, 'c': 4}
5252
"""
5353
self._obj = self._obj.update_attributes(d)
54+
55+
def asdict(self) -> dict[str, JSON]:
56+
return dict(self._obj.metadata.attributes)

src/zarr/core/buffer/core.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def ravel(self, order: Literal["K", "A", "C", "F"] = ...) -> Self: ...
9393

9494
def all(self) -> bool: ...
9595

96-
def __eq__(self, other: Any) -> Self: # type: ignore[explicit-override, override]
96+
def __eq__(self, other: object) -> Self: # type: ignore[explicit-override, override]
9797
"""Element-wise equal
9898
9999
Notes
@@ -136,7 +136,7 @@ class Buffer(ABC):
136136
array-like object that must be 1-dim, contiguous, and byte dtype.
137137
"""
138138

139-
def __init__(self, array_like: ArrayLike):
139+
def __init__(self, array_like: ArrayLike) -> None:
140140
if array_like.ndim != 1:
141141
raise ValueError("array_like: only 1-dim allowed")
142142
if array_like.dtype != np.dtype("b"):
@@ -313,7 +313,7 @@ class NDBuffer:
313313
ndarray-like object that is convertible to a regular Numpy array.
314314
"""
315315

316-
def __init__(self, array: NDArrayLike):
316+
def __init__(self, array: NDArrayLike) -> None:
317317
# assert array.ndim > 0
318318
assert array.dtype != object
319319
self._data = array
@@ -464,9 +464,14 @@ def __repr__(self) -> str:
464464

465465
def all_equal(self, other: Any, equal_nan: bool = True) -> bool:
466466
"""Compare to `other` using np.array_equal."""
467+
if other is None:
468+
# Handle None fill_value for Zarr V2
469+
return False
467470
# use array_equal to obtain equal_nan=True functionality
468471
data, other = np.broadcast_arrays(self._data, other)
469-
result = np.array_equal(self._data, other, equal_nan=equal_nan)
472+
result = np.array_equal(
473+
self._data, other, equal_nan=equal_nan if self._data.dtype.kind not in "US" else False
474+
)
470475
return result
471476

472477
def fill(self, value: Any) -> None:

src/zarr/core/buffer/cpu.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Buffer(core.Buffer):
4545
array-like object that must be 1-dim, contiguous, and byte dtype.
4646
"""
4747

48-
def __init__(self, array_like: ArrayLike):
48+
def __init__(self, array_like: ArrayLike) -> None:
4949
super().__init__(array_like)
5050

5151
@classmethod
@@ -143,7 +143,7 @@ class NDBuffer(core.NDBuffer):
143143
ndarray-like object that is convertible to a regular Numpy array.
144144
"""
145145

146-
def __init__(self, array: NDArrayLike):
146+
def __init__(self, array: NDArrayLike) -> None:
147147
super().__init__(array)
148148

149149
@classmethod

src/zarr/core/buffer/gpu.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Buffer(core.Buffer):
4848
array-like object that must be 1-dim, contiguous, and byte dtype.
4949
"""
5050

51-
def __init__(self, array_like: ArrayLike):
51+
def __init__(self, array_like: ArrayLike) -> None:
5252
if cp is None:
5353
raise ImportError(
5454
"Cannot use zarr.buffer.gpu.Buffer without cupy. Please install cupy."
@@ -137,7 +137,7 @@ class NDBuffer(core.NDBuffer):
137137
ndarray-like object that is convertible to a regular Numpy array.
138138
"""
139139

140-
def __init__(self, array: NDArrayLike):
140+
def __init__(self, array: NDArrayLike) -> None:
141141
if cp is None:
142142
raise ImportError(
143143
"Cannot use zarr.buffer.gpu.NDBuffer without cupy. Please install cupy."

0 commit comments

Comments
 (0)