Skip to content

Commit 437234a

Browse files
committed
Merge branch 'v3' of https://github.com/zarr-developers/zarr-python into v3-main-sync-merge
2 parents 7bcb92b + 124640a commit 437234a

File tree

11 files changed

+77
-11
lines changed

11 files changed

+77
-11
lines changed
File renamed without changes.

docs/guide/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Guide
2+
=====
3+
4+
.. toctree::
5+
:maxdepth: 1
6+
7+
consolidated_metadata

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Zarr-Python
1010

1111
getting_started
1212
tutorial
13-
consolidated_metadata
13+
guide/index
1414
api/index
1515
spec
1616
release

src/zarr/codecs/_v2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async def _decode_single(
3636
chunk_numpy_array = ensure_ndarray(chunk_bytes.as_array_like())
3737

3838
# ensure correct dtype
39-
if str(chunk_numpy_array.dtype) != chunk_spec.dtype:
39+
if str(chunk_numpy_array.dtype) != chunk_spec.dtype and not chunk_spec.dtype.hasobject:
4040
chunk_numpy_array = chunk_numpy_array.view(chunk_spec.dtype)
4141

4242
return get_ndbuffer_class().from_numpy_array(chunk_numpy_array)

src/zarr/core/array.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
ShapeLike,
3636
ZarrFormat,
3737
concurrent_map,
38+
parse_dtype,
3839
parse_shapelike,
3940
product,
4041
)
@@ -365,16 +366,17 @@ async def create(
365366
) -> AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]:
366367
store_path = await make_store_path(store)
367368

369+
dtype_parsed = parse_dtype(dtype, zarr_format)
368370
shape = parse_shapelike(shape)
369371

370372
if chunks is not None and chunk_shape is not None:
371373
raise ValueError("Only one of chunk_shape or chunks can be provided.")
372374

373-
dtype = np.dtype(dtype)
374375
if chunks:
375-
_chunks = normalize_chunks(chunks, shape, dtype.itemsize)
376+
_chunks = normalize_chunks(chunks, shape, dtype_parsed.itemsize)
376377
else:
377-
_chunks = normalize_chunks(chunk_shape, shape, dtype.itemsize)
378+
_chunks = normalize_chunks(chunk_shape, shape, dtype_parsed.itemsize)
379+
378380
result: AsyncArray[ArrayV3Metadata] | AsyncArray[ArrayV2Metadata]
379381
if zarr_format == 3:
380382
if dimension_separator is not None:
@@ -396,7 +398,7 @@ async def create(
396398
result = await cls._create_v3(
397399
store_path,
398400
shape=shape,
399-
dtype=dtype,
401+
dtype=dtype_parsed,
400402
chunk_shape=_chunks,
401403
fill_value=fill_value,
402404
chunk_key_encoding=chunk_key_encoding,
@@ -406,6 +408,14 @@ async def create(
406408
exists_ok=exists_ok,
407409
)
408410
elif zarr_format == 2:
411+
if dtype is str or dtype == "str":
412+
# another special case: zarr v2 added the vlen-utf8 codec
413+
vlen_codec: dict[str, JSON] = {"id": "vlen-utf8"}
414+
if filters and not any(x["id"] == "vlen-utf8" for x in filters):
415+
filters = list(filters) + [vlen_codec]
416+
else:
417+
filters = [vlen_codec]
418+
409419
if codecs is not None:
410420
raise ValueError(
411421
"codecs cannot be used for arrays with version 2. Use filters and compressor instead."
@@ -419,7 +429,7 @@ async def create(
419429
result = await cls._create_v2(
420430
store_path,
421431
shape=shape,
422-
dtype=dtype,
432+
dtype=dtype_parsed,
423433
chunks=_chunks,
424434
dimension_separator=dimension_separator,
425435
fill_value=fill_value,

src/zarr/core/common.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
overload,
1515
)
1616

17+
import numpy as np
18+
19+
from zarr.core.strings import _STRING_DTYPE
20+
1721
if TYPE_CHECKING:
1822
from collections.abc import Awaitable, Callable, Iterator
1923

@@ -151,3 +155,13 @@ def parse_order(data: Any) -> Literal["C", "F"]:
151155
if data in ("C", "F"):
152156
return cast(Literal["C", "F"], data)
153157
raise ValueError(f"Expected one of ('C', 'F'), got {data} instead.")
158+
159+
160+
def parse_dtype(dtype: Any, zarr_format: ZarrFormat) -> np.dtype[Any]:
161+
if dtype is str or dtype == "str":
162+
if zarr_format == 2:
163+
# special case as object
164+
return np.dtype("object")
165+
else:
166+
return _STRING_DTYPE
167+
return np.dtype(dtype)

src/zarr/core/metadata/v2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ def _default_fill_value(dtype: np.dtype[Any]) -> Any:
321321
"""
322322
if dtype.kind == "S":
323323
return b""
324-
elif dtype.kind == "U":
324+
elif dtype.kind in "UO":
325325
return ""
326326
else:
327327
return dtype.type(0)

src/zarr/storage/local.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ def __init__(self, root: Path | str, *, mode: AccessModeLiteral = "r") -> None:
9494
assert isinstance(root, Path)
9595
self.root = root
9696

97+
async def _open(self) -> None:
98+
if not self.mode.readonly:
99+
self.root.mkdir(parents=True, exist_ok=True)
100+
return await super()._open()
101+
97102
async def clear(self) -> None:
98103
self._check_writable()
99104
shutil.rmtree(self.root)

tests/v3/test_codecs/test_vlen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from zarr.core.strings import _NUMPY_SUPPORTS_VLEN_STRING
1212
from zarr.storage.common import StorePath
1313

14-
numpy_str_dtypes: list[type | str | None] = [None, str, np.dtypes.StrDType]
14+
numpy_str_dtypes: list[type | str | None] = [None, str, "str", np.dtypes.StrDType]
1515
expected_zarr_string_dtype: np.dtype[Any]
1616
if _NUMPY_SUPPORTS_VLEN_STRING:
1717
numpy_str_dtypes.append(np.dtypes.StringDType)

tests/v3/test_store/test_local.py

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

3+
from typing import TYPE_CHECKING
4+
35
import pytest
46

7+
import zarr
58
from zarr.core.buffer import Buffer, cpu
69
from zarr.storage.local import LocalStore
710
from zarr.testing.store import StoreTests
811

12+
if TYPE_CHECKING:
13+
import pathlib
14+
915

1016
class TestLocalStore(StoreTests[LocalStore, cpu.Buffer]):
1117
store_cls = LocalStore
@@ -40,3 +46,10 @@ async def test_empty_with_empty_subdir(self, store: LocalStore) -> None:
4046
assert await store.empty()
4147
(store.root / "foo/bar").mkdir(parents=True)
4248
assert await store.empty()
49+
50+
def test_creates_new_directory(self, tmp_path: pathlib.Path):
51+
target = tmp_path.joinpath("a", "b", "c")
52+
assert not target.exists()
53+
54+
store = self.store_cls(root=target, mode="w")
55+
zarr.group(store=store)

0 commit comments

Comments
 (0)