Latest Results
fix(chunk-grids): judge every chunk specification by the chunk normalizer alone (#4376)
* fix: make the chunk normalizer the one judge of a chunk specification
Chunk specifications were parsed by `normalize_chunks_nd`, but a separate
duck-typed classifier, `_is_rectilinear_chunks`, ran on the raw input first
at three sites to decide whether the spec was rectilinear. Two opinions on
the same input is the shape of the bug in #4374, and they could disagree:
a 0-d numpy array counted as rectilinear because it has `__iter__`.
The classifier is gone. Each site normalizes first and asks the resulting
`ChunkGrid` (`is_regular`); stored rectilinear metadata passed as
`chunks=` counts as rectilinear even when its edges are uniform. The shard
resolver sends regular and rectilinear shard specs through the same
normalizer.
Also fixed on the way:
- The legacy v2 branch of `AsyncArray.create` tested `chunks or
chunk_shape`, so `zarr.create(chunks=np.array([...]), zarr_format=2)`
failed with "truth value of an array is ambiguous". It now uses the
`is not None` form the v3 branch already had.
- 0-d numpy arrays unwrap to their scalar in both normalizers instead of
failing with "len() of unsized object".
- A non-integer scalar spec (`2.0`, `np.float64`) raises the normalizer's
own TypeError instead of "object has no len()".
Assisted-by: ClaudeCode:claude-fable-5-1
* docs: add changelog fragment for #4376
Assisted-by: ClaudeCode:claude-fable-5-1
* refactor(chunk_grids): one integer test for chunk input
`_chunk_int` is the normalizer's single integer rule: anything Python's
integer protocol accepts (int, numpy integer scalars, 0-d integer arrays),
except bool. It replaces the repeated numbers.Integral checks and the two
0-d ndarray unwraps in normalize_chunks_1d / normalize_chunks_nd.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: keep numpy chunk tests that cover real bugs; pin legacy v2 rectilinear rejection
The numpy cases that passed before this PR are dropped; the 0-d array and
float cases move into the normalizer's table and error tests. The legacy
zarr.create Zarr format 2 path gets a truthiness test and its rectilinear
rejection is now asserted with a message match.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: note legacy v2 chunks=0/[] and bool chunk sizes in the #4376 changelog
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(chunk_grids): explicit edge lists stay rectilinear in ChunkGrid.from_sizes
`ChunkGrid.from_sizes` collapsed uniform edge lists to `FixedDimension`
while `normalize_chunks_1d` keeps them as `VaryingDimension`, so
`init_array` needed an `isinstance(chunks, RectilinearChunkGridMetadata)`
guard to keep uniform stored rectilinear grids under the Zarr format 2 and
sharding restrictions. Both now agree that a list declares a rectilinear
dimension, and the guard is gone: the normalized grid is the one judge.
Also: `normalize_chunks_nd` and the shard spec are typed with `ChunksLike`;
a bool chunk size is reported as not a chunk size; an integral float
(`10.0`) is pinned as rejected; `None` reaching the normalizer gets the
generic non-integer `TypeError`.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(chunk_grids): reject every spelling of a boolean chunk size with one error
A bool, np.bool_, or boolean array anywhere in a chunk specification now
raises the same TypeError from the normalizer's one integer test, instead
of three different errors depending on spelling. Document that a
RectilinearChunkGridMetadata of bare integers is read as a regular grid.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(chunk_grids): keep zarr 3.4.0's reading of bool chunk sizes and falsy v2 chunks (patch release)
Nothing in a patch release may reject a chunk specification that zarr 3.4.0
accepted. The normalizer's integer test reads a Python `bool` as the `int` it
is again, so `chunks=(True, 5)` and a `True` edge are a chunk size of 1;
`chunks=True` and `chunks=None` raise 3.4.0's `ValueError` again. Numpy
booleans stay rejected, as they were. The legacy `zarr.create(...,
zarr_format=2)` again reads a falsy `chunks` (0, [], False) as not given and
chunks automatically; a numpy array is always taken as given, so its truth
value is never tested.
The `TypeError` for every boolean spelling returns in the next minor release.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: describe the 4376 changes for a patch release
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(array): read one-element and empty numpy chunk arrays in legacy v2 create as zarr 3.4.0 did
The legacy `zarr.create(..., zarr_format=2)` took every numpy array as a
given chunk specification, so `np.array(0)`, `np.array(False)` and
`np.array([0])` raised instead of auto-chunking as in zarr 3.4.0. A numpy
array with more than one element has no truth value and is always given; a
shorter one is read by `.any()`, its truth value, which is False when empty,
so `np.array([])` auto-chunks like `[]` (as 3.4.0 did with numpy 2.1).
The two legacy v2 tests become one table.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(chunk_grids): let operator.index be the only integer test in _chunk_int
`operator.index` raises `TypeError` for everything the `SupportsIndex`
check rejected, so the check was redundant (identical results over bool,
numpy bools and integers, 0-d and 1-d arrays, float, str, bytes, None,
list and a custom `__index__` class).
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(chunk_grids): read bool chunk sizes as rows of the normalizer table
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(array): name the legacy v2 chunks-given test as _v2_chunks_given
Replace the inline conditional expression in `AsyncArray._create` with a
small named predicate. Results are identical on every probed input.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(array): pin 0-d and all-zero numpy chunks in legacy v2 create
Add `np.array(7)` -> `(7, 7)` to `test_legacy_create_v2_chunks` and an
error test for `np.array([0, 0])`, killing the mutants that drop either
half of `_v2_chunks_given`.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(chunk-grids): accept sequences that only define __getitem__ and __len__
The chunk normalizer checked `isinstance(chunks, Iterable)` before calling
`list(chunks)`. `Iterable` does not recognize a sequence that implements
only `__getitem__` and `__len__`, which `list` accepts and which zarr 3.4.0
took as a chunk specification. The normalizer now calls `list` and turns
its `TypeError` into the chunk specification error.
Assisted-by: ClaudeCode:claude-opus-5-5
* fix(chunk-grids): reject NumPy booleans as chunk sizes on NumPy before 2.3
NumPy before 2.3 takes a NumPy boolean as an index with a
DeprecationWarning, so `operator.index(np.True_)` returned 1 there, and
the normalizer read `chunks=np.True_` as a chunk size of 1 instead of
rejecting it as zarr 3.4.0 did. The min-deps CI job failed on this. The
integer test now rejects a value whose dtype is NumPy bool before calling
`operator.index`.
Assisted-by: ClaudeCode:claude-opus-5-5
* fix(chunk-grids): declare every accepted chunk form in ShardsLike and zarr.create
ShardsLike is now ChunksLike plus the sharding configuration and "auto", so
numpy integers and arrays are declared for shards as they are for chunks.
zarr.create and AsyncArray._create take ChunksLike for chunks and
chunk_shape, matching what the normalizer reads. Tests drop the Any
annotations and the type: ignore that worked around the narrower types.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(array): read only None as an absent chunks argument in legacy zarr.create
The Zarr format 2 path of AsyncArray._create used the truth value of
`chunks` to decide whether it was given, so False, 0, [] and np.array([0])
were silently auto-chunked while Zarr format 3 normalized or rejected them.
Both formats now share the None-only `_raw_chunks` and agree on every input.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(sharding): parse the inner chunk shape with the chunk normalizer as a regular grid
ShardingCodec read its inner chunk_shape with parse_shapelike, which
allows 0, so an inner size of 0 was accepted at construction and failed
later with ZeroDivisionError. parse_regular_chunk_shape shares the
normalizer's integer test (_chunk_int/_chunk_list) and requires every
size to be at least 1; with no axis length to resolve against, -1 and
False are rejected, and an explicit edge list (a rectilinear dimension)
is rejected because the inner grid of a shard is regular.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(metadata): parse the Zarr format 2 chunk shape as a regular chunk shape
ArrayV2Metadata read `chunks` with parse_shapelike, which accepts 0, while
RegularChunkGridMetadata rejects it. It now uses parse_regular_chunk_shape,
as ShardingCodec does, so a chunk size below 1 raises at construction.
Stored documents are unaffected: from_dict repairs a stored 0 before the
constructor sees it. The shared chunk-shape tests cover both sites again.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(api): describe what zarr.create does with chunks=None, False and True
The docstring said True guesses the chunk shape and is the default; None is
the default and True raises. The auto-chunking error now tells
create_array callers to pass "auto" rather than the None they just passed.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* revert: keep the strict chunk-size changes in #4431, out of this patch PR
Reverts 47587bc14, baddb31b2 and ad34ae83d, and the error-message part of
e54740185. Rejecting a chunk size of 0 in ArrayV2Metadata and
ShardingCodec, and no longer reading a falsy `chunks` as absent in the
legacy Zarr format 2 zarr.create, reject inputs zarr 3.4.0 accepted; this
PR targets the 3.4.1 patch release, which must not. #4431 makes the same
changes, more strictly, for the next minor release. The widened chunk and
shard annotations and the zarr.create docstring fix stay.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Latest Branches
0%
d-v-b:perf/rectilinear-rle-runs +23%
bendichter:bytes-partial-decode +14%
d-v-b:refactor/all-equal-cpu-scan © 2026 CodSpeed Technology