Skip to content

Commit 8826415

Browse files
authored
Additional testing for AsyncArray, Array (#3049)
* remove duplicate metadata parsing * add test cases * add test cases * tests for different zarr_formats in test_storage_transformers * tests for different zarr_formats in test_storage_transformers * ignore mypy arg-type error for deprecation test * fix typing in tests * test_chunk_key_encoding * test_invalid_v2_arguments * test_array_repr * type annotation for parse_array_metadata * test_v2_and_v3_exist_at_same_path * remove duplicate check for dimension_separator in v3 * tests for invalid arguments in creation * format * revert typing * document changes
1 parent aa33415 commit 8826415

File tree

5 files changed

+213
-59
lines changed

5 files changed

+213
-59
lines changed

changes/3049.misc.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added tests for ``AsyncArray``, ``Array`` and removed duplicate argument parsing.

src/zarr/api/asynchronous.py

-5
Original file line numberDiff line numberDiff line change
@@ -1019,11 +1019,6 @@ async def create(
10191019
warnings.warn("object_codec is not yet implemented", RuntimeWarning, stacklevel=2)
10201020
if read_only is not None:
10211021
warnings.warn("read_only is not yet implemented", RuntimeWarning, stacklevel=2)
1022-
if dimension_separator is not None and zarr_format == 3:
1023-
raise ValueError(
1024-
"dimension_separator is not supported for zarr format 3, use chunk_key_encoding instead"
1025-
)
1026-
10271022
if order is not None:
10281023
_warn_order_kwarg()
10291024
if write_empty_chunks is not None:

src/zarr/core/array.py

+7-16
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ def parse_array_metadata(data: Any) -> ArrayMetadata:
140140
if isinstance(data, ArrayMetadata):
141141
return data
142142
elif isinstance(data, dict):
143-
if data["zarr_format"] == 3:
143+
zarr_format = data.get("zarr_format")
144+
if zarr_format == 3:
144145
meta_out = ArrayV3Metadata.from_dict(data)
145146
if len(meta_out.storage_transformers) > 0:
146147
msg = (
@@ -149,9 +150,11 @@ def parse_array_metadata(data: Any) -> ArrayMetadata:
149150
)
150151
raise ValueError(msg)
151152
return meta_out
152-
elif data["zarr_format"] == 2:
153+
elif zarr_format == 2:
153154
return ArrayV2Metadata.from_dict(data)
154-
raise TypeError
155+
else:
156+
raise ValueError(f"Invalid zarr_format: {zarr_format}. Expected 2 or 3")
157+
raise TypeError # pragma: no cover
155158

156159

157160
def create_codec_pipeline(metadata: ArrayMetadata) -> CodecPipeline:
@@ -160,8 +163,7 @@ def create_codec_pipeline(metadata: ArrayMetadata) -> CodecPipeline:
160163
elif isinstance(metadata, ArrayV2Metadata):
161164
v2_codec = V2Codec(filters=metadata.filters, compressor=metadata.compressor)
162165
return get_pipeline_class().from_codecs([v2_codec])
163-
else:
164-
raise TypeError
166+
raise TypeError # pragma: no cover
165167

166168

167169
async def get_array_metadata(
@@ -268,17 +270,6 @@ def __init__(
268270
store_path: StorePath,
269271
config: ArrayConfigLike | None = None,
270272
) -> None:
271-
if isinstance(metadata, dict):
272-
zarr_format = metadata["zarr_format"]
273-
# TODO: remove this when we extensively type the dict representation of metadata
274-
_metadata = cast(dict[str, JSON], metadata)
275-
if zarr_format == 2:
276-
metadata = ArrayV2Metadata.from_dict(_metadata)
277-
elif zarr_format == 3:
278-
metadata = ArrayV3Metadata.from_dict(_metadata)
279-
else:
280-
raise ValueError(f"Invalid zarr_format: {zarr_format}. Expected 2 or 3")
281-
282273
metadata_parsed = parse_array_metadata(metadata)
283274
config_parsed = parse_array_config(config)
284275

tests/test_api.py

+46-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import re
34
from typing import TYPE_CHECKING
45

56
import zarr.codecs
@@ -72,13 +73,19 @@ def test_create(memory_store: Store) -> None:
7273

7374
# TODO: parametrize over everything this function takes
7475
@pytest.mark.parametrize("store", ["memory"], indirect=True)
75-
def test_create_array(store: Store) -> None:
76+
def test_create_array(store: Store, zarr_format: ZarrFormat) -> None:
7677
attrs: dict[str, JSON] = {"foo": 100} # explicit type annotation to avoid mypy error
7778
shape = (10, 10)
7879
path = "foo"
7980
data_val = 1
8081
array_w = create_array(
81-
store, name=path, shape=shape, attributes=attrs, chunks=shape, dtype="uint8"
82+
store,
83+
name=path,
84+
shape=shape,
85+
attributes=attrs,
86+
chunks=shape,
87+
dtype="uint8",
88+
zarr_format=zarr_format,
8289
)
8390
array_w[:] = data_val
8491
assert array_w.shape == shape
@@ -87,18 +94,27 @@ def test_create_array(store: Store) -> None:
8794

8895

8996
@pytest.mark.parametrize("write_empty_chunks", [True, False])
90-
def test_write_empty_chunks_warns(write_empty_chunks: bool) -> None:
97+
def test_write_empty_chunks_warns(write_empty_chunks: bool, zarr_format: ZarrFormat) -> None:
9198
"""
9299
Test that using the `write_empty_chunks` kwarg on array access will raise a warning.
93100
"""
94101
match = "The `write_empty_chunks` keyword argument .*"
95102
with pytest.warns(RuntimeWarning, match=match):
96103
_ = zarr.array(
97-
data=np.arange(10), shape=(10,), dtype="uint8", write_empty_chunks=write_empty_chunks
104+
data=np.arange(10),
105+
shape=(10,),
106+
dtype="uint8",
107+
write_empty_chunks=write_empty_chunks,
108+
zarr_format=zarr_format,
98109
)
99110

100111
with pytest.warns(RuntimeWarning, match=match):
101-
_ = zarr.create(shape=(10,), dtype="uint8", write_empty_chunks=write_empty_chunks)
112+
_ = zarr.create(
113+
shape=(10,),
114+
dtype="uint8",
115+
write_empty_chunks=write_empty_chunks,
116+
zarr_format=zarr_format,
117+
)
102118

103119

104120
@pytest.mark.parametrize("path", ["foo", "/", "/foo", "///foo/bar"])
@@ -115,18 +131,18 @@ def test_open_normalized_path(
115131
assert node.path == normalize_path(path)
116132

117133

118-
async def test_open_array(memory_store: MemoryStore) -> None:
134+
async def test_open_array(memory_store: MemoryStore, zarr_format: ZarrFormat) -> None:
119135
store = memory_store
120136

121137
# open array, create if doesn't exist
122-
z = open(store=store, shape=100)
138+
z = open(store=store, shape=100, zarr_format=zarr_format)
123139
assert isinstance(z, Array)
124140
assert z.shape == (100,)
125141

126142
# open array, overwrite
127143
# store._store_dict = {}
128144
store = MemoryStore()
129-
z = open(store=store, shape=200)
145+
z = open(store=store, shape=200, zarr_format=zarr_format)
130146
assert isinstance(z, Array)
131147
assert z.shape == (200,)
132148

@@ -140,7 +156,16 @@ async def test_open_array(memory_store: MemoryStore) -> None:
140156

141157
# path not found
142158
with pytest.raises(FileNotFoundError):
143-
open(store="doesnotexist", mode="r")
159+
open(store="doesnotexist", mode="r", zarr_format=zarr_format)
160+
161+
162+
@pytest.mark.parametrize("store", ["memory", "local", "zip"], indirect=True)
163+
def test_v2_and_v3_exist_at_same_path(store: Store) -> None:
164+
zarr.create_array(store, shape=(10,), dtype="uint8", zarr_format=3)
165+
zarr.create_array(store, shape=(10,), dtype="uint8", zarr_format=2)
166+
msg = f"Both zarr.json (Zarr format 3) and .zarray (Zarr format 2) metadata objects exist at {store}. Zarr v3 will be used."
167+
with pytest.warns(UserWarning, match=re.escape(msg)):
168+
zarr.open(store=store, mode="r")
144169

145170

146171
@pytest.mark.parametrize("store", ["memory"], indirect=True)
@@ -163,9 +188,9 @@ async def test_open_group(memory_store: MemoryStore) -> None:
163188
assert "foo" in g
164189

165190
# open group, overwrite
166-
# g = open_group(store=store)
167-
# assert isinstance(g, Group)
168-
# assert "foo" not in g
191+
g = open_group(store=store, mode="w")
192+
assert isinstance(g, Group)
193+
assert "foo" not in g
169194

170195
# open group, read-only
171196
store_cls = type(store)
@@ -308,7 +333,6 @@ def test_open_with_mode_w_minus(tmp_path: pathlib.Path) -> None:
308333
zarr.open(store=tmp_path, mode="w-")
309334

310335

311-
@pytest.mark.parametrize("zarr_format", [2, 3])
312336
def test_array_order(zarr_format: ZarrFormat) -> None:
313337
arr = zarr.ones(shape=(2, 2), order=None, zarr_format=zarr_format)
314338
expected = zarr.config.get("array.order")
@@ -324,7 +348,6 @@ def test_array_order(zarr_format: ZarrFormat) -> None:
324348

325349

326350
@pytest.mark.parametrize("order", ["C", "F"])
327-
@pytest.mark.parametrize("zarr_format", [2, 3])
328351
def test_array_order_warns(order: MemoryOrder | None, zarr_format: ZarrFormat) -> None:
329352
with pytest.warns(RuntimeWarning, match="The `order` keyword argument .*"):
330353
arr = zarr.ones(shape=(2, 2), order=order, zarr_format=zarr_format)
@@ -1095,13 +1118,16 @@ def test_open_falls_back_to_open_group() -> None:
10951118
assert group.attrs == {"key": "value"}
10961119

10971120

1098-
async def test_open_falls_back_to_open_group_async() -> None:
1121+
async def test_open_falls_back_to_open_group_async(zarr_format: ZarrFormat) -> None:
10991122
# https://github.com/zarr-developers/zarr-python/issues/2309
11001123
store = MemoryStore()
1101-
await zarr.api.asynchronous.open_group(store, attributes={"key": "value"})
1124+
await zarr.api.asynchronous.open_group(
1125+
store, attributes={"key": "value"}, zarr_format=zarr_format
1126+
)
11021127

11031128
group = await zarr.api.asynchronous.open(store=store)
11041129
assert isinstance(group, zarr.core.group.AsyncGroup)
1130+
assert group.metadata.zarr_format == zarr_format
11051131
assert group.attrs == {"key": "value"}
11061132

11071133

@@ -1137,13 +1163,14 @@ async def test_metadata_validation_error() -> None:
11371163
["local", "memory", "zip"],
11381164
indirect=True,
11391165
)
1140-
def test_open_array_with_mode_r_plus(store: Store) -> None:
1166+
def test_open_array_with_mode_r_plus(store: Store, zarr_format: ZarrFormat) -> None:
11411167
# 'r+' means read/write (must exist)
11421168
with pytest.raises(FileNotFoundError):
1143-
zarr.open_array(store=store, mode="r+")
1144-
zarr.ones(store=store, shape=(3, 3))
1169+
zarr.open_array(store=store, mode="r+", zarr_format=zarr_format)
1170+
zarr.ones(store=store, shape=(3, 3), zarr_format=zarr_format)
11451171
z2 = zarr.open_array(store=store, mode="r+")
11461172
assert isinstance(z2, Array)
1173+
assert z2.metadata.zarr_format == zarr_format
11471174
result = z2[:]
11481175
assert isinstance(result, NDArrayLike)
11491176
assert (result == 1).all()

0 commit comments

Comments
 (0)