|
| 1 | +import json |
| 2 | +from collections.abc import Iterator |
| 3 | +from typing import Any, Literal |
| 4 | + |
| 5 | +import numcodecs.vlen |
| 6 | +import numpy as np |
| 7 | +import pytest |
| 8 | +from numcodecs import Delta |
| 9 | +from numcodecs.blosc import Blosc |
| 10 | + |
| 11 | +import zarr |
| 12 | +import zarr.core.buffer |
| 13 | +import zarr.storage |
| 14 | +from zarr import Array |
| 15 | +from zarr.storage import MemoryStore, StorePath |
| 16 | + |
| 17 | + |
| 18 | +@pytest.fixture |
| 19 | +async def store() -> Iterator[StorePath]: |
| 20 | + return StorePath(await MemoryStore.open()) |
| 21 | + |
| 22 | + |
| 23 | +def test_simple(store: StorePath) -> None: |
| 24 | + data = np.arange(0, 256, dtype="uint16").reshape((16, 16)) |
| 25 | + |
| 26 | + a = Array.create( |
| 27 | + store / "simple_v2", |
| 28 | + zarr_format=2, |
| 29 | + shape=data.shape, |
| 30 | + chunks=(16, 16), |
| 31 | + dtype=data.dtype, |
| 32 | + fill_value=0, |
| 33 | + ) |
| 34 | + |
| 35 | + a[:, :] = data |
| 36 | + assert np.array_equal(data, a[:, :]) |
| 37 | + |
| 38 | + |
| 39 | +@pytest.mark.parametrize("store", ["memory"], indirect=True) |
| 40 | +@pytest.mark.parametrize( |
| 41 | + ("dtype", "fill_value"), |
| 42 | + [ |
| 43 | + ("bool", False), |
| 44 | + ("int64", 0), |
| 45 | + ("float64", 0.0), |
| 46 | + ("|S1", b""), |
| 47 | + ("|U1", ""), |
| 48 | + ("object", ""), |
| 49 | + (str, ""), |
| 50 | + ], |
| 51 | +) |
| 52 | +def test_implicit_fill_value(store: MemoryStore, dtype: str, fill_value: Any) -> None: |
| 53 | + arr = zarr.create(store=store, shape=(4,), fill_value=None, zarr_format=2, dtype=dtype) |
| 54 | + assert arr.metadata.fill_value is None |
| 55 | + assert arr.metadata.to_dict()["fill_value"] is None |
| 56 | + result = arr[:] |
| 57 | + if dtype is str: |
| 58 | + # special case |
| 59 | + numpy_dtype = np.dtype(object) |
| 60 | + else: |
| 61 | + numpy_dtype = np.dtype(dtype) |
| 62 | + expected = np.full(arr.shape, fill_value, dtype=numpy_dtype) |
| 63 | + np.testing.assert_array_equal(result, expected) |
| 64 | + |
| 65 | + |
| 66 | +def test_codec_pipeline() -> None: |
| 67 | + # https://github.com/zarr-developers/zarr-python/issues/2243 |
| 68 | + store = MemoryStore() |
| 69 | + array = zarr.create( |
| 70 | + store=store, |
| 71 | + shape=(1,), |
| 72 | + dtype="i4", |
| 73 | + zarr_format=2, |
| 74 | + filters=[Delta(dtype="i4").get_config()], |
| 75 | + compressor=Blosc().get_config(), |
| 76 | + ) |
| 77 | + array[:] = 1 |
| 78 | + result = array[:] |
| 79 | + expected = np.ones(1) |
| 80 | + np.testing.assert_array_equal(result, expected) |
| 81 | + |
| 82 | + |
| 83 | +@pytest.mark.parametrize("dtype", ["|S", "|V"]) |
| 84 | +async def test_v2_encode_decode(dtype): |
| 85 | + store = zarr.storage.MemoryStore() |
| 86 | + g = zarr.group(store=store, zarr_format=2) |
| 87 | + g.create_array( |
| 88 | + name="foo", |
| 89 | + shape=(3,), |
| 90 | + chunks=(3,), |
| 91 | + dtype=dtype, |
| 92 | + fill_value=b"X", |
| 93 | + ) |
| 94 | + |
| 95 | + result = await store.get("foo/.zarray", zarr.core.buffer.default_buffer_prototype()) |
| 96 | + assert result is not None |
| 97 | + |
| 98 | + serialized = json.loads(result.to_bytes()) |
| 99 | + expected = { |
| 100 | + "chunks": [3], |
| 101 | + "compressor": None, |
| 102 | + "dtype": f"{dtype}0", |
| 103 | + "fill_value": "WA==", |
| 104 | + "filters": None, |
| 105 | + "order": "C", |
| 106 | + "shape": [3], |
| 107 | + "zarr_format": 2, |
| 108 | + "dimension_separator": ".", |
| 109 | + } |
| 110 | + assert serialized == expected |
| 111 | + |
| 112 | + data = zarr.open_array(store=store, path="foo")[:] |
| 113 | + expected = np.full((3,), b"X", dtype=dtype) |
| 114 | + np.testing.assert_equal(data, expected) |
| 115 | + |
| 116 | + |
| 117 | +@pytest.mark.parametrize("dtype", [str, "str"]) |
| 118 | +async def test_create_dtype_str(dtype: Any) -> None: |
| 119 | + arr = zarr.create(shape=3, dtype=dtype, zarr_format=2) |
| 120 | + assert arr.dtype.kind == "O" |
| 121 | + assert arr.metadata.to_dict()["dtype"] == "|O" |
| 122 | + assert arr.metadata.filters == (numcodecs.vlen.VLenUTF8(),) |
| 123 | + arr[:] = ["a", "bb", "ccc"] |
| 124 | + result = arr[:] |
| 125 | + np.testing.assert_array_equal(result, np.array(["a", "bb", "ccc"], dtype="object")) |
| 126 | + |
| 127 | + |
| 128 | +@pytest.mark.parametrize("filters", [[], [numcodecs.Delta(dtype="<i4")], [numcodecs.Zlib(level=2)]]) |
| 129 | +@pytest.mark.parametrize("order", ["C", "F"]) |
| 130 | +def test_v2_filters_codecs(filters: Any, order: Literal["C", "F"]) -> None: |
| 131 | + array_fixture = [42] |
| 132 | + arr = zarr.create(shape=1, dtype="<i4", zarr_format=2, filters=filters, order=order) |
| 133 | + arr[:] = array_fixture |
| 134 | + result = arr[:] |
| 135 | + np.testing.assert_array_equal(result, array_fixture) |
| 136 | + |
| 137 | + |
| 138 | +@pytest.mark.parametrize("array_order", ["C", "F"]) |
| 139 | +@pytest.mark.parametrize("data_order", ["C", "F"]) |
| 140 | +def test_v2_non_contiguous(array_order: Literal["C", "F"], data_order: Literal["C", "F"]) -> None: |
| 141 | + arr = zarr.Array.create( |
| 142 | + MemoryStore({}), |
| 143 | + shape=(10, 8), |
| 144 | + chunks=(3, 3), |
| 145 | + fill_value=np.nan, |
| 146 | + dtype="float64", |
| 147 | + zarr_format=2, |
| 148 | + exists_ok=True, |
| 149 | + order=array_order, |
| 150 | + ) |
| 151 | + |
| 152 | + # Non-contiguous write |
| 153 | + a = np.arange(arr.shape[0] * arr.shape[1]).reshape(arr.shape, order=data_order) |
| 154 | + arr[slice(6, 9, None), slice(3, 6, None)] = a[ |
| 155 | + slice(6, 9, None), slice(3, 6, None) |
| 156 | + ] # The slice on the RHS is important |
| 157 | + np.testing.assert_array_equal( |
| 158 | + arr[slice(6, 9, None), slice(3, 6, None)], a[slice(6, 9, None), slice(3, 6, None)] |
| 159 | + ) |
| 160 | + |
| 161 | + arr = zarr.Array.create( |
| 162 | + MemoryStore({}), |
| 163 | + shape=(10, 8), |
| 164 | + chunks=(3, 3), |
| 165 | + fill_value=np.nan, |
| 166 | + dtype="float64", |
| 167 | + zarr_format=2, |
| 168 | + exists_ok=True, |
| 169 | + order=array_order, |
| 170 | + ) |
| 171 | + |
| 172 | + # Contiguous write |
| 173 | + a = np.arange(9).reshape((3, 3), order=data_order) |
| 174 | + if data_order == "F": |
| 175 | + assert a.flags.f_contiguous |
| 176 | + else: |
| 177 | + assert a.flags.c_contiguous |
| 178 | + arr[slice(6, 9, None), slice(3, 6, None)] = a |
| 179 | + np.testing.assert_array_equal(arr[slice(6, 9, None), slice(3, 6, None)], a) |
0 commit comments