Skip to content

Commit 437d0e1

Browse files
committed
Fix order handling
1 parent d19f3f0 commit 437d0e1

File tree

12 files changed

+80
-100
lines changed

12 files changed

+80
-100
lines changed

changes/xxx1.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed the error message when passing both ``config`` and ``write_emtpy_chunks`` arguments to reflect the current behaviour (``write_empty_chunks`` takes precedence).

changes/xxx2.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Creating a Zarr format 2 array with the ``order`` keyword argument no longer raises a warning.

changes/xxx3.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Creating a Zarr format 3 array with the ``order`` argument now conistently ignores this argument and raises a warning.

changes/xxx4.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When using ``from_array`` to copy a Zarr format 2 array to a Zarr format 3 array, if the memory order of the input array is ``"F"`` a warning is raised and the order ignored.
2+
This is because Zarr format 3 arrays are always stored in "C" order.

changes/xxxx.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The ``config`` argument to `zarr.create` (and functions that create arrays) is now used - previously it had no effect.

src/zarr/api/asynchronous.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from_array,
1818
get_array_metadata,
1919
)
20-
from zarr.core.array_spec import ArrayConfig, ArrayConfigLike, ArrayConfigParams
20+
from zarr.core.array_spec import ArrayConfigLike, parse_array_config
2121
from zarr.core.buffer import NDArrayLike
2222
from zarr.core.common import (
2323
JSON,
@@ -27,7 +27,6 @@
2727
MemoryOrder,
2828
ZarrFormat,
2929
_default_zarr_format,
30-
_warn_order_kwarg,
3130
_warn_write_empty_chunks_kwarg,
3231
parse_dtype,
3332
)
@@ -1013,8 +1012,6 @@ async def create(
10131012
warnings.warn("object_codec is not yet implemented", RuntimeWarning, stacklevel=2)
10141013
if read_only is not None:
10151014
warnings.warn("read_only is not yet implemented", RuntimeWarning, stacklevel=2)
1016-
if order is not None:
1017-
_warn_order_kwarg()
10181015
if write_empty_chunks is not None:
10191016
_warn_write_empty_chunks_kwarg()
10201017

@@ -1026,26 +1023,17 @@ async def create(
10261023
mode = "a"
10271024
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
10281025

1029-
config_dict: ArrayConfigParams = {}
1026+
config_parsed = parse_array_config(config)
10301027

10311028
if write_empty_chunks is not None:
10321029
if config is not None:
10331030
msg = (
10341031
"Both write_empty_chunks and config keyword arguments are set. "
1035-
"This is redundant. When both are set, write_empty_chunks will be ignored and "
1036-
"config will be used."
1032+
"This is redundant. When both are set, write_empty_chunks will be used instead "
1033+
"of the value in config."
10371034
)
10381035
warnings.warn(UserWarning(msg), stacklevel=1)
1039-
config_dict["write_empty_chunks"] = write_empty_chunks
1040-
if order is not None and config is not None:
1041-
msg = (
1042-
"Both order and config keyword arguments are set. "
1043-
"This is redundant. When both are set, order will be ignored and "
1044-
"config will be used."
1045-
)
1046-
warnings.warn(UserWarning(msg), stacklevel=1)
1047-
1048-
config_parsed = ArrayConfig.from_dict(config_dict)
1036+
config_parsed = dataclasses.replace(config_parsed, write_empty_chunks=write_empty_chunks)
10491037

10501038
return await AsyncArray._create(
10511039
store_path,
@@ -1248,8 +1236,6 @@ async def open_array(
12481236

12491237
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
12501238

1251-
if "order" in kwargs:
1252-
_warn_order_kwarg()
12531239
if "write_empty_chunks" in kwargs:
12541240
_warn_write_empty_chunks_kwarg()
12551241

src/zarr/core/array.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
_warn_order_kwarg,
6363
concurrent_map,
6464
parse_dtype,
65-
parse_order,
6665
parse_shapelike,
6766
product,
6867
)
@@ -603,7 +602,6 @@ async def _create(
603602

604603
if order is not None:
605604
_warn_order_kwarg()
606-
config_parsed = replace(config_parsed, order=order)
607605

608606
result = await cls._create_v3(
609607
store_path,
@@ -631,9 +629,10 @@ async def _create(
631629
raise ValueError("dimension_names cannot be used for arrays with zarr_format 2.")
632630

633631
if order is None:
634-
order_parsed = parse_order(zarr_config.get("array.order"))
632+
order_parsed = config_parsed.order
635633
else:
636634
order_parsed = order
635+
config_parsed = replace(config_parsed, order=order)
637636

638637
result = await cls._create_v2(
639638
store_path,
@@ -721,6 +720,7 @@ async def _create_v3(
721720
attributes: dict[str, JSON] | None = None,
722721
overwrite: bool = False,
723722
) -> AsyncArray[ArrayV3Metadata]:
723+
print(config)
724724
if overwrite:
725725
if store_path.store.supports_deletes:
726726
await store_path.delete_dir()
@@ -4026,6 +4026,7 @@ async def from_array(
40264026
chunk_key_encoding=chunk_key_encoding,
40274027
dimension_names=dimension_names,
40284028
)
4029+
print(order)
40294030
if not hasattr(data, "dtype") or not hasattr(data, "shape"):
40304031
data = np.array(data)
40314032

@@ -4267,10 +4268,8 @@ async def init_array(
42674268
chunks_out = chunk_shape_parsed
42684269
codecs_out = sub_codecs
42694270

4270-
if config is None:
4271-
config = {}
4272-
if order is not None and isinstance(config, dict):
4273-
config["order"] = config.get("order", order)
4271+
if order is not None:
4272+
_warn_order_kwarg()
42744273

42754274
meta = AsyncArray._create_metadata_v3(
42764275
shape=shape_parsed,
@@ -4521,15 +4520,26 @@ def _parse_keep_array_attr(
45214520
serializer = "auto"
45224521
if fill_value is None:
45234522
fill_value = data.fill_value
4524-
if order is None:
4523+
4524+
if data.metadata.zarr_format == 2 and zarr_format == 3 and data.order == "F":
4525+
# Can't set order="F" for v3 arrays
4526+
warnings.warn(
4527+
"Zarr format 3 arrays are always stored with order='C'. "
4528+
"The existing order='F' of the source Zarr format 2 array will be ignored.",
4529+
UserWarning,
4530+
stacklevel=2,
4531+
)
4532+
elif order is None and zarr_format == 2:
45254533
order = data.order
4534+
45264535
if chunk_key_encoding is None and zarr_format == data.metadata.zarr_format:
45274536
if isinstance(data.metadata, ArrayV2Metadata):
45284537
chunk_key_encoding = {"name": "v2", "separator": data.metadata.dimension_separator}
45294538
elif isinstance(data.metadata, ArrayV3Metadata):
45304539
chunk_key_encoding = data.metadata.chunk_key_encoding
45314540
if dimension_names is None and data.metadata.zarr_format == 3:
45324541
dimension_names = data.metadata.dimension_names
4542+
print(order)
45334543
else:
45344544
if chunks == "keep":
45354545
chunks = "auto"

src/zarr/core/group.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,7 +2417,7 @@ def create_array(
24172417
compressor: CompressorLike = "auto",
24182418
serializer: SerializerLike = "auto",
24192419
fill_value: Any | None = 0,
2420-
order: MemoryOrder | None = "C",
2420+
order: MemoryOrder | None = None,
24212421
attributes: dict[str, JSON] | None = None,
24222422
chunk_key_encoding: ChunkKeyEncodingLike | None = None,
24232423
dimension_names: DimensionNames = None,
@@ -2811,7 +2811,7 @@ def array(
28112811
compressor: CompressorLike = None,
28122812
serializer: SerializerLike = "auto",
28132813
fill_value: Any | None = 0,
2814-
order: MemoryOrder | None = "C",
2814+
order: MemoryOrder | None = None,
28152815
attributes: dict[str, JSON] | None = None,
28162816
chunk_key_encoding: ChunkKeyEncodingLike | None = None,
28172817
dimension_names: DimensionNames = None,

test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import zarr
2+
3+
# Write fortran style array
4+
group = zarr.group(store={})
5+
array = group.create_array(
6+
name="example",
7+
shape=(128, 128),
8+
dtype="float32",
9+
order="F",
10+
dimension_names=("row", "col"),
11+
)

tests/test_api.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -339,34 +339,49 @@ def test_open_with_mode_w_minus(tmp_path: pathlib.Path) -> None:
339339
zarr.open(store=tmp_path, mode="w-")
340340

341341

342-
def test_array_order(zarr_format: ZarrFormat) -> None:
343-
arr = zarr.ones(shape=(2, 2), order=None, zarr_format=zarr_format)
344-
expected = zarr.config.get("array.order")
345-
assert arr.order == expected
346-
347-
vals = np.asarray(arr)
348-
if expected == "C":
349-
assert vals.flags.c_contiguous
350-
elif expected == "F":
351-
assert vals.flags.f_contiguous
352-
else:
353-
raise AssertionError
342+
@pytest.mark.parametrize("order", ["C", "F", None])
343+
@pytest.mark.parametrize("config", [{"order": "C"}, {"order": "F"}, {}], ids=["C", "F", "None"])
344+
def test_array_order(
345+
order: MemoryOrder, config: dict[str, MemoryOrder], zarr_format: ZarrFormat
346+
) -> None:
347+
"""
348+
Check that:
349+
- For v2, memory order is taken from the `order` keyword argument.
350+
- For v3, memory order is taken from `config`, and when order is passed a warning is raised
351+
- The numpy array returned has the expected order
352+
- For v2, the order metadata is set correctly
353+
"""
354+
default_order = zarr.config.get("array.order")
355+
if zarr_format == 3:
356+
if order is not None:
357+
ctx = pytest.warns(
358+
RuntimeWarning,
359+
match="The `order` keyword argument has no effect for Zarr format 3 arrays",
360+
)
361+
else:
362+
ctx = contextlib.nullcontext()
363+
expected_order = config.get("order", default_order)
354364

365+
if zarr_format == 2:
366+
ctx = contextlib.nullcontext()
367+
expected_order = order or config.get("order", default_order)
355368

356-
@pytest.mark.parametrize("order", ["C", "F"])
357-
def test_array_order_warns(order: MemoryOrder | None, zarr_format: ZarrFormat) -> None:
358-
with pytest.warns(RuntimeWarning, match="The `order` keyword argument .*"):
359-
arr = zarr.ones(shape=(2, 2), order=order, zarr_format=zarr_format)
360-
assert arr.order == order
369+
with ctx:
370+
arr = zarr.ones(shape=(2, 2), order=order, zarr_format=zarr_format, config=config)
361371

372+
assert arr.order == expected_order
362373
vals = np.asarray(arr)
363-
if order == "C":
374+
if expected_order == "C":
364375
assert vals.flags.c_contiguous
365-
elif order == "F":
376+
elif expected_order == "F":
366377
assert vals.flags.f_contiguous
367378
else:
368379
raise AssertionError
369380

381+
if zarr_format == 2:
382+
assert arr.metadata.zarr_format == 2
383+
assert arr.metadata.order == expected_order
384+
370385

371386
# def test_lazy_loader():
372387
# foo = np.arange(100)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy