Skip to content

Commit e8800b0

Browse files
Check tests folder with mypy (#2150)
* Check tests folder with mypy * use shapelike * fixup
1 parent 5e113f5 commit e8800b0

File tree

7 files changed

+73
-47
lines changed

7 files changed

+73
-47
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ repos:
2525
rev: v1.11.2
2626
hooks:
2727
- id: mypy
28-
files: src
28+
files: src|tests/v3/test_(api|array|buffer).py
2929
additional_dependencies:
3030
# Package dependencies
3131
- asciitree

src/zarr/core/array.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
ZARRAY_JSON,
2626
ZATTRS_JSON,
2727
ChunkCoords,
28+
ShapeLike,
2829
ZarrFormat,
2930
concurrent_map,
31+
parse_shapelike,
3032
product,
3133
)
3234
from zarr.core.config import config, parse_indexing_order
@@ -116,7 +118,7 @@ async def create(
116118
store: StoreLike,
117119
*,
118120
# v2 and v3
119-
shape: ChunkCoords,
121+
shape: ShapeLike,
120122
dtype: npt.DTypeLike,
121123
zarr_format: ZarrFormat = 3,
122124
fill_value: Any | None = None,
@@ -132,7 +134,7 @@ async def create(
132134
codecs: Iterable[Codec | dict[str, JSON]] | None = None,
133135
dimension_names: Iterable[str] | None = None,
134136
# v2 only
135-
chunks: ChunkCoords | None = None,
137+
chunks: ShapeLike | None = None,
136138
dimension_separator: Literal[".", "/"] | None = None,
137139
order: Literal["C", "F"] | None = None,
138140
filters: list[dict[str, JSON]] | None = None,
@@ -143,9 +145,14 @@ async def create(
143145
) -> AsyncArray:
144146
store_path = await make_store_path(store)
145147

148+
shape = parse_shapelike(shape)
149+
146150
if chunk_shape is None:
147151
if chunks is None:
148152
chunk_shape = chunks = _guess_chunks(shape=shape, typesize=np.dtype(dtype).itemsize)
153+
else:
154+
chunks = parse_shapelike(chunks)
155+
149156
chunk_shape = chunks
150157
elif chunks is not None:
151158
raise ValueError("Only one of chunk_shape or chunks must be provided.")
@@ -217,7 +224,7 @@ async def _create_v3(
217224
cls,
218225
store_path: StorePath,
219226
*,
220-
shape: ChunkCoords,
227+
shape: ShapeLike,
221228
dtype: npt.DTypeLike,
222229
chunk_shape: ChunkCoords,
223230
fill_value: Any | None = None,
@@ -235,6 +242,7 @@ async def _create_v3(
235242
if not exists_ok:
236243
await ensure_no_existing_node(store_path, zarr_format=3)
237244

245+
shape = parse_shapelike(shape)
238246
codecs = list(codecs) if codecs is not None else [BytesCodec()]
239247

240248
if fill_value is None:

src/zarr/core/chunk_grids.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
JSON,
1616
ChunkCoords,
1717
ChunkCoordsLike,
18+
ShapeLike,
1819
parse_named_configuration,
1920
parse_shapelike,
2021
)
@@ -27,7 +28,7 @@
2728

2829

2930
def _guess_chunks(
30-
shape: ChunkCoords,
31+
shape: ShapeLike,
3132
typesize: int,
3233
*,
3334
increment_bytes: int = 256 * 1024,
@@ -57,6 +58,8 @@ def _guess_chunks(
5758
ChunkCoords
5859
5960
"""
61+
if isinstance(shape, int):
62+
shape = (shape,)
6063

6164
ndims = len(shape)
6265
# require chunks to have non-zero length for all dimensions

src/zarr/core/group.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
ZATTRS_JSON,
2323
ZGROUP_JSON,
2424
ChunkCoords,
25+
ShapeLike,
2526
ZarrFormat,
2627
parse_shapelike,
2728
)
@@ -365,7 +366,7 @@ async def create_array(
365366
self,
366367
name: str,
367368
*,
368-
shape: ChunkCoords,
369+
shape: ShapeLike,
369370
dtype: npt.DTypeLike = "float64",
370371
fill_value: Any | None = None,
371372
attributes: dict[str, JSON] | None = None,
@@ -380,7 +381,7 @@ async def create_array(
380381
codecs: Iterable[Codec | dict[str, JSON]] | None = None,
381382
dimension_names: Iterable[str] | None = None,
382383
# v2 only
383-
chunks: ChunkCoords | None = None,
384+
chunks: ShapeLike | None = None,
384385
dimension_separator: Literal[".", "/"] | None = None,
385386
order: Literal["C", "F"] | None = None,
386387
filters: list[dict[str, JSON]] | None = None,
@@ -890,7 +891,7 @@ def create_array(
890891
self,
891892
name: str,
892893
*,
893-
shape: ChunkCoords,
894+
shape: ShapeLike,
894895
dtype: npt.DTypeLike = "float64",
895896
fill_value: Any | None = None,
896897
attributes: dict[str, JSON] | None = None,
@@ -905,7 +906,7 @@ def create_array(
905906
codecs: Iterable[Codec | dict[str, JSON]] | None = None,
906907
dimension_names: Iterable[str] | None = None,
907908
# v2 only
908-
chunks: ChunkCoords | None = None,
909+
chunks: ShapeLike | None = None,
909910
dimension_separator: Literal[".", "/"] | None = None,
910911
order: Literal["C", "F"] | None = None,
911912
filters: list[dict[str, JSON]] | None = None,

tests/v3/package_with_entrypoint/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from numpy import ndarray
44

5-
from zarr.abc.codec import ArrayBytesCodec, CodecInput, CodecPipeline
5+
from zarr.abc.codec import ArrayBytesCodec, CodecInput, CodecOutput, CodecPipeline
66
from zarr.codecs import BytesCodec
77
from zarr.core.array_spec import ArraySpec
88
from zarr.core.buffer import Buffer, NDBuffer
@@ -15,7 +15,7 @@ class TestEntrypointCodec(ArrayBytesCodec):
1515
async def encode(
1616
self,
1717
chunks_and_specs: Iterable[tuple[CodecInput | None, ArraySpec]],
18-
) -> BytesLike | None:
18+
) -> Iterable[CodecOutput | None]:
1919
pass
2020

2121
async def decode(

tests/v3/test_api.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import pathlib
2+
13
import numpy as np
24
import pytest
35
from numpy.testing import assert_array_equal
4-
from pytest_asyncio import fixture
56

67
import zarr
78
from zarr import Array, Group
89
from zarr.abc.store import Store
910
from zarr.api.synchronous import create, load, open, open_group, save, save_array, save_group
11+
from zarr.store.memory import MemoryStore
1012

1113

1214
def test_create_array(memory_store: Store) -> None:
@@ -30,7 +32,7 @@ def test_create_array(memory_store: Store) -> None:
3032
assert z.chunks == (40,)
3133

3234

33-
async def test_open_array(memory_store: Store) -> None:
35+
async def test_open_array(memory_store: MemoryStore) -> None:
3436
store = memory_store
3537

3638
# open array, create if doesn't exist
@@ -57,7 +59,7 @@ async def test_open_array(memory_store: Store) -> None:
5759
open(store="doesnotexist", mode="r")
5860

5961

60-
async def test_open_group(memory_store: Store) -> None:
62+
async def test_open_group(memory_store: MemoryStore) -> None:
6163
store = memory_store
6264

6365
# open group, create if doesn't exist
@@ -85,59 +87,65 @@ def test_save_errors() -> None:
8587
save_group("data/group.zarr")
8688
with pytest.raises(TypeError):
8789
# no array provided
88-
save_array("data/group.zarr")
90+
save_array("data/group.zarr") # type: ignore[call-arg]
8991
with pytest.raises(ValueError):
9092
# no arrays provided
9193
save("data/group.zarr")
9294

9395

94-
@fixture
95-
def tmppath(tmpdir):
96-
return str(tmpdir / "example.zarr")
97-
98-
99-
def test_open_with_mode_r(tmppath) -> None:
96+
def test_open_with_mode_r(tmp_path: pathlib.Path) -> None:
10097
# 'r' means read only (must exist)
10198
with pytest.raises(FileNotFoundError):
102-
zarr.open(store=tmppath, mode="r")
103-
zarr.ones(store=tmppath, shape=(3, 3))
104-
z2 = zarr.open(store=tmppath, mode="r")
99+
zarr.open(store=tmp_path, mode="r")
100+
zarr.ones(store=tmp_path, shape=(3, 3))
101+
z2 = zarr.open(store=tmp_path, mode="r")
102+
assert isinstance(z2, Array)
105103
assert (z2[:] == 1).all()
106104
with pytest.raises(ValueError):
107105
z2[:] = 3
108106

109107

110-
def test_open_with_mode_r_plus(tmppath) -> None:
108+
def test_open_with_mode_r_plus(tmp_path: pathlib.Path) -> None:
111109
# 'r+' means read/write (must exist)
112110
with pytest.raises(FileNotFoundError):
113-
zarr.open(store=tmppath, mode="r+")
114-
zarr.ones(store=tmppath, shape=(3, 3))
115-
z2 = zarr.open(store=tmppath, mode="r+")
111+
zarr.open(store=tmp_path, mode="r+")
112+
zarr.ones(store=tmp_path, shape=(3, 3))
113+
z2 = zarr.open(store=tmp_path, mode="r+")
114+
assert isinstance(z2, Array)
116115
assert (z2[:] == 1).all()
117116
z2[:] = 3
118117

119118

120-
def test_open_with_mode_a(tmppath) -> None:
119+
def test_open_with_mode_a(tmp_path: pathlib.Path) -> None:
121120
# 'a' means read/write (create if doesn't exist)
122-
zarr.open(store=tmppath, mode="a", shape=(3, 3))[...] = 1
123-
z2 = zarr.open(store=tmppath, mode="a")
121+
arr = zarr.open(store=tmp_path, mode="a", shape=(3, 3))
122+
assert isinstance(arr, Array)
123+
arr[...] = 1
124+
z2 = zarr.open(store=tmp_path, mode="a")
125+
assert isinstance(z2, Array)
124126
assert (z2[:] == 1).all()
125127
z2[:] = 3
126128

127129

128-
def test_open_with_mode_w(tmppath) -> None:
130+
def test_open_with_mode_w(tmp_path: pathlib.Path) -> None:
129131
# 'w' means create (overwrite if exists);
130-
zarr.open(store=tmppath, mode="w", shape=(3, 3))[...] = 3
131-
z2 = zarr.open(store=tmppath, mode="w", shape=(3, 3))
132+
arr = zarr.open(store=tmp_path, mode="w", shape=(3, 3))
133+
assert isinstance(arr, Array)
134+
135+
arr[...] = 3
136+
z2 = zarr.open(store=tmp_path, mode="w", shape=(3, 3))
137+
assert isinstance(z2, Array)
132138
assert not (z2[:] == 3).all()
133139
z2[:] = 3
134140

135141

136-
def test_open_with_mode_w_minus(tmppath) -> None:
142+
def test_open_with_mode_w_minus(tmp_path: pathlib.Path) -> None:
137143
# 'w-' means create (fail if exists)
138-
zarr.open(store=tmppath, mode="w-", shape=(3, 3))[...] = 1
144+
arr = zarr.open(store=tmp_path, mode="w-", shape=(3, 3))
145+
assert isinstance(arr, Array)
146+
arr[...] = 1
139147
with pytest.raises(FileExistsError):
140-
zarr.open(store=tmppath, mode="w-")
148+
zarr.open(store=tmp_path, mode="w-")
141149

142150

143151
# def test_lazy_loader():

tests/v3/test_buffer.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import types
4+
35
import numpy as np
46
import pytest
57

@@ -27,14 +29,14 @@
2729
cp = None
2830

2931

30-
def test_nd_array_like(xp):
32+
def test_nd_array_like(xp: types.ModuleType) -> None:
3133
ary = xp.arange(10)
3234
assert isinstance(ary, ArrayLike)
3335
assert isinstance(ary, NDArrayLike)
3436

3537

3638
@pytest.mark.asyncio
37-
async def test_async_array_prototype():
39+
async def test_async_array_prototype() -> None:
3840
"""Test the use of a custom buffer prototype"""
3941

4042
expect = np.zeros((9, 9), dtype="uint16", order="F")
@@ -55,13 +57,15 @@ async def test_async_array_prototype():
5557
prototype=my_prototype,
5658
)
5759
got = await a.getitem(selection=(slice(0, 9), slice(0, 9)), prototype=my_prototype)
58-
assert isinstance(got, TestNDArrayLike)
59-
assert np.array_equal(expect, got)
60+
# ignoring a mypy error here that TestNDArrayLike doesn't meet the NDArrayLike protocol
61+
# The test passes, so it clearly does.
62+
assert isinstance(got, TestNDArrayLike) # type: ignore[unreachable]
63+
assert np.array_equal(expect, got) # type: ignore[unreachable]
6064

6165

6266
@gpu_test
6367
@pytest.mark.asyncio
64-
async def test_async_array_gpu_prototype():
68+
async def test_async_array_gpu_prototype() -> None:
6569
"""Test the use of the GPU buffer prototype"""
6670

6771
expect = cp.zeros((9, 9), dtype="uint16", order="F")
@@ -85,7 +89,7 @@ async def test_async_array_gpu_prototype():
8589

8690

8791
@pytest.mark.asyncio
88-
async def test_codecs_use_of_prototype():
92+
async def test_codecs_use_of_prototype() -> None:
8993
expect = np.zeros((10, 10), dtype="uint16", order="F")
9094
a = await AsyncArray.create(
9195
StorePath(StoreExpectingTestBuffer(mode="w")) / "test_codecs_use_of_prototype",
@@ -112,13 +116,15 @@ async def test_codecs_use_of_prototype():
112116
prototype=my_prototype,
113117
)
114118
got = await a.getitem(selection=(slice(0, 10), slice(0, 10)), prototype=my_prototype)
115-
assert isinstance(got, TestNDArrayLike)
116-
assert np.array_equal(expect, got)
119+
# ignoring a mypy error here that TestNDArrayLike doesn't meet the NDArrayLike protocol
120+
# The test passes, so it clearly does.
121+
assert isinstance(got, TestNDArrayLike) # type: ignore[unreachable]
122+
assert np.array_equal(expect, got) # type: ignore[unreachable]
117123

118124

119125
@gpu_test
120126
@pytest.mark.asyncio
121-
async def test_codecs_use_of_gpu_prototype():
127+
async def test_codecs_use_of_gpu_prototype() -> None:
122128
expect = cp.zeros((10, 10), dtype="uint16", order="F")
123129
a = await AsyncArray.create(
124130
StorePath(MemoryStore(mode="w")) / "test_codecs_use_of_gpu_prototype",
@@ -147,7 +153,7 @@ async def test_codecs_use_of_gpu_prototype():
147153
assert cp.array_equal(expect, got)
148154

149155

150-
def test_numpy_buffer_prototype():
156+
def test_numpy_buffer_prototype() -> None:
151157
buffer = cpu.buffer_prototype.buffer.create_zero_length()
152158
ndbuffer = cpu.buffer_prototype.nd_buffer.create(shape=(1, 2), dtype=np.dtype("int64"))
153159
assert isinstance(buffer.as_array_like(), np.ndarray)

0 commit comments

Comments
 (0)