11import sys
2- from typing import Any
2+ from typing import Any , Literal
33
44import hypothesis .extra .numpy as npst
55import hypothesis .strategies as st
88from hypothesis .strategies import SearchStrategy
99
1010import zarr
11- from zarr .abc .store import RangeByteRequest
11+ from zarr .abc .store import RangeByteRequest , Store
1212from zarr .core .array import Array
1313from zarr .core .common import ZarrFormat
14+ from zarr .core .metadata import ArrayV2Metadata , ArrayV3Metadata
1415from zarr .core .sync import sync
1516from zarr .storage import MemoryStore , StoreLike
1617from zarr .storage ._common import _dereference_path
@@ -67,6 +68,11 @@ def safe_unicode_for_dtype(dtype: np.dtype[np.str_]) -> st.SearchStrategy[str]:
6768 )
6869
6970
71+ def clear_store (x : Store ) -> Store :
72+ sync (x .clear ())
73+ return x
74+
75+
7076# From https://zarr-specs.readthedocs.io/en/latest/v3/core/v3.0.html#node-names
7177# 1. must not be the empty string ("")
7278# 2. must not include the character "/"
@@ -85,12 +91,64 @@ def safe_unicode_for_dtype(dtype: np.dtype[np.str_]) -> st.SearchStrategy[str]:
8591# st.builds will only call a new store constructor for different keyword arguments
8692# i.e. stores.examples() will always return the same object per Store class.
8793# So we map a clear to reset the store.
88- stores = st .builds (MemoryStore , st .just ({})).map (lambda x : sync ( x . clear ()) )
94+ stores = st .builds (MemoryStore , st .just ({})).map (clear_store )
8995compressors = st .sampled_from ([None , "default" ])
9096zarr_formats : st .SearchStrategy [ZarrFormat ] = st .sampled_from ([2 , 3 ])
9197array_shapes = npst .array_shapes (max_dims = 4 , min_side = 0 )
9298
9399
100+ @st .composite # type: ignore[misc]
101+ def dimension_names (draw : st .DrawFn , * , ndim : int | None = None ) -> list [None | str ] | None :
102+ simple_text = st .text (zarr_key_chars , min_size = 0 )
103+ return draw (st .none () | st .lists (st .none () | simple_text , min_size = ndim , max_size = ndim )) # type: ignore[no-any-return]
104+
105+
106+ @st .composite # type: ignore[misc]
107+ def array_metadata (
108+ draw : st .DrawFn ,
109+ * ,
110+ array_shapes : st .SearchStrategy [tuple [int , ...]] = npst .array_shapes ,
111+ zarr_formats : st .SearchStrategy [Literal [2 , 3 ]] = zarr_formats ,
112+ attributes : st .SearchStrategy [dict [str , Any ]] = attrs ,
113+ ) -> ArrayV2Metadata | ArrayV3Metadata :
114+ from zarr .codecs .bytes import BytesCodec
115+ from zarr .core .chunk_grids import RegularChunkGrid
116+ from zarr .core .chunk_key_encodings import DefaultChunkKeyEncoding
117+ from zarr .core .metadata .v3 import ArrayV3Metadata
118+
119+ zarr_format = draw (zarr_formats )
120+ # separator = draw(st.sampled_from(['/', '\\']))
121+ shape = draw (array_shapes ())
122+ ndim = len (shape )
123+ chunk_shape = draw (array_shapes (min_dims = ndim , max_dims = ndim ))
124+ dtype = draw (v3_dtypes ())
125+ fill_value = draw (npst .from_dtype (dtype ))
126+ if zarr_format == 2 :
127+ return ArrayV2Metadata (
128+ shape = shape ,
129+ chunks = chunk_shape ,
130+ dtype = dtype ,
131+ fill_value = fill_value ,
132+ order = draw (st .sampled_from (["C" , "F" ])),
133+ attributes = draw (attributes ),
134+ dimension_separator = draw (st .sampled_from (["." , "/" ])),
135+ filters = None ,
136+ compressor = None ,
137+ )
138+ else :
139+ return ArrayV3Metadata (
140+ shape = shape ,
141+ data_type = dtype ,
142+ chunk_grid = RegularChunkGrid (chunk_shape = chunk_shape ),
143+ fill_value = fill_value ,
144+ attributes = draw (attributes ),
145+ dimension_names = draw (dimension_names (ndim = ndim )),
146+ chunk_key_encoding = DefaultChunkKeyEncoding (separator = "/" ), # FIXME
147+ codecs = [BytesCodec ()],
148+ storage_transformers = (),
149+ )
150+
151+
94152@st .composite # type: ignore[misc]
95153def numpy_arrays (
96154 draw : st .DrawFn ,
0 commit comments