Skip to content

Commit c8ff676

Browse files
committed
Fix typing errors in testing.stateful
1 parent 2609748 commit c8ff676

File tree

6 files changed

+64
-48
lines changed

6 files changed

+64
-48
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ repos:
3737
- obstore>=0.5.1
3838
# Tests
3939
- pytest
40+
- hypothesis
4041
- repo: https://github.com/scientific-python/cookie
4142
rev: 2025.01.22
4243
hooks:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
356356
[[tool.mypy.overrides]]
357357
module = [
358358
"tests.package_with_entrypoint.*",
359+
"zarr.testing.stateful",
359360
"tests.test_codecs.test_transpose",
360361
"tests.test_config"
361362
]
@@ -365,7 +366,6 @@ strict = false
365366
# and fix the errors
366367
[[tool.mypy.overrides]]
367368
module = [
368-
"zarr.testing.stateful", # lots of hypothesis decorator errors
369369
"tests.test_codecs.test_codecs",
370370
"tests.test_metadata.*",
371371
"tests.test_store.*",

src/zarr/core/array.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ def _create_metadata_v3(
672672
fill_value: Any | None = None,
673673
chunk_key_encoding: ChunkKeyEncodingLike | None = None,
674674
codecs: Iterable[Codec | dict[str, JSON]] | None = None,
675-
dimension_names: Iterable[str] | None = None,
675+
dimension_names: Iterable[str | None] | None = None,
676676
attributes: dict[str, JSON] | None = None,
677677
) -> ArrayV3Metadata:
678678
"""
@@ -3821,7 +3821,7 @@ async def from_array(
38213821
zarr_format: ZarrFormat | None = None,
38223822
attributes: dict[str, JSON] | None = None,
38233823
chunk_key_encoding: ChunkKeyEncodingLike | None = None,
3824-
dimension_names: Iterable[str] | None = None,
3824+
dimension_names: Iterable[str | None] | None = None,
38253825
storage_options: dict[str, Any] | None = None,
38263826
overwrite: bool = False,
38273827
config: ArrayConfig | ArrayConfigLike | None = None,
@@ -4083,7 +4083,7 @@ async def init_array(
40834083
zarr_format: ZarrFormat | None = 3,
40844084
attributes: dict[str, JSON] | None = None,
40854085
chunk_key_encoding: ChunkKeyEncodingLike | None = None,
4086-
dimension_names: Iterable[str] | None = None,
4086+
dimension_names: Iterable[str | None] | None = None,
40874087
overwrite: bool = False,
40884088
config: ArrayConfigLike | None,
40894089
) -> AsyncArray[ArrayV3Metadata] | AsyncArray[ArrayV2Metadata]:
@@ -4477,7 +4477,7 @@ def _parse_keep_array_attr(
44774477
order: MemoryOrder | None,
44784478
zarr_format: ZarrFormat | None,
44794479
chunk_key_encoding: ChunkKeyEncodingLike | None,
4480-
dimension_names: Iterable[str] | None,
4480+
dimension_names: Iterable[str | None] | None,
44814481
) -> tuple[
44824482
ChunkCoords | Literal["auto"],
44834483
ShardsLike | None,
@@ -4488,7 +4488,7 @@ def _parse_keep_array_attr(
44884488
MemoryOrder | None,
44894489
ZarrFormat,
44904490
ChunkKeyEncodingLike | None,
4491-
Iterable[str] | None,
4491+
Iterable[str | None] | None,
44924492
]:
44934493
if isinstance(data, Array):
44944494
if chunks == "keep":

src/zarr/core/metadata/v3.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ class ArrayV3Metadata(Metadata):
242242
fill_value: Any
243243
codecs: tuple[Codec, ...]
244244
attributes: dict[str, Any] = field(default_factory=dict)
245-
dimension_names: tuple[str, ...] | None = None
245+
dimension_names: tuple[str | None, ...] | None = None
246246
zarr_format: Literal[3] = field(default=3, init=False)
247247
node_type: Literal["array"] = field(default="array", init=False)
248248
storage_transformers: tuple[dict[str, JSON], ...]
@@ -257,7 +257,7 @@ def __init__(
257257
fill_value: Any,
258258
codecs: Iterable[Codec | dict[str, JSON]],
259259
attributes: dict[str, JSON] | None,
260-
dimension_names: Iterable[str] | None,
260+
dimension_names: Iterable[str | None] | None,
261261
storage_transformers: Iterable[dict[str, JSON]] | None = None,
262262
) -> None:
263263
"""

src/zarr/testing/stateful.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ def init_store(self) -> None:
326326
self.store.clear()
327327

328328
@rule(key=zarr_keys(), data=st.binary(min_size=0, max_size=MAX_BINARY_SIZE))
329-
def set(self, key: str, data: DataObject) -> None:
330-
note(f"(set) Setting {key!r} with {data}")
329+
def set(self, key: str, data: bytes) -> None:
330+
note(f"(set) Setting {key!r} with {data!r}")
331331
assert not self.store.read_only
332332
data_buf = cpu.Buffer.from_bytes(data)
333333
self.store.set(key, data_buf)

src/zarr/testing/strategies.py

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import math
22
import sys
3+
from collections.abc import Callable, Mapping
34
from typing import Any, Literal
45

56
import hypothesis.extra.numpy as npst
67
import hypothesis.strategies as st
78
import numpy as np
9+
import numpy.typing as npt
810
from hypothesis import event
911
from hypothesis.strategies import SearchStrategy
1012

@@ -14,7 +16,7 @@
1416
from zarr.core.array import Array
1517
from zarr.core.chunk_grids import RegularChunkGrid
1618
from zarr.core.chunk_key_encodings import DefaultChunkKeyEncoding
17-
from zarr.core.common import ZarrFormat
19+
from zarr.core.common import JSON, ZarrFormat
1820
from zarr.core.metadata import ArrayV2Metadata, ArrayV3Metadata
1921
from zarr.core.sync import sync
2022
from zarr.storage import MemoryStore, StoreLike
@@ -30,17 +32,17 @@
3032
)
3133

3234

33-
@st.composite # type: ignore[misc]
34-
def keys(draw: st.DrawFn, *, max_num_nodes: int | None = None) -> Any:
35+
@st.composite
36+
def keys(draw: st.DrawFn, *, max_num_nodes: int | None = None) -> str:
3537
return draw(st.lists(node_names, min_size=1, max_size=max_num_nodes).map("/".join))
3638

3739

38-
@st.composite # type: ignore[misc]
39-
def paths(draw: st.DrawFn, *, max_num_nodes: int | None = None) -> Any:
40+
@st.composite
41+
def paths(draw: st.DrawFn, *, max_num_nodes: int | None = None) -> str:
4042
return draw(st.just("/") | keys(max_num_nodes=max_num_nodes))
4143

4244

43-
def v3_dtypes() -> st.SearchStrategy[np.dtype]:
45+
def v3_dtypes() -> st.SearchStrategy[np.dtype[Any]]:
4446
return (
4547
npst.boolean_dtypes()
4648
| npst.integer_dtypes(endianness="=")
@@ -54,7 +56,7 @@ def v3_dtypes() -> st.SearchStrategy[np.dtype]:
5456
)
5557

5658

57-
def v2_dtypes() -> st.SearchStrategy[np.dtype]:
59+
def v2_dtypes() -> st.SearchStrategy[np.dtype[Any]]:
5860
return (
5961
npst.boolean_dtypes()
6062
| npst.integer_dtypes(endianness="=")
@@ -107,7 +109,9 @@ def clear_store(x: Store) -> Store:
107109
.filter(lambda name: name.lower() != "zarr.json")
108110
)
109111
array_names = node_names
110-
attrs = st.none() | st.dictionaries(_attr_keys, _attr_values)
112+
attrs: st.SearchStrategy[Mapping[str, JSON] | None] = st.none() | st.dictionaries(
113+
_attr_keys, _attr_values
114+
)
111115
# st.builds will only call a new store constructor for different keyword arguments
112116
# i.e. stores.examples() will always return the same object per Store class.
113117
# So we map a clear to reset the store.
@@ -118,19 +122,19 @@ def clear_store(x: Store) -> Store:
118122
array_shapes = npst.array_shapes(max_dims=4, min_side=3) | npst.array_shapes(max_dims=4, min_side=0)
119123

120124

121-
@st.composite # type: ignore[misc]
125+
@st.composite
122126
def dimension_names(draw: st.DrawFn, *, ndim: int | None = None) -> list[None | str] | None:
123127
simple_text = st.text(zarr_key_chars, min_size=0)
124-
return draw(st.none() | st.lists(st.none() | simple_text, min_size=ndim, max_size=ndim)) # type: ignore[no-any-return]
128+
return draw(st.none() | st.lists(st.none() | simple_text, min_size=ndim, max_size=ndim)) # type: ignore[arg-type]
125129

126130

127-
@st.composite # type: ignore[misc]
131+
@st.composite
128132
def array_metadata(
129133
draw: st.DrawFn,
130134
*,
131-
array_shapes: st.SearchStrategy[tuple[int, ...]] = npst.array_shapes,
135+
array_shapes: Callable[..., st.SearchStrategy[tuple[int, ...]]] = npst.array_shapes,
132136
zarr_formats: st.SearchStrategy[Literal[2, 3]] = zarr_formats,
133-
attributes: st.SearchStrategy[dict[str, Any]] = attrs,
137+
attributes: SearchStrategy[Mapping[str, JSON] | None] = attrs,
134138
) -> ArrayV2Metadata | ArrayV3Metadata:
135139
zarr_format = draw(zarr_formats)
136140
# separator = draw(st.sampled_from(['/', '\\']))
@@ -146,7 +150,7 @@ def array_metadata(
146150
dtype=dtype,
147151
fill_value=fill_value,
148152
order=draw(st.sampled_from(["C", "F"])),
149-
attributes=draw(attributes),
153+
attributes=draw(attributes), # type: ignore[arg-type]
150154
dimension_separator=draw(st.sampled_from([".", "/"])),
151155
filters=None,
152156
compressor=None,
@@ -157,22 +161,22 @@ def array_metadata(
157161
data_type=dtype,
158162
chunk_grid=RegularChunkGrid(chunk_shape=chunk_shape),
159163
fill_value=fill_value,
160-
attributes=draw(attributes),
164+
attributes=draw(attributes), # type: ignore[arg-type]
161165
dimension_names=draw(dimension_names(ndim=ndim)),
162166
chunk_key_encoding=DefaultChunkKeyEncoding(separator="/"), # FIXME
163167
codecs=[BytesCodec()],
164168
storage_transformers=(),
165169
)
166170

167171

168-
@st.composite # type: ignore[misc]
172+
@st.composite
169173
def numpy_arrays(
170174
draw: st.DrawFn,
171175
*,
172176
shapes: st.SearchStrategy[tuple[int, ...]] = array_shapes,
173177
dtype: np.dtype[Any] | None = None,
174-
zarr_formats: st.SearchStrategy[ZarrFormat] | None = zarr_formats,
175-
) -> Any:
178+
zarr_formats: st.SearchStrategy[ZarrFormat] = zarr_formats,
179+
) -> npt.NDArray[Any]:
176180
"""
177181
Generate numpy arrays that can be saved in the provided Zarr format.
178182
"""
@@ -186,7 +190,7 @@ def numpy_arrays(
186190
return draw(npst.arrays(dtype=dtype, shape=shapes))
187191

188192

189-
@st.composite # type: ignore[misc]
193+
@st.composite
190194
def chunk_shapes(draw: st.DrawFn, *, shape: tuple[int, ...]) -> tuple[int, ...]:
191195
# We want this strategy to shrink towards arrays with smaller number of chunks
192196
# 1. st.integers() shrinks towards smaller values. So we use that to generate number of chunks
@@ -208,7 +212,7 @@ def chunk_shapes(draw: st.DrawFn, *, shape: tuple[int, ...]) -> tuple[int, ...]:
208212
return chunks
209213

210214

211-
@st.composite # type: ignore[misc]
215+
@st.composite
212216
def shard_shapes(
213217
draw: st.DrawFn, *, shape: tuple[int, ...], chunk_shape: tuple[int, ...]
214218
) -> tuple[int, ...]:
@@ -220,9 +224,11 @@ def shard_shapes(
220224
return tuple(m * c for m, c in zip(multiples, chunk_shape, strict=True))
221225

222226

223-
@st.composite # type: ignore[misc]
227+
@st.composite
224228
def np_array_and_chunks(
225-
draw: st.DrawFn, *, arrays: st.SearchStrategy[np.ndarray] = numpy_arrays
229+
draw: st.DrawFn,
230+
*,
231+
arrays: st.SearchStrategy[npt.NDArray[Any]] = numpy_arrays(), # noqa: B008
226232
) -> tuple[np.ndarray, tuple[int, ...]]: # type: ignore[type-arg]
227233
"""A hypothesis strategy to generate small sized random arrays.
228234
@@ -232,14 +238,14 @@ def np_array_and_chunks(
232238
return (array, draw(chunk_shapes(shape=array.shape)))
233239

234240

235-
@st.composite # type: ignore[misc]
241+
@st.composite
236242
def arrays(
237243
draw: st.DrawFn,
238244
*,
239245
shapes: st.SearchStrategy[tuple[int, ...]] = array_shapes,
240246
compressors: st.SearchStrategy = compressors,
241247
stores: st.SearchStrategy[StoreLike] = stores,
242-
paths: st.SearchStrategy[str | None] = paths(), # noqa: B008
248+
paths: st.SearchStrategy[str] = paths(), # noqa: B008
243249
array_names: st.SearchStrategy = array_names,
244250
arrays: st.SearchStrategy | None = None,
245251
attrs: st.SearchStrategy = attrs,
@@ -296,7 +302,7 @@ def arrays(
296302
return a
297303

298304

299-
@st.composite # type: ignore[misc]
305+
@st.composite
300306
def simple_arrays(
301307
draw: st.DrawFn,
302308
*,
@@ -317,7 +323,7 @@ def is_negative_slice(idx: Any) -> bool:
317323
return isinstance(idx, slice) and idx.step is not None and idx.step < 0
318324

319325

320-
@st.composite # type: ignore[misc]
326+
@st.composite
321327
def end_slices(draw: st.DrawFn, *, shape: tuple[int]) -> Any:
322328
"""
323329
A strategy that slices ranges that include the last chunk.
@@ -332,23 +338,32 @@ def end_slices(draw: st.DrawFn, *, shape: tuple[int]) -> Any:
332338
return tuple(slicers)
333339

334340

335-
@st.composite # type: ignore[misc]
336-
def basic_indices(draw: st.DrawFn, *, shape: tuple[int], **kwargs: Any) -> Any:
341+
@st.composite
342+
def basic_indices(
343+
draw: st.DrawFn,
344+
*,
345+
shape: tuple[int],
346+
min_dims: int = 0,
347+
max_dims: int | None = None,
348+
allow_newaxis: bool = False,
349+
allow_ellipsis: bool = True,
350+
) -> Any:
337351
"""Basic indices without unsupported negative slices."""
338-
strategy = npst.basic_indices(shape=shape, **kwargs).filter(
339-
lambda idxr: (
340-
not (
341-
is_negative_slice(idxr)
342-
or (isinstance(idxr, tuple) and any(is_negative_slice(idx) for idx in idxr))
343-
)
344-
)
352+
strategy = npst.basic_indices(
353+
shape=shape,
354+
min_dims=min_dims,
355+
max_dims=max_dims,
356+
allow_newaxis=allow_newaxis,
357+
allow_ellipsis=allow_ellipsis,
358+
).filter(
359+
lambda idxr: (not (is_negative_slice(idxr) or any(is_negative_slice(idx) for idx in idxr)))
345360
)
346361
if math.prod(shape) >= 3:
347362
strategy = end_slices(shape=shape) | strategy
348363
return draw(strategy)
349364

350365

351-
@st.composite # type: ignore[misc]
366+
@st.composite
352367
def orthogonal_indices(
353368
draw: st.DrawFn, *, shape: tuple[int]
354369
) -> tuple[tuple[np.ndarray[Any, Any], ...], tuple[np.ndarray[Any, Any], ...]]:
@@ -386,8 +401,8 @@ def orthogonal_indices(
386401

387402

388403
def key_ranges(
389-
keys: SearchStrategy = node_names, max_size: int = sys.maxsize
390-
) -> SearchStrategy[list[int]]:
404+
keys: SearchStrategy[str] = node_names, max_size: int = sys.maxsize
405+
) -> SearchStrategy[list[tuple[str, RangeByteRequest]]]:
391406
"""
392407
Function to generate key_ranges strategy for get_partial_values()
393408
returns list strategy w/ form::

0 commit comments

Comments
 (0)