|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -import pickle |
4 | 3 | from typing import TYPE_CHECKING |
5 | 4 |
|
6 | | -import numpy as np |
7 | 5 | import pytest |
8 | 6 |
|
9 | | -import numcodecs.bitround |
10 | | - |
11 | 7 | if TYPE_CHECKING: # pragma: no cover |
12 | 8 | import zarr |
13 | 9 | else: |
14 | | - zarr = pytest.importorskip("zarr") |
15 | | - |
16 | | -import zarr.storage |
17 | | -from zarr.core.common import JSON |
18 | | - |
19 | | -import numcodecs.zarr3 |
20 | | - |
21 | | -pytestmark = [ |
22 | | - pytest.mark.skipif(zarr.__version__ < "3.0.0", reason="zarr 3.0.0 or later is required"), |
23 | | - pytest.mark.filterwarnings("ignore:Codec 'numcodecs.*' not configured in config.*:UserWarning"), |
24 | | - pytest.mark.filterwarnings( |
25 | | - "ignore:Numcodecs codecs are not in the Zarr version 3 specification and may not be supported by other zarr implementations." |
26 | | - ), |
| 10 | + zarr = pytest.importorskip("zarr", "3.1.3") |
| 11 | +import numcodecs.zarr3 as zarr3 |
| 12 | + |
| 13 | +codec_names = [ |
| 14 | + "BZ2", |
| 15 | + "CRC32", |
| 16 | + "CRC32C", |
| 17 | + "LZ4", |
| 18 | + "LZMA", |
| 19 | + "ZFPY", |
| 20 | + "Adler32", |
| 21 | + "AsType", |
| 22 | + "BitRound", |
| 23 | + "Blosc", |
| 24 | + "Delta", |
| 25 | + "FixedScaleOffset", |
| 26 | + "Fletcher32", |
| 27 | + "GZip", |
| 28 | + "JenkinsLookup3", |
| 29 | + "PCodec", |
| 30 | + "PackBits", |
| 31 | + "Quantize", |
| 32 | + "Shuffle", |
| 33 | + "Zlib", |
| 34 | + "Zstd", |
27 | 35 | ] |
28 | 36 |
|
29 | | -get_codec_class = zarr.registry.get_codec_class |
30 | | -Array = zarr.Array |
31 | | -BytesCodec = zarr.codecs.BytesCodec |
32 | | -Store = zarr.abc.store.Store |
33 | | -MemoryStore = zarr.storage.MemoryStore |
34 | | -StorePath = zarr.storage.StorePath |
35 | | - |
36 | | - |
37 | | -EXPECTED_WARNING_STR = "Numcodecs codecs are not in the Zarr version 3.*" |
38 | | - |
39 | | - |
40 | | -@pytest.fixture |
41 | | -def store() -> StorePath: |
42 | | - return StorePath(MemoryStore(read_only=False)) |
43 | | - |
44 | | - |
45 | | -ALL_CODECS = [getattr(numcodecs.zarr3, cls_name) for cls_name in numcodecs.zarr3.__all__] |
46 | | - |
47 | | - |
48 | | -@pytest.mark.parametrize("codec_class", ALL_CODECS) |
49 | | -def test_entry_points(codec_class: type[numcodecs.zarr3._NumcodecsCodec]): |
50 | | - codec_name = codec_class.codec_name |
51 | | - assert get_codec_class(codec_name) == codec_class |
52 | | - |
53 | | - |
54 | | -@pytest.mark.parametrize("codec_class", ALL_CODECS) |
55 | | -def test_docstring(codec_class: type[numcodecs.zarr3._NumcodecsCodec]): |
56 | | - assert "See :class:`numcodecs." in codec_class.__doc__ # type: ignore[operator] |
57 | | - |
58 | | - |
59 | | -@pytest.mark.parametrize( |
60 | | - "codec_class", |
61 | | - [ |
62 | | - numcodecs.zarr3.Blosc, |
63 | | - numcodecs.zarr3.LZ4, |
64 | | - numcodecs.zarr3.Zstd, |
65 | | - numcodecs.zarr3.Zlib, |
66 | | - numcodecs.zarr3.GZip, |
67 | | - numcodecs.zarr3.BZ2, |
68 | | - numcodecs.zarr3.LZMA, |
69 | | - numcodecs.zarr3.Shuffle, |
70 | | - ], |
71 | | -) |
72 | | -def test_generic_compressor( |
73 | | - store: StorePath, codec_class: type[numcodecs.zarr3._NumcodecsBytesBytesCodec] |
74 | | -): |
75 | | - data = np.arange(0, 256, dtype="uint16").reshape((16, 16)) |
76 | | - |
77 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
78 | | - a = zarr.create_array( |
79 | | - store / "generic", |
80 | | - shape=data.shape, |
81 | | - chunks=(16, 16), |
82 | | - dtype=data.dtype, |
83 | | - fill_value=0, |
84 | | - compressors=[codec_class()], |
85 | | - ) |
86 | | - |
87 | | - a[:, :] = data.copy() |
88 | | - np.testing.assert_array_equal(data, a[:, :]) |
89 | | - |
90 | | - |
91 | | -@pytest.mark.parametrize( |
92 | | - ("codec_class", "codec_config"), |
93 | | - [ |
94 | | - (numcodecs.zarr3.Delta, {"dtype": "float32"}), |
95 | | - (numcodecs.zarr3.FixedScaleOffset, {"offset": 0, "scale": 25.5}), |
96 | | - (numcodecs.zarr3.FixedScaleOffset, {"offset": 0, "scale": 51, "astype": "uint16"}), |
97 | | - (numcodecs.zarr3.AsType, {"encode_dtype": "float32", "decode_dtype": "float32"}), |
98 | | - ], |
99 | | - ids=[ |
100 | | - "delta", |
101 | | - "fixedscaleoffset", |
102 | | - "fixedscaleoffset2", |
103 | | - "astype", |
104 | | - ], |
105 | | -) |
106 | | -def test_generic_filter( |
107 | | - store: StorePath, |
108 | | - codec_class: type[numcodecs.zarr3._NumcodecsArrayArrayCodec], |
109 | | - codec_config: dict[str, JSON], |
110 | | -): |
111 | | - data = np.linspace(0, 10, 256, dtype="float32").reshape((16, 16)) |
112 | | - |
113 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
114 | | - a = zarr.create_array( |
115 | | - store / "generic", |
116 | | - shape=data.shape, |
117 | | - chunks=(16, 16), |
118 | | - dtype=data.dtype, |
119 | | - fill_value=0, |
120 | | - filters=[ |
121 | | - codec_class(**codec_config), |
122 | | - ], |
123 | | - ) |
124 | | - |
125 | | - a[:, :] = data.copy() |
126 | | - a = zarr.open_array(store / "generic", mode="r") |
127 | | - np.testing.assert_array_equal(data, a[:, :]) |
128 | | - |
129 | | - |
130 | | -def test_generic_filter_bitround(store: StorePath): |
131 | | - data = np.linspace(0, 1, 256, dtype="float32").reshape((16, 16)) |
132 | | - |
133 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
134 | | - a = zarr.create_array( |
135 | | - store / "generic_bitround", |
136 | | - shape=data.shape, |
137 | | - chunks=(16, 16), |
138 | | - dtype=data.dtype, |
139 | | - fill_value=0, |
140 | | - filters=[numcodecs.zarr3.BitRound(keepbits=3)], |
141 | | - ) |
142 | | - |
143 | | - a[:, :] = data.copy() |
144 | | - a = zarr.open_array(store / "generic_bitround", mode="r") |
145 | | - assert np.allclose(data, a[:, :], atol=0.1) |
146 | | - |
147 | | - |
148 | | -def test_generic_filter_quantize(store: StorePath): |
149 | | - data = np.linspace(0, 10, 256, dtype="float32").reshape((16, 16)) |
150 | | - |
151 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
152 | | - a = zarr.create_array( |
153 | | - store / "generic_quantize", |
154 | | - shape=data.shape, |
155 | | - chunks=(16, 16), |
156 | | - dtype=data.dtype, |
157 | | - fill_value=0, |
158 | | - filters=[numcodecs.zarr3.Quantize(digits=3)], |
159 | | - ) |
160 | | - |
161 | | - a[:, :] = data.copy() |
162 | | - a = zarr.open_array(store / "generic_quantize", mode="r") |
163 | | - assert np.allclose(data, a[:, :], atol=0.001) |
164 | | - |
165 | | - |
166 | | -def test_generic_filter_packbits(store: StorePath): |
167 | | - data = np.zeros((16, 16), dtype="bool") |
168 | | - data[0:4, :] = True |
169 | | - |
170 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
171 | | - a = zarr.create_array( |
172 | | - store / "generic_packbits", |
173 | | - shape=data.shape, |
174 | | - chunks=(16, 16), |
175 | | - dtype=data.dtype, |
176 | | - fill_value=0, |
177 | | - filters=[numcodecs.zarr3.PackBits()], |
178 | | - ) |
179 | | - |
180 | | - a[:, :] = data.copy() |
181 | | - a = zarr.open_array(store / "generic_packbits", mode="r") |
182 | | - np.testing.assert_array_equal(data, a[:, :]) |
183 | | - |
184 | | - with pytest.raises(ValueError, match=".*requires bool dtype.*"): |
185 | | - zarr.create_array( |
186 | | - store / "generic_packbits_err", |
187 | | - shape=data.shape, |
188 | | - chunks=(16, 16), |
189 | | - dtype="uint32", |
190 | | - fill_value=0, |
191 | | - filters=[numcodecs.zarr3.PackBits()], |
192 | | - ) |
193 | | - |
194 | | - |
195 | | -@pytest.mark.parametrize( |
196 | | - "codec_class", |
197 | | - [ |
198 | | - numcodecs.zarr3.CRC32, |
199 | | - numcodecs.zarr3.CRC32C, |
200 | | - numcodecs.zarr3.Adler32, |
201 | | - numcodecs.zarr3.Fletcher32, |
202 | | - numcodecs.zarr3.JenkinsLookup3, |
203 | | - ], |
204 | | -) |
205 | | -def test_generic_checksum( |
206 | | - store: StorePath, codec_class: type[numcodecs.zarr3._NumcodecsBytesBytesCodec] |
207 | | -): |
208 | | - data = np.linspace(0, 10, 256, dtype="float32").reshape((16, 16)) |
209 | | - |
210 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
211 | | - a = zarr.create_array( |
212 | | - store / "generic_checksum", |
213 | | - shape=data.shape, |
214 | | - chunks=(16, 16), |
215 | | - dtype=data.dtype, |
216 | | - fill_value=0, |
217 | | - compressors=[codec_class()], |
218 | | - ) |
219 | | - |
220 | | - a[:, :] = data.copy() |
221 | | - a = zarr.open_array(store / "generic_checksum", mode="r") |
222 | | - np.testing.assert_array_equal(data, a[:, :]) |
223 | | - |
224 | | - |
225 | | -@pytest.mark.parametrize("codec_class", [numcodecs.zarr3.PCodec, numcodecs.zarr3.ZFPY]) |
226 | | -def test_generic_bytes_codec( |
227 | | - store: StorePath, codec_class: type[numcodecs.zarr3._NumcodecsArrayBytesCodec] |
228 | | -): |
229 | | - try: |
230 | | - codec_class()._codec # noqa: B018 |
231 | | - except ValueError as e: # pragma: no cover |
232 | | - if "codec not available" in str(e): |
233 | | - pytest.xfail(f"{codec_class.codec_name} is not available: {e}") |
234 | | - else: |
235 | | - raise |
236 | | - except ImportError as e: # pragma: no cover |
237 | | - pytest.xfail(f"{codec_class.codec_name} is not available: {e}") |
238 | | - |
239 | | - data = np.arange(0, 256, dtype="float32").reshape((16, 16)) |
240 | | - |
241 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
242 | | - a = zarr.create_array( |
243 | | - store / "generic", |
244 | | - shape=data.shape, |
245 | | - chunks=(16, 16), |
246 | | - dtype=data.dtype, |
247 | | - fill_value=0, |
248 | | - serializer=codec_class(), |
249 | | - ) |
250 | | - |
251 | | - a[:, :] = data.copy() |
252 | | - np.testing.assert_array_equal(data, a[:, :]) |
253 | | - |
254 | | - |
255 | | -def test_delta_astype(store: StorePath): |
256 | | - data = np.linspace(0, 10, 256, dtype="i8").reshape((16, 16)) |
257 | | - |
258 | | - with pytest.warns(UserWarning, match=EXPECTED_WARNING_STR): |
259 | | - a = zarr.create_array( |
260 | | - store / "generic", |
261 | | - shape=data.shape, |
262 | | - chunks=(16, 16), |
263 | | - dtype=data.dtype, |
264 | | - fill_value=0, |
265 | | - filters=[ |
266 | | - numcodecs.zarr3.Delta(dtype="i8", astype="i2"), |
267 | | - ], |
268 | | - ) |
269 | | - |
270 | | - a[:, :] = data.copy() |
271 | | - a = zarr.open_array(store / "generic", mode="r") |
272 | | - np.testing.assert_array_equal(data, a[:, :]) |
273 | | - |
274 | | - |
275 | | -def test_repr(): |
276 | | - codec = numcodecs.zarr3.LZ4(level=5) |
277 | | - assert repr(codec) == "LZ4(codec_name='numcodecs.lz4', codec_config={'level': 5})" |
278 | | - |
279 | | - |
280 | | -def test_to_dict(): |
281 | | - codec = numcodecs.zarr3.LZ4(level=5) |
282 | | - assert codec.to_dict() == {"name": "numcodecs.lz4", "configuration": {"level": 5}} |
283 | | - |
284 | | - |
285 | | -@pytest.mark.parametrize( |
286 | | - "codec_cls", |
287 | | - [ |
288 | | - numcodecs.zarr3.Blosc, |
289 | | - numcodecs.zarr3.LZ4, |
290 | | - numcodecs.zarr3.Zstd, |
291 | | - numcodecs.zarr3.Zlib, |
292 | | - numcodecs.zarr3.GZip, |
293 | | - numcodecs.zarr3.BZ2, |
294 | | - numcodecs.zarr3.LZMA, |
295 | | - numcodecs.zarr3.Shuffle, |
296 | | - numcodecs.zarr3.BitRound, |
297 | | - numcodecs.zarr3.Delta, |
298 | | - numcodecs.zarr3.FixedScaleOffset, |
299 | | - numcodecs.zarr3.Quantize, |
300 | | - numcodecs.zarr3.PackBits, |
301 | | - numcodecs.zarr3.AsType, |
302 | | - numcodecs.zarr3.CRC32, |
303 | | - numcodecs.zarr3.CRC32C, |
304 | | - numcodecs.zarr3.Adler32, |
305 | | - numcodecs.zarr3.Fletcher32, |
306 | | - numcodecs.zarr3.JenkinsLookup3, |
307 | | - numcodecs.zarr3.PCodec, |
308 | | - numcodecs.zarr3.ZFPY, |
309 | | - ], |
310 | | -) |
311 | | -def test_codecs_pickleable(codec_cls): |
312 | | - codec = codec_cls() |
313 | | - |
314 | | - expected = codec |
315 | 37 |
|
316 | | - p = pickle.dumps(codec) |
317 | | - actual = pickle.loads(p) |
318 | | - assert actual == expected |
| 38 | +@pytest.mark.parametrize('codec_name', codec_names) |
| 39 | +def test_export(codec_name: str) -> None: |
| 40 | + """ |
| 41 | + Ensure that numcodecs.zarr3 re-exports codecs defined in zarr.codecs.numcodecs |
| 42 | + """ |
| 43 | + assert getattr(zarr3, codec_name) == getattr(zarr.codecs.numcodecs, codec_name) |
0 commit comments