zarr-vlen-ndarray
Typed variable-length ndarray data type (vlen-ndarray) for Zarr v3.
Each array element is a variable-length ndarray of shape (n, *inner_shape):
the leading dimension n varies per element; the trailing inner_shape and
the scalar dtype are fixed in the data type's configuration. One
parameterized type covers, e.g., (n, 2) float32 centroid sets and (n,)
uint64 location lists.
{
"name": "vlen-ndarray",
"configuration": {"dtype": "float32", "inner_shape": [2]}
}
The package provides:
VlenNDArray— a zarr-pythonZDTypefor the data type above;VlenNDArrayCodec— the paired parameter-freearray -> bytescodec, which serializes each element as its raw little-endian C-order bytes and frames chunks exactly like zarr'svlen-bytescodec (numcodecsVLenBytesframing: u32le count, then per element u32le length + payload).
The normative extension text is in SPEC.md and the
registry/ directory (zarr-extensions house style).
Why: byte identity with bytes + vlen-bytes
For any chunk, the encoded bytes are byte-identical to what zarr's
vlen-bytes codec produces for the equivalent raw-bytes payloads (proved in
tests/test_byte_identity.py, including through a zstd chain). A store that
keeps ragged data as the bytes data type with raw-LE-bytes elements — the
zagg-ragged/1 convention —
can be upgraded to the typed form by rewriting zarr.json only; chunk
objects are untouched. Downgrade is equally metadata-only, which is the
escape hatch for readers without this package.
This package is phase 1 of
englacial/zagg#210 and
defines the element type for the zagg-ragged/2 store revision
(englacial/zagg#340).
Install
From PyPI:
pip install zarr-vlen-ndarray
Or from source:
pip install git+https://github.com/espg/zarr-vlen-ndarray
Requires Python >= 3.11, zarr >= 3.1.0 (the ZDType API), numcodecs >= 0.14.
Usage
import numpy as np
import zarr
import zarr_vlen_ndarray # registers the data type and codec
from zarr_vlen_ndarray import VlenNDArray, VlenNDArrayCodec
zdtype = VlenNDArray(dtype="float32", inner_shape=(2,))
arr = zarr.create_array(
store="example.zarr",
shape=(4,),
chunks=(4,),
dtype=zdtype,
serializer=VlenNDArrayCodec(), # required: zarr has no default serializer for extension dtypes
compressors=zarr.codecs.ZstdCodec(level=3),
)
cells = np.empty(4, dtype=object) # object-array staging, as with zarr's vlen types
cells[:] = [
np.random.random((3, 2)).astype("float32"),
np.empty((0, 2), dtype="float32"), # empty cell
np.random.random((7, 2)).astype("float32"),
np.random.random((1, 2)).astype("float32"),
]
arr[:] = cells
arr[:][0] # -> float32 ndarray of shape (3, 2)
Reading requires import zarr_vlen_ndarray first (see the registration
section below); after that, plain zarr.open works.
Semantics worth knowing
- Slice reads return object arrays whose cells are float32/uint64/...
ndarrays. Decoded cells are read-only views over the decoded payload
(zero-copy); call
.copy()to mutate. - Scalar reads (
arr[0]) return the cell wrapped in a 0-d object array — zarr-python does the same for its own vlen types.zarr_vlen_ndarray.unboxrecovers the cell. - Fill values: the default fill is the empty cell
(0, *inner_shape)(metadatafill_value: "", base64 of zero bytes). Cells materialized from the fill areVlenScalarinstances — ndarray subclasses whose==/!=compare the whole cell and return a plain bool (this is what makes zarr's empty-chunk elision work for ndarray cells; in every other respect they behave like regular ndarrays).
Registration and the no-package failure mode
The package declares both zarr entry points:
[project.entry-points."zarr.data_type"]
vlen-ndarray = "zarr_vlen_ndarray:VlenNDArray"
[project.entry-points."zarr.codecs"]
vlen-ndarray = "zarr_vlen_ndarray:VlenNDArrayCodec"
and also registers eagerly on import. Status with current zarr-python
(3.1.0–3.2.1, pinned by tests/test_registration.py): the codec entry
point is discovered lazily by zarr as designed, but zarr collects
zarr.data_type entry points into the data type registry's lazy-load list
without ever flushing it, so data type discovery via entry points is
currently inert upstream. Practical rule: import zarr_vlen_ndarray before
opening a store. When upstream flushes the lazy list, the explicit import
becomes unnecessary automatically.
A vanilla zarr user without this package installed who opens a
vlen-ndarray store gets (verbatim):
ValueError: No Zarr data type found that matches {'name': 'vlen-ndarray', 'configuration': {'dtype': 'float32', 'inner_shape': [2]}}
The fix is pip install zarr-vlen-ndarray + import zarr_vlen_ndarray — or,
without installing anything, the metadata-only downgrade to bytes +
vlen-bytes described above.
Registry status
The vlen-ndarray name is not yet registered with
zarr-developers/zarr-extensions
(no conflicting entry exists as of 2026-08-08, re-checked at upstream
4da7b37). The prepared submission —
registry-formatted spec files plus a PR description — is in
registry/ and
registry_submission_draft.md.
Development
uv sync --extra test
uv run pytest -v
uv run ruff check src tests && uv run ruff format --check src tests
uv run mypy src
CI runs the test matrix on Python 3.11–3.13 against latest zarr, plus a floor
job against zarr==3.1.0.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zarr_vlen_ndarray-0.1.1.tar.gz.
File metadata
- Download URL: zarr_vlen_ndarray-0.1.1.tar.gz
- Upload date:
- Size: 19.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c24b063e787097c64033b59292457db940782bb7742b3075884b7d202f81363
|
|
| MD5 |
9c43a0a8ee8cf354e36d7f4c4daa59cf
|
|
| BLAKE2b-256 |
54ba9f3b3f88f6dc06b7431f4383a366318db6414e8dfdbe337688f72a224e6b
|
Provenance
The following attestation bundles were made for zarr_vlen_ndarray-0.1.1.tar.gz:
Publisher:
publish.yml on espg/zarr-vlen-ndarray
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zarr_vlen_ndarray-0.1.1.tar.gz -
Subject digest:
8c24b063e787097c64033b59292457db940782bb7742b3075884b7d202f81363 - Sigstore transparency entry: 2387710248
- Sigstore integration time:
-
Permalink:
espg/zarr-vlen-ndarray@8310f443d19f17eadedf000ef2bcf3093f7f4e78 -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/espg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8310f443d19f17eadedf000ef2bcf3093f7f4e78 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zarr_vlen_ndarray-0.1.1-py3-none-any.whl.
File metadata
- Download URL: zarr_vlen_ndarray-0.1.1-py3-none-any.whl
- Upload date:
- Size: 13.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17cab21d6e955d8fde398b5d3999dadc1fae2c344d50075b1d95543858a1f236
|
|
| MD5 |
f01de8de399f922b9bf8e46ec1b67900
|
|
| BLAKE2b-256 |
c721b45c751c1b2cf67f4c2d4b6ffcf6889aed4c37098788192f48a55cdc6cb3
|
Provenance
The following attestation bundles were made for zarr_vlen_ndarray-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on espg/zarr-vlen-ndarray
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zarr_vlen_ndarray-0.1.1-py3-none-any.whl -
Subject digest:
17cab21d6e955d8fde398b5d3999dadc1fae2c344d50075b1d95543858a1f236 - Sigstore transparency entry: 2387710262
- Sigstore integration time:
-
Permalink:
espg/zarr-vlen-ndarray@8310f443d19f17eadedf000ef2bcf3093f7f4e78 -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/espg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8310f443d19f17eadedf000ef2bcf3093f7f4e78 -
Trigger Event:
push
-
Statement type: