Skip to content

zarr_indexing.messages

zarr_indexing.messages

The ndsel message layer — pure JSON in, canonical JSON out.

This module implements the ndsel draft wire format: a JSON-serializable representation of NumPy-style n-dimensional selections that adapts TensorStore's IndexTransform model. It is a pure JSON→JSON layer depending only on the standard library. It validates and desugars messages, removing redundant fields on constant maps. It limits input rank to 32 and checks affine references against that rank. Finite bounds and in-memory array construction are enforced by the engine lowering layer.

Two entry points:

  • parse_ndsel(obj) — structurally validate an ndsel message of any of the five kinds (point/box/slice/points/transform), returning it unchanged. Raises NdselError (carrying a spec reason code) on any defect.
  • normalize_ndsel(obj) — desugar and canonicalize a message to the single deterministic canonical transform body of the spec (section 4.3): a bare IndexTransform JSON body, without the kind discriminator. normalize is idempotent when its output is re-tagged with kind: "transform".

The canonical body uses TensorStore's IndexTransform field vocabulary. Normalization alone does not guarantee TensorStore acceptance: index-array content is deferred, and TensorStore has additional coordinate and label limits.

Value rules for validated fields (excluding the verbatim index_array payload and discarded constant-map fields): integers are 64-bit signed values; JSON booleans are not integers (Python's isinstance(True, int) is guarded against explicitly); the "-inf"/"+inf" sentinels are legal only in bound positions; an implicit bound is the one-element [n]-bracket form, and its implicit/explicit flag is preserved through normalization.

REASON_CODES module-attribute

REASON_CODES = frozenset(
    {
        "invalid_json",
        "unknown_kind",
        "unknown_field",
        "multiple_upper_bounds",
        "bounds_out_of_order",
        "output_map_conflict",
        "rank_mismatch",
        "step_zero",
        "negative_step_unsupported",
    }
)

__all__ module-attribute

__all__ = ['NdselError', 'normalize_ndsel', 'parse_ndsel']

NdselError

Bases: ValueError

An ndsel message failed validation.

Carries the spec reason code (one of REASON_CODES) so callers and the conformance harness can assert on it directly, plus a human-readable detail.

Examples:

>>> try:
...     normalize_ndsel({"kind": "bogus"})
... except NdselError as error:
...     (error.reason, str(error))
('unknown_kind', "unknown_kind: unknown kind 'bogus'")
Source code in src/zarr_indexing/messages.py
class NdselError(ValueError):
    """An ndsel message failed validation.

    Carries the spec `reason` code (one of `REASON_CODES`) so callers and the
    conformance harness can assert on it directly, plus a human-readable
    `detail`.

    Examples
    --------
    >>> try:
    ...     normalize_ndsel({"kind": "bogus"})
    ... except NdselError as error:
    ...     (error.reason, str(error))
    ('unknown_kind', "unknown_kind: unknown kind 'bogus'")
    """

    def __init__(self, reason: str, detail: str = "") -> None:
        """Store `reason` and `detail` and compose the message as `"reason: detail"`.

        `reason` is a spec reason code (one of `REASON_CODES`); `detail` is
        optional human-readable context, and when empty the message is the
        bare `reason`.
        """
        self.reason = reason
        self.detail = detail
        super().__init__(f"{reason}: {detail}" if detail else reason)

detail instance-attribute

detail = detail

reason instance-attribute

reason = reason

__init__

__init__(reason: str, detail: str = '') -> None

Store reason and detail and compose the message as "reason: detail".

reason is a spec reason code (one of REASON_CODES); detail is optional human-readable context, and when empty the message is the bare reason.

Source code in src/zarr_indexing/messages.py
def __init__(self, reason: str, detail: str = "") -> None:
    """Store `reason` and `detail` and compose the message as `"reason: detail"`.

    `reason` is a spec reason code (one of `REASON_CODES`); `detail` is
    optional human-readable context, and when empty the message is the
    bare `reason`.
    """
    self.reason = reason
    self.detail = detail
    super().__init__(f"{reason}: {detail}" if detail else reason)

normalize_ndsel

normalize_ndsel(obj: Any) -> dict[str, Any]

Desugar and canonicalize an ndsel message to its canonical transform body.

Accepts any of the five message kinds and returns the bare canonical IndexTransform body of spec section 4.3 — no kind field. Raises NdselError (carrying a reason code) for any invalid input.

Examples:

>>> body = normalize_ndsel({"kind": "box", "shape": [2, 3]})
>>> (body["input_rank"], body["input_inclusive_min"], body["input_exclusive_max"])
(2, [0, 0], [2, 3])
>>> body["output"][0]
{'offset': 0, 'stride': 1, 'input_dimension': 0}
Source code in src/zarr_indexing/messages.py
def normalize_ndsel(obj: Any) -> dict[str, Any]:
    """Desugar and canonicalize an ndsel message to its canonical transform body.

    Accepts any of the five message kinds and returns the bare canonical
    `IndexTransform` body of spec section 4.3 — no `kind` field. Raises
    `NdselError` (carrying a reason code) for any invalid input.

    Examples
    --------
    >>> body = normalize_ndsel({"kind": "box", "shape": [2, 3]})
    >>> (body["input_rank"], body["input_inclusive_min"], body["input_exclusive_max"])
    (2, [0, 0], [2, 3])
    >>> body["output"][0]
    {'offset': 0, 'stride': 1, 'input_dimension': 0}
    """
    message = _require_object(obj)
    kind = _message_kind(message)
    canonical = _NORMALIZERS[kind](message)
    # Apply the same limit to inferred and shorthand ranks as to explicit
    # transform ranks, so every result can be normalized again.
    if canonical["input_rank"] > _MAX_RANK:
        raise NdselError(
            "invalid_json", f"input_rank must be <= {_MAX_RANK}, got {canonical['input_rank']}"
        )
    return canonical

parse_ndsel

parse_ndsel(obj: Any) -> dict[str, Any]

Structurally validate an ndsel message, returning it unchanged.

Runs the same validation and desugaring as normalize_ndsel, discards the canonical body, and returns the original object. Useful for validating a message you intend to keep in its compact shorthand form. Index-array payload validation remains the engine's responsibility.

Examples:

>>> message = {"kind": "point", "coords": [3, 4]}
>>> parse_ndsel(message) is message
True
>>> normalize_ndsel(message)["output"]
[{'offset': 3}, {'offset': 4}]
Source code in src/zarr_indexing/messages.py
def parse_ndsel(obj: Any) -> dict[str, Any]:
    """Structurally validate an ndsel message, returning it unchanged.

    Runs the same validation and desugaring as `normalize_ndsel`, discards
    the canonical body, and returns the original object. Useful for validating
    a message you intend to keep in its compact shorthand form. Index-array
    payload validation remains the engine's responsibility.

    Examples
    --------
    >>> message = {"kind": "point", "coords": [3, 4]}
    >>> parse_ndsel(message) is message
    True
    >>> normalize_ndsel(message)["output"]
    [{'offset': 3}, {'offset': 4}]
    """
    message = _require_object(obj)
    _message_kind(message)
    # Validation and desugaring share one pass; run it and discard the body.
    normalize_ndsel(message)
    return message