Skip to content

Commit fff280c

Browse files
authored
Merge branch 'main' into feat/batch-creation
2 parents a1e75b9 + 8b77464 commit fff280c

File tree

6 files changed

+67
-13
lines changed

6 files changed

+67
-13
lines changed

changes/2755.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The array returned by ``zarr.empty`` and an empty ``zarr.core.buffer.cpu.NDBuffer`` will now be filled with the
2+
specified fill value, or with zeros if no fill value is provided.
3+
This fixes a bug where Zarr format 2 data with no fill value was written with un-predictable chunk sizes.

src/zarr/api/asynchronous.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,8 @@ async def create(
10771077
async def empty(
10781078
shape: ChunkCoords, **kwargs: Any
10791079
) -> AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]:
1080-
"""Create an empty array.
1080+
"""Create an empty array with the specified shape. The contents will be filled with the
1081+
array's fill value or zeros if no fill value is provided.
10811082
10821083
Parameters
10831084
----------
@@ -1099,7 +1100,8 @@ async def empty(
10991100
async def empty_like(
11001101
a: ArrayLike, **kwargs: Any
11011102
) -> AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]:
1102-
"""Create an empty array like `a`.
1103+
"""Create an empty array like `a`. The contents will be filled with the
1104+
array's fill value or zeros if no fill value is provided.
11031105
11041106
Parameters
11051107
----------
@@ -1112,6 +1114,12 @@ async def empty_like(
11121114
-------
11131115
Array
11141116
The new array.
1117+
1118+
Notes
1119+
-----
1120+
The contents of an empty Zarr array are not defined. On attempting to
1121+
retrieve data from an empty Zarr array, any values may be returned,
1122+
and these are not guaranteed to be stable from one access to the next.
11151123
"""
11161124
like_kwargs = _like_args(a, kwargs)
11171125
return await empty(**like_kwargs)

src/zarr/api/synchronous.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,8 @@ def create_array(
909909

910910
# TODO: add type annotations for kwargs
911911
def empty(shape: ChunkCoords, **kwargs: Any) -> Array:
912-
"""Create an empty array.
912+
"""Create an empty array with the specified shape. The contents will be filled with the
913+
array's fill value or zeros if no fill value is provided.
913914
914915
Parameters
915916
----------
@@ -935,7 +936,8 @@ def empty(shape: ChunkCoords, **kwargs: Any) -> Array:
935936
# TODO: move ArrayLike to common module
936937
# TODO: add type annotations for kwargs
937938
def empty_like(a: ArrayLike, **kwargs: Any) -> Array:
938-
"""Create an empty array like another array.
939+
"""Create an empty array like another array. The contents will be filled with the
940+
array's fill value or zeros if no fill value is provided.
939941
940942
Parameters
941943
----------
@@ -948,6 +950,12 @@ def empty_like(a: ArrayLike, **kwargs: Any) -> Array:
948950
-------
949951
Array
950952
The new array.
953+
954+
Notes
955+
-----
956+
The contents of an empty Zarr array are not defined. On attempting to
957+
retrieve data from an empty Zarr array, any values may be returned,
958+
and these are not guaranteed to be stable from one access to the next.
951959
"""
952960
return Array(sync(async_api.empty_like(a, **kwargs)))
953961

src/zarr/core/buffer/cpu.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ def create(
154154
order: Literal["C", "F"] = "C",
155155
fill_value: Any | None = None,
156156
) -> Self:
157-
ret = cls(np.empty(shape=tuple(shape), dtype=dtype, order=order))
158-
if fill_value is not None:
159-
ret.fill(fill_value)
160-
return ret
157+
if fill_value is None:
158+
return cls(np.zeros(shape=tuple(shape), dtype=dtype, order=order))
159+
else:
160+
return cls(np.full(shape=tuple(shape), fill_value=fill_value, dtype=dtype, order=order))
161161

162162
@classmethod
163163
def from_numpy_array(cls, array_like: npt.ArrayLike) -> Self:

src/zarr/core/group.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,8 @@ async def tree(self, expand: bool | None = None, level: int | None = None) -> An
15411541
async def empty(
15421542
self, *, name: str, shape: ChunkCoords, **kwargs: Any
15431543
) -> AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]:
1544-
"""Create an empty array in this Group.
1544+
"""Create an empty array with the specified shape in this Group. The contents will
1545+
be filled with the array's fill value or zeros if no fill value is provided.
15451546
15461547
Parameters
15471548
----------
@@ -1558,7 +1559,6 @@ async def empty(
15581559
retrieve data from an empty Zarr array, any values may be returned,
15591560
and these are not guaranteed to be stable from one access to the next.
15601561
"""
1561-
15621562
return await async_api.empty(shape=shape, store=self.store_path, path=name, **kwargs)
15631563

15641564
async def zeros(
@@ -1635,7 +1635,8 @@ async def full(
16351635
async def empty_like(
16361636
self, *, name: str, data: async_api.ArrayLike, **kwargs: Any
16371637
) -> AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]:
1638-
"""Create an empty sub-array like `data`.
1638+
"""Create an empty sub-array like `data`. The contents will be filled with
1639+
the array's fill value or zeros if no fill value is provided.
16391640
16401641
Parameters
16411642
----------
@@ -2528,7 +2529,8 @@ def require_array(self, name: str, *, shape: ShapeLike, **kwargs: Any) -> Array:
25282529

25292530
@_deprecate_positional_args
25302531
def empty(self, *, name: str, shape: ChunkCoords, **kwargs: Any) -> Array:
2531-
"""Create an empty array in this Group.
2532+
"""Create an empty array with the specified shape in this Group. The contents will be filled with
2533+
the array's fill value or zeros if no fill value is provided.
25322534
25332535
Parameters
25342536
----------
@@ -2617,7 +2619,8 @@ def full(
26172619

26182620
@_deprecate_positional_args
26192621
def empty_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) -> Array:
2620-
"""Create an empty sub-array like `data`.
2622+
"""Create an empty sub-array like `data`. The contents will be filled
2623+
with the array's fill value or zeros if no fill value is provided.
26212624
26222625
Parameters
26232626
----------
@@ -2632,6 +2635,12 @@ def empty_like(self, *, name: str, data: async_api.ArrayLike, **kwargs: Any) ->
26322635
-------
26332636
Array
26342637
The new array.
2638+
2639+
Notes
2640+
-----
2641+
The contents of an empty Zarr array are not defined. On attempting to
2642+
retrieve data from an empty Zarr array, any values may be returned,
2643+
and these are not guaranteed to be stable from one access to the next.
26352644
"""
26362645
return Array(self._sync(self._async_group.empty_like(name=name, data=data, **kwargs)))
26372646

tests/test_store/test_memory.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
from __future__ import annotations
22

3+
from typing import TYPE_CHECKING
4+
5+
import numpy as np
36
import pytest
47

8+
import zarr
59
from zarr.core.buffer import Buffer, cpu, gpu
610
from zarr.storage import GpuMemoryStore, MemoryStore
711
from zarr.testing.store import StoreTests
812
from zarr.testing.utils import gpu_test
913

14+
if TYPE_CHECKING:
15+
from zarr.core.common import ZarrFormat
16+
1017

1118
class TestMemoryStore(StoreTests[MemoryStore, cpu.Buffer]):
1219
store_cls = MemoryStore
@@ -46,6 +53,25 @@ def test_store_supports_partial_writes(self, store: MemoryStore) -> None:
4653
def test_list_prefix(self, store: MemoryStore) -> None:
4754
assert True
4855

56+
@pytest.mark.parametrize("dtype", ["uint8", "float32", "int64"])
57+
@pytest.mark.parametrize("zarr_format", [2, 3])
58+
async def test_deterministic_size(
59+
self, store: MemoryStore, dtype, zarr_format: ZarrFormat
60+
) -> None:
61+
a = zarr.empty(
62+
store=store,
63+
shape=(3,),
64+
chunks=(1000,),
65+
dtype=dtype,
66+
zarr_format=zarr_format,
67+
overwrite=True,
68+
)
69+
a[...] = 1
70+
a.resize((1000,))
71+
72+
np.testing.assert_array_equal(a[:3], 1)
73+
np.testing.assert_array_equal(a[3:], 0)
74+
4975

5076
@gpu_test
5177
class TestGpuMemoryStore(StoreTests[GpuMemoryStore, gpu.Buffer]):

0 commit comments

Comments
 (0)