ngff_zarr.structural_validation

Structural validation for OME-Zarr v0.4 image/multiscales metadata.

This module implements the OME-Zarr v0.4 specification MUSTs that a JSON Schema cannot express, layered conceptually on top of the schema validation performed by :mod:ngff_zarr.validate (which checks the raw attribute dict). Where schema validation answers “is this shaped like OME-Zarr?”, the rules here answer “does this obey the spec’s structural invariants?” – e.g. axis counts, axis ordering, coordinate-transformation arity, finest-to-coarsest dataset ordering, and OMERO channel color format.

The rules are pure Python (standard library only) and operate on the already parsed :class:~ngff_zarr.v04.zarr_metadata.Metadata dataclass, so they work even when the optional [validate] extra (jsonschema) is not installed.

Each rule is identified by a stable, kebab-case :class:SpecRule value that is part of the observable surface (reused verbatim in logs, tests, and the TypeScript port). Rules fail fast: the orchestrator (:func:validate_structural) raises a :class:ValidationError on the first violation, in canonical spec-MUST order.

Module Contents

Classes

SpecRule

Stable, kebab-case identifiers for the structural spec rules.

ValidationLevel

How much validation :func:validate_structural performs.

ValidateOptions

Options controlling structural validation.

Functions

_transform_len_mismatch

Check one coordinate transform’s vector length against the axis count.

_first_scale_vector

Return the first scale vector in a dataset, or None if absent.

validate_axis_count

Validate that the axis count is within the v0.4-permitted range.

validate_axis_type

Validate axis-type multiplicity.

validate_axis_order

Validate the class ordering of axes.

validate_spatial_axis_order

Validate the count and names of spatial axes.

validate_per_dataset_scale_count

Validate that each dataset defines exactly one scale transform.

validate_scale_length

Validate coordinate-transform vector lengths against the axis count.

validate_transform_order

Validate coordinate-transform ordering within each dataset.

validate_dataset_order

Validate that datasets are ordered finest to coarsest.

validate_omero_color_hex

Validate the color format of each OMERO channel.

_axis_to_validation_dict

Render a parsed :class:Axis back to the dict form RFC 4 consumes.

validate_axis_orientation

Validate RFC 4 anatomical-orientation metadata on the spatial axes.

validate_plate_well_index_consistency

Validate each plate well’s recorded indices against its path.

validate_well_acquisition

Validate that well images reference an acquisition when required.

validate_structural

Run the structural image/multiscales rules in canonical spec order.

validate_plate

Run the structural HCS plate rules in canonical spec order.

validate_well

Run the structural HCS well rules in canonical spec order.

Data

API

class ngff_zarr.structural_validation.SpecRule

Bases: str, enum.Enum

Stable, kebab-case identifiers for the structural spec rules.

Inheriting from :class:str makes the kebab-case identifier the member’s value, so it can be compared and logged directly (SpecRule.AXIS_COUNT == "axis-count") for stable assertions. Members are listed in canonical spec-MUST evaluation order.

Initialization

Initialize self. See help(type(self)) for accurate signature.

AXIS_COUNT

‘axis-count’

AXIS_TYPE

‘axis-type’

AXIS_ORDER

‘axis-order’

SCALE_LENGTH_MISMATCH

‘scale-length-mismatch’

GLOBAL_COORD_TRANSFORM_AFTER_PER_LEVEL

‘global-coord-transform-after-per-level’

DATASET_ORDER_HIGHEST_TO_LOWEST

‘dataset-order-highest-to-lowest’

OMERO_CHANNEL_COLOR_FORMAT

‘omero-channel-color-format’

AXIS_ORIENTATION_CONSISTENT_TYPE

‘axis-orientation-consistent-type’

AXIS_ORIENTATION_COMPLETENESS

‘axis-orientation-completeness’

PLATE_ROW_INDEX_CONSISTENCY

‘plate-row-index-consistency’

WELL_ACQUISITION_MISSING

‘well-acquisition-missing’

class ngff_zarr.structural_validation.ValidationLevel

Bases: str, enum.Enum

How much validation :func:validate_structural performs.

STRICT is the default and runs every structural image/multiscales rule in this module. SCHEMA_ONLY skips them (schema validation is handled separately by :mod:ngff_zarr.validate on the raw attribute dict). Deep, store-reading checks (e.g. confirming each dataset array exists with the expected shape) are intentionally out of scope for both levels.

Initialization

Initialize self. See help(type(self)) for accurate signature.

SCHEMA_ONLY

‘schema_only’

STRICT

‘strict’

class ngff_zarr.structural_validation.ValidateOptions

Options controlling structural validation.

Attributes

level: Validation level to run. Defaults to :attr:ValidationLevel.STRICT. allow_unknown_fields: Whether unknown metadata fields are tolerated. Defaults to True to mirror the parser, which warns on (rather than rejects) unknown fields.

level: ngff_zarr.structural_validation.ValidationLevel

None

allow_unknown_fields: bool

True

exception ngff_zarr.structural_validation.ValidationError(
rule: ngff_zarr.structural_validation.SpecRule,
message: str,
location: str | None = None,
)

Bases: Exception

Raised when a structural spec rule is violated.

Carries the offending :class:SpecRule, a human-readable message, and an optional location identifying the offending metadata node in dotted-segment (JSON-Pointer-style) form, e.g. multiscales[0].datasets[2].coordinateTransformations[0].

Initialization

Initialize self. See help(type(self)) for accurate signature.

__str__() str
ngff_zarr.structural_validation._transform_len_mismatch(
transform: ngff_zarr.v04.zarr_metadata.Transform,
axes_len: int,
) tuple[str, int] | None

Check one coordinate transform’s vector length against the axis count.

Centralizes the length invariant shared by the global and per-dataset coordinate-transformation checks (see

Attr:

SpecRule.SCALE_LENGTH_MISMATCH), so the rule lives in one place.

Returns ("scale", actual_len) or ("translation", actual_len) when the transform’s vector length disagrees with axes_len; otherwise returns None. Transforms that carry no length-bearing vector (e.g. identity) are ignored and return None.

ngff_zarr.structural_validation._first_scale_vector(
dataset: ngff_zarr.v04.zarr_metadata.Dataset,
) list[float] | None

Return the first scale vector in a dataset, or None if absent.

Used by :func:validate_dataset_order to compare adjacent multiscale levels without re-deriving the per-dataset scale lookup.

ngff_zarr.structural_validation._AXIS_TYPE_RANK

None

ngff_zarr.structural_validation._SPATIAL_AXIS_NAMES

(‘z’, ‘y’, ‘x’)

ngff_zarr.structural_validation.validate_axis_count(metadata: ngff_zarr.v04.zarr_metadata.Metadata) None

Validate that the axis count is within the v0.4-permitted range.

OME-Zarr v0.4 requires between 2 and 5 axes, inclusive.

Raises

ValidationError With :attr:SpecRule.AXIS_COUNT when len(metadata.axes) lies outside 2..=5; location multiscales[0].axes.

ngff_zarr.structural_validation.validate_axis_type(metadata: ngff_zarr.v04.zarr_metadata.Metadata) None

Validate axis-type multiplicity.

At most one time axis and at most one channel axis may be present.

Raises

ValidationError With :attr:SpecRule.AXIS_TYPE when more than one time axis or more than one channel axis is present; location multiscales[0].axes.

ngff_zarr.structural_validation.validate_axis_order(metadata: ngff_zarr.v04.zarr_metadata.Metadata) None

Validate the class ordering of axes.

Axes are ranked by type (time < channel < space) and must be listed in non-decreasing rank order: every time axis precedes every channel axis, which precedes every space axis. The first adjacent pair that inverts this ranking is reported.

Raises

ValidationError With :attr:SpecRule.AXIS_ORDER for the first adjacent pair where a lower-ranked axis type follows a higher-ranked one; location multiscales[0].axes[i+1].

ngff_zarr.structural_validation.validate_spatial_axis_order(
metadata: ngff_zarr.v04.zarr_metadata.Metadata,
) None

Validate the count and names of spatial axes.

The space axes, taken in order, must be the matching-length suffix of (z, y, x): one spatial axis must be (x,), two must be (y, x), and three must be (z, y, x). At most three space axes are permitted.

Raises

ValidationError With :attr:SpecRule.AXIS_ORDER when there are more than three space axes, or when their names are not the expected suffix of (z, y, x).

ngff_zarr.structural_validation.validate_per_dataset_scale_count(
metadata: ngff_zarr.v04.zarr_metadata.Metadata,
) None

Validate that each dataset defines exactly one scale transform.

Every dataset’s coordinateTransformations must contain exactly one scale (the per-level voxel size). This shares the per-dataset coordinate-transform-shape rule identifier (:attr:SpecRule.GLOBAL_COORD_TRANSFORM_AFTER_PER_LEVEL); there is no dedicated identifier for the scale count.

Raises

ValidationError With :attr:SpecRule.GLOBAL_COORD_TRANSFORM_AFTER_PER_LEVEL when a dataset has zero or more than one scale transform; location multiscales[0].datasets[i].coordinateTransformations.

ngff_zarr.structural_validation.validate_scale_length(metadata: ngff_zarr.v04.zarr_metadata.Metadata) None

Validate coordinate-transform vector lengths against the axis count.

Every scale and translation vector – in the global coordinateTransformations (if present) and in each dataset – must have exactly one entry per axis. The first mismatch, scanned global-then- per-dataset, is reported. The length invariant itself lives in

Func:

_transform_len_mismatch.

Raises

ValidationError With :attr:SpecRule.SCALE_LENGTH_MISMATCH for the first transform whose vector length disagrees with len(metadata.axes); location identifies the offending transform.

ngff_zarr.structural_validation.validate_transform_order(metadata: ngff_zarr.v04.zarr_metadata.Metadata) None

Validate coordinate-transform ordering within each dataset.

Within a dataset’s coordinateTransformations, a translation (if present) must follow its scale – a scale must never appear after a translation.

Raises

ValidationError With :attr:SpecRule.GLOBAL_COORD_TRANSFORM_AFTER_PER_LEVEL for the first dataset where a scale follows a translation; location identifies the offending scale.

ngff_zarr.structural_validation.validate_dataset_order(metadata: ngff_zarr.v04.zarr_metadata.Metadata) None

Validate that datasets are ordered finest to coarsest.

Multiscale datasets must be listed from highest resolution (finest, i.e. smallest spatial scale) to lowest (coarsest). For each adjacent pair, the later level’s scale must not be smaller than the earlier level’s on any space axis. Pairs whose scale vectors are missing or too short to index a spatial axis are skipped – those shapes are the concern of

Func:

validate_per_dataset_scale_count and :func:validate_scale_length – so this rule never raises IndexError.

Raises

ValidationError With :attr:SpecRule.DATASET_ORDER_HIGHEST_TO_LOWEST for the first adjacent pair whose later (coarser) level has a smaller spatial scale; location multiscales[0].datasets[i+1].

ngff_zarr.structural_validation.validate_omero_color_hex(metadata: ngff_zarr.v04.zarr_metadata.Metadata) None

Validate the color format of each OMERO channel.

When OMERO rendering metadata is present, every channel color must be exactly six hexadecimal digits (RGB). This reuses the existing

Meth:

~ngff_zarr.v04.zarr_metadata.OmeroChannel.validate_color predicate rather than re-implementing the format check.

Raises

ValidationError With :attr:SpecRule.OMERO_CHANNEL_COLOR_FORMAT for the first channel whose color is not six hex digits; location multiscales[0].omero.channels[i].color.

ngff_zarr.structural_validation._axis_to_validation_dict(
axis: ngff_zarr.v04.zarr_metadata.Axis,
) dict[str, Any]

Render a parsed :class:Axis back to the dict form RFC 4 consumes.

Emits only the fields the RFC 4 helpers inspect – name, type, and (for a spatial axis that declares it) orientation as a {"type", "value"} dict. A stored orientation may be a raw dict (the image read path builds axes via Axis(**axis_dict), so no coercion happens) or an :class:~ngff_zarr.rfc4.AnatomicalOrientation dataclass; an enum value is reduced to its string, mirroring the package’s hasattr(..., "value") convention.

ngff_zarr.structural_validation.validate_axis_orientation(
metadata: ngff_zarr.v04.zarr_metadata.Metadata,
) None

Validate RFC 4 anatomical-orientation metadata on the spatial axes.

This rule does not reimplement RFC 4; it wraps the package’s existing logic – :func:~ngff_zarr.rfc4_validation.has_rfc4_orientation_metadata and

Func:

~ngff_zarr.rfc4_validation.validate_rfc4_orientation – and surfaces its failures through the unified :class:ValidationError channel. The parsed

Class:

~ngff_zarr.v04.zarr_metadata.Axis objects are first rendered back to the axis-dict form those helpers expect (see :func:_axis_to_validation_dict).

Orientation is optional in RFC 4, so when no spatial axis carries it the rule is a no-op and the comparatively heavy jsonschema import inside

Func:

validate_rfc4_orientation is never triggered.

Raises

ValidationError With :attr:SpecRule.AXIS_ORIENTATION_CONSISTENT_TYPE when the spatial axes’ orientations do not all share one type, or :attr:SpecRule.AXIS_ORIENTATION_COMPLETENESS when orientation is defined for some but not all spatial axes; location multiscales[0].axes. The original RFC 4 message text is preserved. jsonschema.exceptions.ValidationError Propagated unchanged when an orientation value is outside the RFC 4 vocabulary – a schema-level concern with no dedicated structural rule.

ngff_zarr.structural_validation.validate_plate_well_index_consistency(
plate: ngff_zarr.v04.zarr_metadata.Plate,
) None

Validate each plate well’s recorded indices against its path.

Every :class:~ngff_zarr.v04.zarr_metadata.PlateWell records a path of the form "<row>/<column>" (e.g. "B/03") alongside numeric rowIndex and columnIndex fields. OME-Zarr v0.4 requires these to agree: the row segment must name an entry in plate.rows whose position equals rowIndex, and the column segment must name an entry in plate.columns whose position equals columnIndex. The first offending well, scanned in order, is reported.

Raises

ValidationError With :attr:SpecRule.PLATE_ROW_INDEX_CONSISTENCY for the first well whose path is not "<row>/<column>", whose row/column segment is absent from plate.rows/plate.columns, or whose recorded rowIndex/columnIndex disagrees with that segment’s position; location plate.wells[i].

ngff_zarr.structural_validation.validate_well_acquisition(
plate: ngff_zarr.v04.zarr_metadata.Plate,
well: ngff_zarr.v04.zarr_metadata.Well,
) None

Validate that well images reference an acquisition when required.

When a plate declares more than one acquisition, every image in each of its wells must carry an acquisition reference so the field can be attributed to a specific acquisition. Plates with zero or one acquisition impose no such requirement, so this rule is a no-op for them. The first image missing its reference, scanned in order, is reported.

Raises

ValidationError With :attr:SpecRule.WELL_ACQUISITION_MISSING for the first :class:~ngff_zarr.v04.zarr_metadata.WellImage whose acquisition is None while the plate declares multiple acquisitions; location well.images[i].acquisition.

ngff_zarr.structural_validation.validate_structural(
metadata: ngff_zarr.v04.zarr_metadata.Metadata,
options: ngff_zarr.structural_validation.ValidateOptions | None = None,
) None

Run the structural image/multiscales rules in canonical spec order.

Orchestrates the per-rule validate_* functions in this module. It is fail-fast: the rules run in canonical spec-MUST order and the first

Class:

ValidationError raised propagates to the caller – later rules do not run.

The rules run in this order:

  1. func:

    validate_axis_count

  2. func:

    validate_axis_type

  3. func:

    validate_axis_order

  4. func:

    validate_spatial_axis_order

  5. func:

    validate_per_dataset_scale_count

  6. func:

    validate_scale_length

  7. func:

    validate_transform_order

  8. func:

    validate_dataset_order

  9. func:

    validate_omero_color_hex

  10. func:

    validate_axis_orientation

Parameters

metadata: The parsed OME-Zarr v0.4 multiscales metadata to validate. options: Validation options. Defaults to :class:ValidateOptions, i.e. :attr:ValidationLevel.STRICT. Under :attr:ValidationLevel.SCHEMA_ONLY this function returns immediately without running any structural rule – shape/schema validation is the separate concern of :mod:ngff_zarr.validate, which checks the raw attribute dict.

Raises

ValidationError For the first structural rule violated, carrying the offending :class:SpecRule and location. Never raised under :attr:ValidationLevel.SCHEMA_ONLY, which runs no structural rule.

Notes

This orchestrator covers the image/multiscales rules, including the RFC 4 anatomical-orientation checks (:func:validate_axis_orientation, a no-op when no axis declares orientation). The HCS plate/well structural rules operate on separate metadata objects and are dispatched by the companion

Func:

validate_plate and :func:validate_well entry points.

ngff_zarr.structural_validation.validate_plate(
plate: ngff_zarr.v04.zarr_metadata.Plate,
options: ngff_zarr.structural_validation.ValidateOptions | None = None,
) None

Run the structural HCS plate rules in canonical spec order.

The plate-level counterpart to :func:validate_structural: it orchestrates the structural rules that operate on a parsed

Class:

~ngff_zarr.v04.zarr_metadata.Plate. Like its image/multiscales sibling it is fail-fast – the first :class:ValidationError propagates to the caller and later rules do not run.

The rules run in this order:

  1. func:

    validate_plate_well_index_consistency

Per-well image rules (e.g. acquisition references) are the separate concern of :func:validate_well, which is called where each well’s own metadata is loaded.

Parameters

plate: The parsed OME-Zarr v0.4 plate metadata to validate. options: Validation options. Defaults to :class:ValidateOptions, i.e. :attr:ValidationLevel.STRICT. Under :attr:ValidationLevel.SCHEMA_ONLY this function returns immediately without running any structural rule – shape/schema validation is the separate concern of :mod:ngff_zarr.validate, which checks the raw attribute dict.

Raises

ValidationError For the first structural plate rule violated, carrying the offending :class:SpecRule and location. Never raised under :attr:ValidationLevel.SCHEMA_ONLY, which runs no structural rule.

ngff_zarr.structural_validation.validate_well(
plate: ngff_zarr.v04.zarr_metadata.Plate,
well: ngff_zarr.v04.zarr_metadata.Well,
options: ngff_zarr.structural_validation.ValidateOptions | None = None,
) None

Run the structural HCS well rules in canonical spec order.

Validates a single :class:~ngff_zarr.v04.zarr_metadata.Well in the context of its parent :class:~ngff_zarr.v04.zarr_metadata.Plate – the plate’s acquisition declarations govern whether each well image must reference an acquisition. Fail-fast, like :func:validate_structural and

Func:

validate_plate.

The rules run in this order:

  1. func:

    validate_well_acquisition

Parameters

plate: The parsed plate metadata that owns well; supplies the acquisition context the well rules consult. well: The parsed well metadata to validate. options: Validation options. Defaults to :class:ValidateOptions, i.e. :attr:ValidationLevel.STRICT. Under :attr:ValidationLevel.SCHEMA_ONLY this function returns immediately without running any structural rule – shape/schema validation is the separate concern of :mod:ngff_zarr.validate, which checks the raw attribute dict.

Raises

ValidationError For the first structural well rule violated, carrying the offending :class:SpecRule and location. Never raised under :attr:ValidationLevel.SCHEMA_ONLY, which runs no structural rule.