Skip to content

Commit b9e9f5a

Browse files
authored
FSStore: use ensure_bytes() (zarr-developers#1285)
1 parent 385b5d3 commit b9e9f5a

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

zarr/storage.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
from zarr.util import (buffer_size, json_loads, nolock, normalize_chunks,
5656
normalize_dimension_separator,
5757
normalize_dtype, normalize_fill_value, normalize_order,
58-
normalize_shape, normalize_storage_path, retry_call
59-
)
58+
normalize_shape, normalize_storage_path, retry_call,
59+
ensure_contiguous_ndarray_or_bytes)
6060

6161
from zarr._storage.absstore import ABSStore # noqa: F401
6262
from zarr._storage.store import (_get_hierarchy_metadata, # noqa: F401
@@ -1395,13 +1395,19 @@ def __getitem__(self, key):
13951395
def setitems(self, values):
13961396
if self.mode == 'r':
13971397
raise ReadOnlyError()
1398-
values = {self._normalize_key(key): val for key, val in values.items()}
1398+
1399+
# Normalize keys and make sure the values are bytes
1400+
values = {
1401+
self._normalize_key(key): ensure_contiguous_ndarray_or_bytes(val)
1402+
for key, val in values.items()
1403+
}
13991404
self.map.setitems(values)
14001405

14011406
def __setitem__(self, key, value):
14021407
if self.mode == 'r':
14031408
raise ReadOnlyError()
14041409
key = self._normalize_key(key)
1410+
value = ensure_contiguous_ndarray_or_bytes(value)
14051411
path = self.dir_path(key)
14061412
try:
14071413
if self.fs.isdir(path):

zarr/tests/test_core.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from numpy.testing import assert_array_almost_equal, assert_array_equal
1818
from pkg_resources import parse_version
1919

20+
import zarr
2021
from zarr._storage.store import (
2122
v3_api_available,
2223
)
@@ -3409,3 +3410,27 @@ def test_array_mismatched_store_versions():
34093410
Array(store_v3, path='dataset', read_only=False, chunk_store=chunk_store_v2)
34103411
with pytest.raises(ValueError):
34113412
Array(store_v2, path='dataset', read_only=False, chunk_store=chunk_store_v3)
3413+
3414+
3415+
@pytest.mark.skipif(have_fsspec is False, reason="needs fsspec")
3416+
def test_issue_1279(tmpdir):
3417+
"""See <https://github.com/zarr-developers/zarr-python/issues/1279>"""
3418+
3419+
data = np.arange(25).reshape((5, 5))
3420+
ds = zarr.create(
3421+
shape=data.shape,
3422+
chunks=(5, 5),
3423+
dtype=data.dtype,
3424+
compressor=(None),
3425+
store=FSStore(url=str(tmpdir), mode="a"),
3426+
order="F",
3427+
)
3428+
3429+
ds[:] = data
3430+
3431+
ds_reopened = zarr.open_array(
3432+
store=FSStore(url=str(tmpdir), mode="r")
3433+
)
3434+
3435+
written_data = ds_reopened[:]
3436+
assert_array_equal(data, written_data)

zarr/util.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@
55
from textwrap import TextWrapper
66
import mmap
77
import time
8+
from typing import Any, Callable, Dict, Optional, Tuple, Union
89

910
import numpy as np
1011
from asciitree import BoxStyle, LeftAligned
1112
from asciitree.traversal import Traversal
1213
from collections.abc import Iterable
13-
from numcodecs.compat import ensure_text, ensure_ndarray_like
14+
from numcodecs.compat import (
15+
ensure_text,
16+
ensure_ndarray_like,
17+
ensure_bytes,
18+
ensure_contiguous_ndarray_like
19+
)
20+
from numcodecs.ndarray_like import NDArrayLike
1421
from numcodecs.registry import codec_registry
1522
from numcodecs.blosc import cbuffer_sizes, cbuffer_metainfo
1623

17-
from typing import Any, Callable, Dict, Optional, Tuple, Union
18-
1924

2025
def flatten(arg: Iterable) -> Iterable:
2126
for element in arg:
@@ -696,3 +701,28 @@ def all_equal(value: Any, array: Any):
696701
# using == raises warnings from numpy deprecated pattern, but
697702
# using np.equal() raises type errors for structured dtypes...
698703
return np.all(value == array)
704+
705+
706+
def ensure_contiguous_ndarray_or_bytes(buf) -> Union[NDArrayLike, bytes]:
707+
"""Convenience function to coerce `buf` to ndarray-like array or bytes.
708+
709+
First check if `buf` can be zero-copy converted to a contiguous array.
710+
If not, `buf` will be copied to a newly allocated `bytes` object.
711+
712+
Parameters
713+
----------
714+
buf : ndarray-like, array-like, or bytes-like
715+
A numpy array like object such as numpy.ndarray, cupy.ndarray, or
716+
any object exporting a buffer interface.
717+
718+
Returns
719+
-------
720+
arr : NDArrayLike or bytes
721+
A ndarray-like or bytes object
722+
"""
723+
724+
try:
725+
return ensure_contiguous_ndarray_like(buf)
726+
except TypeError:
727+
# An error is raised if `buf` couldn't be zero-copy converted
728+
return ensure_bytes(buf)

0 commit comments

Comments
 (0)