diff --git a/docs/index.md b/docs/index.md index 1cb7671..d3b6e72 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,24 +48,24 @@ group_model = Group.from_zarr(zgroup) multi_meta = group_model.attributes.multiscales print(multi_meta) """ -[ +( MultiscaleMetadata( version='0.4', name=None, type=None, metadata=None, - datasets=[ + datasets=( Dataset( path='0', coordinateTransformations=( VectorScale( type='scale', - scale=[ + scale=( 1.0, 0.5002025531914894, 0.3603981534640209, 0.3603981534640209, - ], + ), ), ), ), @@ -74,12 +74,12 @@ print(multi_meta) coordinateTransformations=( VectorScale( type='scale', - scale=[ + scale=( 1.0, 0.5002025531914894, 0.7207963069280418, 0.7207963069280418, - ], + ), ), ), ), @@ -88,25 +88,25 @@ print(multi_meta) coordinateTransformations=( VectorScale( type='scale', - scale=[ + scale=( 1.0, 0.5002025531914894, 1.4415926138560835, 1.4415926138560835, - ], + ), ), ), ), - ], - axes=[ + ), + axes=( Axis(name='c', type='channel', unit=None), Axis(name='z', type='space', unit='micrometer'), Axis(name='y', type='space', unit='micrometer'), Axis(name='x', type='space', unit='micrometer'), - ], + ), coordinateTransformations=None, - ) -] + ), +) """ # to get the Zarr arrays referenced by the multiscale metadata, we access them by name from the Zarr group. @@ -180,43 +180,43 @@ print(group_model.model_dump()) { 'zarr_version': 2, 'attributes': { - 'multiscales': [ + 'multiscales': ( { 'version': '0.4', 'name': None, 'type': None, 'metadata': None, - 'datasets': [ + 'datasets': ( { 'path': 's0', 'coordinateTransformations': ( - {'type': 'scale', 'scale': [1.0, 2.0, 2.0, 2.0]}, + {'type': 'scale', 'scale': (1.0, 2.0, 2.0, 2.0)}, { 'type': 'translation', - 'translation': [0.0, 0.0, 0.0, 0.0], + 'translation': (0.0, 0.0, 0.0, 0.0), }, ), }, { 'path': 's1', 'coordinateTransformations': ( - {'type': 'scale', 'scale': [2.0, 4.0, 4.0, 4.0]}, + {'type': 'scale', 'scale': (2.0, 4.0, 4.0, 4.0)}, { 'type': 'translation', - 'translation': [0.5, 1.0, 1.0, 1.0], + 'translation': (0.5, 1.0, 1.0, 1.0), }, ), }, - ], - 'axes': [ + ), + 'axes': ( {'name': 't', 'type': 'time', 'unit': 'second'}, {'name': 'z', 'type': 'space', 'unit': 'nanometer'}, {'name': 'y', 'type': 'space', 'unit': 'nanometer'}, {'name': 'x', 'type': 'space', 'unit': 'nanometer'}, - ], + ), 'coordinateTransformations': None, - } - ] + }, + ) }, 'members': { 's0': { @@ -269,37 +269,93 @@ foo print(stored_group.attrs.asdict()) """ { - 'multiscales': [ + 'multiscales': ( { 'version': '0.4', 'name': None, 'type': None, 'metadata': None, - 'datasets': [ + 'datasets': ( { 'path': 's0', 'coordinateTransformations': ( - {'type': 'scale', 'scale': [1.0, 2.0, 2.0, 2.0]}, - {'type': 'translation', 'translation': [0.0, 0.0, 0.0, 0.0]}, + {'type': 'scale', 'scale': (1.0, 2.0, 2.0, 2.0)}, + {'type': 'translation', 'translation': (0.0, 0.0, 0.0, 0.0)}, ), }, { 'path': 's1', 'coordinateTransformations': ( - {'type': 'scale', 'scale': [2.0, 4.0, 4.0, 4.0]}, - {'type': 'translation', 'translation': [0.5, 1.0, 1.0, 1.0]}, + {'type': 'scale', 'scale': (2.0, 4.0, 4.0, 4.0)}, + {'type': 'translation', 'translation': (0.5, 1.0, 1.0, 1.0)}, ), }, - ], - 'axes': [ + ), + 'axes': ( {'name': 't', 'type': 'time', 'unit': 'second'}, {'name': 'z', 'type': 'space', 'unit': 'nanometer'}, {'name': 'y', 'type': 'space', 'unit': 'nanometer'}, {'name': 'x', 'type': 'space', 'unit': 'nanometer'}, - ], + ), 'coordinateTransformations': None, - } - ] + }, + ) } """ +``` + + +## Data validation + +This library attempts to detect invalid OME-NGFF containers and provide useful error messages when something is broken. The following examples illustrate a few ways in which OME-NGFF metadata can be broken, and what the error messages look like. + +```python +from pydantic import ValidationError +from pydantic_zarr.v2 import ArraySpec +from pydantic_ome_ngff.v04.multiscale import Group +from pydantic_ome_ngff.v04.axis import Axis +import numpy as np + +arrays = np.zeros((10,10)), np.zeros((5,5)) +scales = ((1,1), (2,2)) +translations = ((0,0), (0.5, 0.5)) +paths = ('s0','s1') +axes = ( + Axis(name='y', unit='nanometer', type='space'), + Axis(name='x', unit='nanometer', type='space') +) + +# create a valid multiscale group +group_model = Group.from_arrays(arrays, paths=paths, axes=axes, scales=scales, translations=translations) + +# convert that group to a dictionary, so we can break it +group_model_missing_array = group_model.model_dump() + +# remove one of the arrays. this invalidates the multiscale metadata +group_model_missing_array['members'].pop('s0') + +try: + Group(**group_model_missing_array) +except ValidationError as e: + print(e) + """ + 1 validation error for Group + Value error, Dataset s0 was specified in multiscale metadata, but no array with that name was found in the hierarchy. All arrays referenced in multiscale metadata must be contained in the group. [type=value_error, input_value={'zarr_version': 2, 'attr...: 'zstd', 'level': 3}}}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.6/v/value_error + """ + +group_model_wrong_array = group_model.model_dump() + +# insert an array with incorrect dimensionality +group_model_wrong_array['members']['s0'] = ArraySpec.from_array(np.arange(10)).model_dump() + +try: + Group(**group_model_wrong_array) +except ValidationError as e: + print(e) + """ + 1 validation error for Group + Value error, Transform type='scale' scale=(1, 1) has dimensionality 2, which does not match the dimensionality of the array found in this group at s0 (1). Transform dimensionality must match array dimensionality. [type=value_error, input_value={'zarr_version': 2, 'attr...: 'zstd', 'level': 3}}}}, input_type=dict] + For further information visit https://errors.pydantic.dev/2.6/v/value_error + """ ``` \ No newline at end of file