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
12+ from zarr .codecs .bytes import BytesCodec
1213from zarr .core .array import Array
14+ from zarr .core .chunk_grids import RegularChunkGrid
15+ from zarr .core .chunk_key_encodings import DefaultChunkKeyEncoding
1316from zarr .core .common import ZarrFormat
17+ from zarr .core .metadata import ArrayV2Metadata , ArrayV3Metadata
1418from zarr .core .sync import sync
1519from zarr .storage import MemoryStore , StoreLike
1620from zarr .storage ._common import _dereference_path
@@ -67,6 +71,11 @@ def safe_unicode_for_dtype(dtype: np.dtype[np.str_]) -> st.SearchStrategy[str]:
6771 )
6872
6973
74+ def clear_store (x : Store ) -> Store :
75+ sync (x .clear ())
76+ return x
77+
78+
7079# From https://zarr-specs.readthedocs.io/en/latest/v3/core/v3.0.html#node-names
7180# 1. must not be the empty string ("")
7281# 2. must not include the character "/"
@@ -85,12 +94,59 @@ def safe_unicode_for_dtype(dtype: np.dtype[np.str_]) -> st.SearchStrategy[str]:
8594# st.builds will only call a new store constructor for different keyword arguments
8695# i.e. stores.examples() will always return the same object per Store class.
8796# So we map a clear to reset the store.
88- stores = st .builds (MemoryStore , st .just ({})).map (lambda x : sync ( x . clear ()) )
97+ stores = st .builds (MemoryStore , st .just ({})).map (clear_store )
8998compressors = st .sampled_from ([None , "default" ])
9099zarr_formats : st .SearchStrategy [ZarrFormat ] = st .sampled_from ([2 , 3 ])
91100array_shapes = npst .array_shapes (max_dims = 4 , min_side = 0 )
92101
93102
103+ @st .composite # type: ignore[misc]
104+ def dimension_names (draw : st .DrawFn , * , ndim : int | None = None ) -> list [None | str ] | None :
105+ simple_text = st .text (zarr_key_chars , min_size = 0 )
106+ return draw (st .none () | st .lists (st .none () | simple_text , min_size = ndim , max_size = ndim )) # type: ignore[no-any-return]
107+
108+
109+ @st .composite # type: ignore[misc]
110+ def array_metadata (
111+ draw : st .DrawFn ,
112+ * ,
113+ array_shapes : st .SearchStrategy [tuple [int , ...]] = npst .array_shapes ,
114+ zarr_formats : st .SearchStrategy [Literal [2 , 3 ]] = zarr_formats ,
115+ attributes : st .SearchStrategy [dict [str , Any ]] = attrs ,
116+ ) -> ArrayV2Metadata | ArrayV3Metadata :
117+ zarr_format = draw (zarr_formats )
118+ # separator = draw(st.sampled_from(['/', '\\']))
119+ shape = draw (array_shapes ())
120+ ndim = len (shape )
121+ chunk_shape = draw (array_shapes (min_dims = ndim , max_dims = ndim ))
122+ dtype = draw (v3_dtypes ())
123+ fill_value = draw (npst .from_dtype (dtype ))
124+ if zarr_format == 2 :
125+ return ArrayV2Metadata (
126+ shape = shape ,
127+ chunks = chunk_shape ,
128+ dtype = dtype ,
129+ fill_value = fill_value ,
130+ order = draw (st .sampled_from (["C" , "F" ])),
131+ attributes = draw (attributes ),
132+ dimension_separator = draw (st .sampled_from (["." , "/" ])),
133+ filters = None ,
134+ compressor = None ,
135+ )
136+ else :
137+ return ArrayV3Metadata (
138+ shape = shape ,
139+ data_type = dtype ,
140+ chunk_grid = RegularChunkGrid (chunk_shape = chunk_shape ),
141+ fill_value = fill_value ,
142+ attributes = draw (attributes ),
143+ dimension_names = draw (dimension_names (ndim = ndim )),
144+ chunk_key_encoding = DefaultChunkKeyEncoding (separator = "/" ), # FIXME
145+ codecs = [BytesCodec ()],
146+ storage_transformers = (),
147+ )
148+
149+
94150@st .composite # type: ignore[misc]
95151def numpy_arrays (
96152 draw : st .DrawFn ,
0 commit comments