From f9b75d833db54554d2815ac14c597b56295e3243 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Wed, 21 Oct 2026 19:23:33 +0200 Subject: [PATCH 1/2] fix(api): drop unreachable scalar branch in save_array `save_array` rejects anything that is not `NDArrayLike` before it reaches `np.isscalar(arr)`. numpy scalars lack `__len__` and `__setitem__`, so they never pass the protocol check, and the `np.array(arr)` fallback was dead. numpy 2.5.4 changed the `np.isscalar` stub from `TypeGuard` to `TypeIs`, which lets mypy intersect the narrowed `NDArrayLike` with the scalar union and report the branch as unreachable. Delete it and pin the error path. Assisted-by: ClaudeCode:claude-fable-5-1 --- src/zarr/api/asynchronous.py | 2 -- tests/test_api.py | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/zarr/api/asynchronous.py b/src/zarr/api/asynchronous.py index 39e7cdd9d4..7b77c9e07f 100644 --- a/src/zarr/api/asynchronous.py +++ b/src/zarr/api/asynchronous.py @@ -533,8 +533,6 @@ async def save_array( mode = kwargs.pop("mode", "a") store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options) - if np.isscalar(arr): - arr = np.array(arr) shape = arr.shape chunks = getattr(arr, "chunks", None) # for array-likes with chunks attribute overwrite = kwargs.pop("overwrite", None) or _infer_overwrite(mode) diff --git a/tests/test_api.py b/tests/test_api.py index f874bd45b7..e0ff7dcf49 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -844,6 +844,12 @@ def test_save_errors() -> None: zarr.save("data/example.zarr", a, mode="w") +def test_save_array_rejects_scalar() -> None: + # numpy scalars lack __len__ and __setitem__, so they fail the NDArrayLike check + with pytest.raises(TypeError, match="arr argument must be numpy or other NDArrayLike array"): + save_array("data/example.zarr", np.float64(1.0)) # type: ignore[arg-type] + + def test_save_storage_options_is_not_an_array(tmp_path: Path) -> None: # `storage_options` is an option of `save`, not one of the arrays to save, so a # single array still lands as an array and not as a group holding `arr_0`. From 56073425a1cc5e440ceaa24dccfd45feef1a1049 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 22 Oct 2026 22:38:12 +0200 Subject: [PATCH 2/2] docs(changes): add changelog fragment for #4554 Assisted-by: ClaudeCode:claude-opus-5-5 --- changes/4554.misc.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/4554.misc.md diff --git a/changes/4554.misc.md b/changes/4554.misc.md new file mode 100644 index 0000000000..a5a961b1c1 --- /dev/null +++ b/changes/4554.misc.md @@ -0,0 +1 @@ +Removed an unreachable scalar-conversion branch from `zarr.save_array`. Scalars are already rejected by the `NDArrayLike` check that precedes it, and mypy began reporting the branch as unreachable once numpy 2.5.4 annotated `numpy.isscalar` with `TypeIs`. A test now pins the `TypeError` that `zarr.save_array` raises for a scalar.