zarr_metadata.v3.definition
zarr_metadata.v3.definition ¶
Metadata fields read against their definitions: the public door.
Every Zarr v3 extension point -- codecs, data types, chunk grids, chunk key encodings, storage transformers -- is a metadata field: a name, and a configuration whose JSON the extension defines. A definition says what that JSON is and what is allowed in it. It is a value, not a class to subclass:
name, the name the metadata carries;configuration, the TypedDict the configuration's JSON is, which is the one declaration of it: the type checker is compiled from it, and a checked configuration has it as its static type. It reads as the typing spec defines a TypedDict --total,Required,NotRequired,closedandextra_itemsmean what they mean to a type checker, whether or not its module postpones annotations;rules, a function over that TypedDict yielding what the spec disallows -- a bound, members read together -- located in the configuration.
What kind of metadata a definition defines is its type: CodecDefinition
(with the codec's kind, and its size: whether the size of what it gives
out is fixed by the size of what it is handed, "static", or depends on
the values, "dynamic"), DataTypeDefinition, ChunkGridDefinition,
ChunkKeyEncodingDefinition, StorageTransformerDefinition.
Reading JSON. Three steps, each feeding the next, and each usable on its own by a caller that holds nothing but JSON:
check(value, SomeTypedDict), fromzarr_metadata.typed_jsonand here too, type-checks JSON against a TypedDict and needs nothing else: a value of the TypedDict or None, and every problem, each located. The value holds what the TypedDict admits and nothing else. A member typed with a field alias is checked as the JSON a metadata field is.definition.judge(configuration)is the check, each nested field's envelope judged -- a stray member, amust_understandoffalse-- and then the rules, for one configuration:GZIP_CODEC.judge({"level": 12}).-
resolve(field, CodecDefinition, CORE_AND_EXTENSIONS)reads a whole field in a scope: its envelope judged, its name related to a definition, its configuration judged, and each nested field read the same way. It returnsResolved-- the field's JSON, itsresolution, the definition and the checked configuration -- and every problem. A name nothing in scope claims isout_of_scope: left unjudged, which is what keeps the format open. A field is read as one of the five kinds, with or without type arguments;resolve(field, Definition, scope)is aTypeError, since nothing is filed under it.configuration_of(resolved, GZIP_CODEC)is the configuration typed as that definition's TypedDict, when it read it.from zarr_metadata.v3.codec.gzip import GZIP_CODEC from zarr_metadata.v3.definition import ( CORE_AND_EXTENSIONS, CodecDefinition, configuration_of, resolve, )
resolved, problems = resolve({"name": "gzip", "configuration": {"level": 12}}, CodecDefinition, CORE_AND_EXTENSIONS) resolved.resolution # 'invalid' problems[0].loc # ('configuration', 'level')
resolved, problems = resolve({"name": "gzip", "configuration": {"level": 5}}, CodecDefinition, CORE_AND_EXTENSIONS) configuration_of(resolved, GZIP_CODEC) # {'level': 5}, a GzipCodecConfiguration
Problems are values, not exceptions: ValidationProblem(loc, message,
kind), with kind one of invalid_type, invalid_value,
missing_key, unknown_key and invalid_json. A field with an
unknown key is still read -- the key reported, the configuration judged
without it -- so a consumer that tolerates one filters by kind and uses
what was read; the field is valid only when there is no problem at all.
Writing an extension. A TypedDict, a function for its rules, and a
definition; then a scope that holds it. The TypedDict is a
typing_extensions.TypedDict: closed and extra_items are PEP 728's,
which typing.TypedDict does not take on the versions this package
supports. The rules are handed the configuration and the fields it holds
as the scope read them: a field that is read keeps what it read inside it
as Resolved.nested, a Nested mapping by where each sits, so a
struct's rules reach its field types. judge, which reads in no scope,
hands them none.
from collections.abc import Iterator
from typing_extensions import TypedDict
from zarr_metadata.v3.definition import (
CORE_AND_EXTENSIONS,
CodecDefinition,
Nested,
ValidationProblem,
)
class AcmeLz4Configuration(TypedDict, closed=True):
acceleration: int
def acme_lz4_rules(
configuration: AcmeLz4Configuration, nested: Nested
) -> Iterator[ValidationProblem]:
if configuration["acceleration"] < 1:
yield ValidationProblem(("acceleration",), "expected an integer >= 1", "invalid_value")
ACME_LZ4 = CodecDefinition(
name="acme.lz4",
configuration=AcmeLz4Configuration,
kind="bytes_bytes",
size="dynamic",
rules=acme_lz4_rules,
)
SCOPE = CORE_AND_EXTENSIONS.extended_with(ACME_LZ4)
A scope reads whole documents as well as fields:
validate_array_metadata_v3(document, context=SCOPE), from
zarr_metadata.model, reads each extension point of a v3 array document
through the definitions in SCOPE, and so do the model's from_json and
from_key_value: a fill value is judged against the data type it names,
by that data type's definition, the chunk grid against the shape, by
the grid's definition, and the codecs as a pipeline, each by its
definition against the chunk it is handed.
The TypedDict says what a key it does not declare is: with
closed=True, a problem, as above; with extra_items=, a key holding
that type; with closed=False, anything at all. One that says none of
these is open by default, and would take a misspelled key without a
word, so a definition refuses it. Its members are the shapes JSON takes:
int, float, bool, str, None, JSONValue, a Literal,
tuple[T, ...] and tuple[T1, T2], a union, a TypedDict,
Mapping[str, V], a NewType and a type alias.
A member holding another metadata field is annotated with the field alias
of its kind -- a shard's codecs: tuple[CodecField, ...] -- and read in
the scope its field is read in. A member that takes codecs of static size
only is annotated StaticCodecField -- a shard's index_codecs, since a
reader finds the index by a size it knows before reading it -- and a codec
of dynamic size there is a problem at its place, where the field is read
in a scope; a name nothing claims is left unjudged, its size unknown with
the rest of it. ZarrV3MetadataFieldJSON is the same
JSON, but checks as JSON and nothing more, so a definition refuses a
member typed with it. An extension with nothing to configure takes
EmptyConfiguration, and is written as its bare name.
A data type also says what its fill value is: fill_value, the JSON
shape of one as an annotation the checker reads -- Int8FillValue -- and
fill_value_rules, a function yielding what the spec disallows in a fill
value of that shape: an integer out of range, a hex string of another
width. The rules are handed the configuration, the fields it holds as the
scope read them, and the typed fill value, so a struct judges each
field's fill value by that field's own type.
fill_value_problems(data_type, value) judges a fill value against a data
type field the scope read; one nothing in scope claims leaves it unjudged.
A data type that says nothing of its fill value takes any JSON.
A data type says how its values are stored, too: storage, a function
of its configuration and the fields it holds, giving a StorageClass --
in single bytes, in several bytes at a time, or each in as many as it
needs. A struct's is its fields'. storage_of(data_type) asks it of a
data type field the scope read: the bytes codec takes an endian for
numbers of several bytes, and a struct refuses a field whose values vary
in size. A data type that says nothing of it leaves it unknown.
A chunk grid says which arrays it fits: shape_rules, a function
yielding what the spec disallows in a grid of its configuration over an
array of a given shape -- a dimension with no chunk length, chunks that
fall short of one -- located in the configuration. A grid that says
nothing of the shape fits every one. It also says the lengths its chunks
take along each axis of an array it fits, chunk_lengths: a set per
axis, since a rectilinear grid's chunks differ. chunk_grid_lengths(grid,
shape) gives both of a chunk grid field the scope read: an entry for
each dimension of the shape, None where nothing says the lengths.
A codec is judged against what it is handed. The array hands its first
codec a Chunk: the lengths of its grid's chunks along each of the
array's dimensions, and its data type field, with None for what nothing
says. A codec handed an array says what the spec disallows in it handed
a chunk: chunk_rules, located in its configuration -- a transpose
whose order has another number of axes. An array -> array codec says
what it hands the next, whatever its chunk rules found: transition --
transpose permutes the axes. read_pipeline(codecs, chunk) reads codec
fields the scope read as a pipeline: their order -- array -> array
codecs, one array -> bytes codec, bytes -> bytes codecs -- and then each
against the chunk it is handed, giving each codec's Stage with that
chunk. Nothing is guessed: the codec after one the scope did not read,
or after one that says nothing of what it hands on, is handed a chunk
nothing is known of, Chunk(), which is refused nothing; a codec after
that hands on only what it says of its own accord.
Raw bits are the one data type whose name carries its configuration: a
document writes r and the size in bits, and r16 reads as r*, as the
specification's table writes raw bits, with {"bits": 16}. A reader that
reads raw bits its own way defines r*; a data type named r16 is
refused, since that name reads as r*. r* itself is notation, and a
document that writes it names nothing in any scope.
The simplest spelling. canonicalize(field, kind, scope) gives a
field without problems in its simplest equivalent spelling: each nested
field in its own simplest spelling, then the definition's canonical --
blosc drops a typesize that noshuffle ignores, a rectilinear grid
run-length encodes its chunk shapes -- and the envelope in the fewest
words; raw bits write their size back into the name, in decimal, so
r008 is r8. A field with any problem, an unknown key included, has
none: a simpler spelling of it would erase what its author wrote. What
canonical gives is judged again: one that does not hold is a
ValueError, a fault in the definition.
A definition checks itself when it is built, and each of these is a
TypeError saying what is wrong: a configuration that is not a
TypedDict, says nothing of the keys it does not declare, or has a member
no checker reads, named down to the TypedDict that holds it; a name
that is not a string; a member declared as a function -- rules,
canonical, fill_value_rules, storage, shape_rules,
chunk_lengths, chunk_rules, transition -- that is not one; a data type's
fill_value no checker reads; a codec kind that is not one of the
three, or a size that is not "static" or "dynamic"; chunk rules of
a bytes -> bytes codec, which is handed bytes, or a transition of a
codec that hands on bytes; a data type named as raw bits of one size are
written. A scope refuses a definition
of no kind. Nothing happens at class creation.
CORE
module-attribute
¶
Only what the Zarr v3 specification defines.
CORE_AND_EXTENSIONS
module-attribute
¶
What the specification defines, plus what zarr-extensions registers.
ChunkGridField
module-attribute
¶
ChunkGridField = TypeAliasType(
"ChunkGridField", ZarrV3MetadataFieldJSON
)
A configuration member holding a chunk grid.
ChunkKeyEncodingField
module-attribute
¶
ChunkKeyEncodingField = TypeAliasType(
"ChunkKeyEncodingField", ZarrV3MetadataFieldJSON
)
A configuration member holding a chunk key encoding.
CodecField
module-attribute
¶
CodecField = TypeAliasType(
"CodecField", ZarrV3MetadataFieldJSON
)
A configuration member holding a codec: a shard's codecs is tuple[CodecField, ...].
CodecKind
module-attribute
¶
CodecKind = Literal[
"array_array", "array_bytes", "bytes_bytes"
]
What a codec does to what it is handed: the three positions a pipeline orders.
CodecSize
module-attribute
¶
CodecSize = Literal['static', 'dynamic']
Whether the size of what a codec gives out is fixed by the size of what it is handed.
static: it is -- bytes writes each element in its width, crc32c adds
four bytes. dynamic: it depends on the values -- every compressor.
DataTypeField
module-attribute
¶
DataTypeField = TypeAliasType(
"DataTypeField", ZarrV3MetadataFieldJSON
)
A configuration member holding a data type, read in the scope the member's field is read in.
JSONValue
module-attribute
¶
JSONValue = TypeAliasType(
"JSONValue",
int
| float
| bool
| str
| list["JSONValue"]
| tuple["JSONValue", ...]
| Mapping[str, "JSONValue"]
| None,
)
A recursive type alias for JSON-encodable values.
Defined via TypeAliasType (rather than a plain TypeAlias) so the
self-reference is a named recursion point that pydantic can resolve when
building a TypeAdapter; a bare recursive TypeAlias raises
PydanticUserError/RecursionError at validation time.
Lengths
module-attribute
¶
Per axis, every length chunks take along it -- a set, since a rectilinear grid's differ -- or None where unknown.
Loc
module-attribute
¶
Where in a document a value sits: the keys and indices down to it.
Nested
module-attribute
¶
The fields a configuration holds, each as the scope read it, by where it sits in the configuration.
ProblemKind
module-attribute
¶
ProblemKind = Literal[
"missing_key",
"invalid_type",
"invalid_value",
"invalid_json",
"unknown_key",
]
Machine-readable classification of a ValidationProblem.
missing_key: a required key (document key or store key) is absent.invalid_type: a value has the wrong structural type (e.g. a string where a mapping is required, a non-JSON-serializable object).invalid_value: a value has an acceptable type but an invalid content (e.g.zarr_format: 2in a v3 document,order: "Q").invalid_json: bytes that do not decode as JSON.unknown_key: a key an object's type does not declare, where the type says it is closed, as a closed TypedDict does. Whether a Zarr configuration is closed is rarely said (zarr-developers/zarr-specs#270 has been open since 2023), and many readers refuse such a key. It gets a kind of its own so that a caller who tolerates it can tell it from a wrong value, and so that it never masks the other findings about the same object.
Resolution
module-attribute
¶
What a scope made of a field: read by the definition that claims it, or unread, and why.
StaticCodecField
module-attribute
¶
StaticCodecField = TypeAliasType(
"StaticCodecField", ZarrV3MetadataFieldJSON
)
A configuration member holding a codec of static size: a shard's index_codecs is one,
since a reader finds the index by a size it knows before reading it.
StorageClass
module-attribute
¶
StorageClass = Literal[
"single_byte", "multi_byte", "variable_length"
]
How a data type's values are stored: in single bytes, in several bytes at a time, or each in as many as it needs.
A number of several bytes is stored in a byte order, which the bytes
codec's endian says. A value made of single bytes -- a uint8, or a
struct of int8 fields -- has no byte order, and a value whose size
varies takes a codec of its own.
StorageTransformerField
module-attribute
¶
StorageTransformerField = TypeAliasType(
"StorageTransformerField", ZarrV3MetadataFieldJSON
)
A configuration member holding a storage transformer.
Unread
module-attribute
¶
Unread = Literal['out_of_scope', 'invalid']
A field no definition read: nothing in scope claims its name, or it could not be read.
ZarrV3MetadataFieldJSON
module-attribute
¶
ZarrV3MetadataFieldJSON = str | ZarrV3NamedConfigJSON
The JSON shape of any v3 metadata extension-point entry: either a bare
short-hand name string or a {name, configuration, must_understand} envelope.
Used for data_type, chunk_grid, chunk_key_encoding, individual
codec entries, and storage_transformers in v3 array metadata, and for
the inner codecs / index_codecs lists of the sharding_indexed
codec.
Chunk
dataclass
¶
What a codec is handed: chunks of some lengths along each axis, of a data type.
What nothing says is None: the lengths along an axis the grid does not
say, and every part of the chunk handed on by a codec that says nothing
of what it hands on. A data type field the scope did not read is held
as written, and says nothing of the values either. A codec's chunk
rules judge what is known and leave the rest, so a chunk nothing is
known of, Chunk(), is refused nothing.
Source code in src/zarr_metadata/v3/_definition.py
data_type
class-attribute
instance-attribute
¶
data_type: Resolved[DataTypeDefinition[Any]] | None = None
The data type field of the values, as a scope read it; None when no field says what they are.
ChunkGridDefinition
dataclass
¶
Bases: Definition[C]
A chunk grid, and the arrays it fits.
shape_rules is what the spec disallows in a grid of this
configuration over an array of a given shape: a dimension with no
chunk length, chunks that fall short of one. It is handed the
configuration, the fields it holds as the scope read them, and the
shape, and locates its problems in the configuration. A grid that
says nothing of the shape fits every one.
chunk_lengths is what the first codec of the array's pipeline is
handed: the lengths the grid's chunks take along each axis of an
array of a shape it fits -- one for each axis of a regular grid, every
length a rectilinear grid lists. It is asked only of a grid its shape
rules accept. A grid that says nothing of it leaves the lengths along
every axis unknown.
Source code in src/zarr_metadata/v3/_definition.py
chunk_lengths
class-attribute
instance-attribute
¶
The lengths its chunks take along each axis of an array of a shape it fits, None where unknown.
ChunkKeyEncodingDefinition
dataclass
¶
CodecDefinition
dataclass
¶
Bases: Definition[C]
A codec: what it does to what it is handed, and whether the size of what it gives out is static.
A codec handed an array -- array -> array, array -> bytes -- says what
the spec disallows in it handed a Chunk: chunk_rules, handed the
configuration, the fields it holds as the scope read them, and the
chunk, and locating its problems in the configuration -- a bytes
codec without endian, handed a multi-byte data type. An array ->
array codec also says what it hands on: transition, the chunk the
next codec is handed, given the one it is handed -- transpose
permutes the axes, cast_value changes the data type. The two are the
spec's pair: a codec computes what it gives from the shape and data
type it is handed, and "If the decoded_representation_type is not
supported, this algorithm must fail with an error"
(https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/core/index.rst#L987-L994).
The transition is asked of every chunk the codec is handed, whatever
its chunk rules found, so it gives only what holds either way: a
transpose whose order has another number of axes hands on lengths
nothing is known of. A codec that says nothing of what it hands on
hands the next a chunk nothing is known of.
Source code in src/zarr_metadata/v3/_definition.py
Context
dataclass
¶
The definitions in scope while metadata is read.
A value with no reading of its own: resolve reads a field in it,
and claimant is the one question it answers, which definition a
name belongs to. Built from definitions with Context.of, extended
with more by extended_with.
Source code in src/zarr_metadata/v3/_registry.py
claimant ¶
The definition of kind in scope that reads name, a name a document writes; None if none does.
The one filed under the name spelled reads it as: itself, but
for raw bits, r16 read by the definition of r*.
Source code in src/zarr_metadata/v3/_registry.py
definitions ¶
definitions() -> tuple[Definition[Any], ...]
extended_with ¶
extended_with(*definitions: Definition[Any]) -> Context
This scope, plus definitions of your own.
A name already filed under the same kind is taken over by what is
passed here, which is how a reader substitutes its own reading of a
codec the package already defines -- or of raw bits, by defining
r*.
Source code in src/zarr_metadata/v3/_registry.py
of
classmethod
¶
of(*definitions: Definition[Any]) -> Context
A scope of exactly these definitions; a later one takes a name over from an earlier.
TypeError for a definition of no kind, which no position in a
document could hold.
Source code in src/zarr_metadata/v3/_registry.py
DataTypeDefinition
dataclass
¶
Bases: Definition[C]
A data type, and the fill value an array of it takes.
fill_value is the JSON shape of a fill value -- Int8FillValue, an
annotation the checker reads as it reads a configuration's members --
and fill_value_rules is what the spec disallows in a fill value of
that shape: an integer out of range, a hex string of another width.
The rules are handed the configuration, the fields it holds as the
scope read them (a struct's field types), and the typed fill value. A
data type that says nothing of its fill value takes any JSON.
storage says how its values are stored -- in single bytes, in
several bytes at a time, or each in as many as it needs -- which is
what the bytes codec asks of the data type it is handed: an
endian, for numbers of several bytes. A struct's is its fields', so
it is handed the fields the configuration holds as the scope read
them. A data type that says nothing of it leaves it unknown.
One named as a document writes raw bits of one size -- r16 -- is
refused: that name reads as r*, so nothing would ever read it with
this definition.
Source code in src/zarr_metadata/v3/_definition.py
fill_value
class-attribute
instance-attribute
¶
The JSON shape of a fill value, as an annotation: Int8FillValue.
fill_value_rules
class-attribute
instance-attribute
¶
fill_value_rules: Callable[
[C, Nested, Any], Iterable[ValidationProblem]
] = no_rules
What the spec disallows in a fill value of that shape, located in it.
storage
class-attribute
instance-attribute
¶
storage: Callable[[C, Nested], StorageClass | None] = (
unknown_storage
)
How its values are stored, given the configuration and the fields it holds; None when unknown.
Definition
dataclass
¶
Bases: Generic[C]
One extension's metadata, as JSON: its name, the TypedDict its configuration is, its rules.
configuration is the TypedDict, and so the one declaration of the
JSON: the checker is compiled from it, the static type of a checked
configuration is it, and a document's author writes to it. It reads
as the typing spec defines it -- total, Required, NotRequired,
closed and extra_items mean what they mean to a type checker --
and it says what a key it does not declare is: with closed=True, a
problem, reported and left out; with extra_items=, a key of that
type; with closed=False, anything. rules yields what the spec
disallows in a configuration of that type, as it finds each; it is
handed only a configuration that has passed the check, holding what
the TypedDict admits and nothing else, and the fields it holds as the
scope read them -- a struct's field types -- which is nothing when no
scope read it. judge is the two, for a caller holding JSON.
canonical is where two spellings of the configuration that mean the
same thing are made one.
Built by hand, a definition refuses what it could not read with: a
configuration that is not a TypedDict, says nothing of the keys it
does not declare, or has a member no checker reads, which is named;
and a name or rules that are not what they say.
Source code in src/zarr_metadata/v3/_definition.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |
canonical
class-attribute
instance-attribute
¶
canonical: Callable[[C], C] = unchanged
A well-typed, allowed configuration in its simplest equivalent spelling.
Only the definition's own members: a nested field is put in its own
canonical form by canonicalize, which knows where each one sits.
name
instance-attribute
¶
name: str
The name the metadata carries, which a scope files the definition under.
requires_configuration
property
¶
requires_configuration: bool
Whether a document must write a configuration: whether the TypedDict has a required key.
rules
class-attribute
instance-attribute
¶
rules: Callable[
[C, Nested], Iterable[ValidationProblem]
] = no_rules
What the spec disallows in a well-typed configuration and the fields it holds, located in it.
check ¶
value type-checked as this definition's configuration, each nested field's envelope judged.
zarr_metadata.typed_json.check is the type check alone; this also
judges the envelope of each metadata field a member holds.
Source code in src/zarr_metadata/v3/_definition.py
judge ¶
value type-checked, then judged by the rules: the configuration if it holds, and every problem.
The rules are asked only of a configuration that type-checked and
whose nested fields are well formed, holding what its TypedDict
admits and nothing else, so a caller holding JSON never reaches a
rule with a member of the wrong type, or one the type says cannot
be there. No scope reads the fields it holds, so the rules see
none of them read, and a rule about one -- a struct's field of a
type whose values vary in size -- finds nothing to judge: resolve
reads the field in a scope, and asks every rule.
Source code in src/zarr_metadata/v3/_definition.py
EmptyConfiguration ¶
Bases: TypedDict
The configuration of a definition with nothing to configure, written as its bare name.
MetadataValidationError ¶
Bases: ValueError
Raised when a value fails validation, by the entry points that raise rather than report.
Carries every problem found (not just the first) in .problems, as an
immutable tuple: a raised error is a finished report, and a caller
inspecting it must not be able to edit the record.
Source code in src/zarr_metadata/_json.py
Resolved
dataclass
¶
Bases: Generic[D]
One metadata field, as read in a scope: its JSON, and what the scope made of it.
resolution is what became of the configuration: read by the
definition that claims the name, claimed by nothing, or not readable.
A problem with the envelope around it -- a stray member, a
must_understand of false -- is reported with the field, and leaves
the resolution as it is.
Source code in src/zarr_metadata/v3/_definition.py
configuration
instance-attribute
¶
The configuration, type-checked and allowed by the rules, when the field was read; None otherwise.
definition
instance-attribute
¶
The definition that claims the field's name; None when nothing in scope does, or it names none.
json
instance-attribute
¶
json: JSONValue
The field as written, refined: arrays as tuples. None for a value that was not JSON, as for null.
nested
class-attribute
instance-attribute
¶
nested: Nested = dataclasses.field(
default_factory=_nothing_nested
)
The fields the configuration holds, each as the scope read it, by where it sits in the configuration.
A struct's field types at ("fields", 0, "data_type"), a shard's
codecs at ("codecs", 0): what a definition's functions consult about
the fields inside its own. Empty unless the field was read.
Stage
dataclass
¶
One codec of a pipeline, and the chunk it is handed.
Source code in src/zarr_metadata/v3/_pipeline.py
StorageTransformerDefinition
dataclass
¶
ValidationProblem
dataclass
¶
A single problem found in a value: where it is, what is wrong, and what kind of wrong.
loc is the path from the root of what was judged to the offending
value, e.g. ("codecs", 0, "name") in a document, and an empty loc
refers to that root.
kind classifies the failure mode for programmatic dispatch; message
is the human-readable description.
Source code in src/zarr_metadata/_json.py
canonicalize ¶
canonicalize(
data: object,
kind: type[D],
context: Context,
loc: Loc = (),
) -> tuple[JSONValue | None, Problems]
data, one metadata field, in its simplest equivalent spelling, and every problem.
Only a field without problems has one. A simplest spelling says what
the author wrote in fewer words, and a key the TypedDict does not
declare, a stray envelope member or a must_understand of false is
something the author wrote that it would erase, so a field with any
problem comes back None, with its problems. Otherwise each nested
field goes in its own simplest spelling, and then the definition's
canonical has the rest -- judged again, so a canonical that gives
a configuration that does not hold is a ValueError, a fault in the
definition rather than the field. The envelope takes the fewest
words: the bare name when nothing is configured, and no
must_understand, since true is what absence means. A name nothing
in scope claims comes back as written, since what it simplifies to is
its own definition's call.
Source code in src/zarr_metadata/v3/_definition.py
check ¶
check(
value: object, shape: type[T], loc: Loc = ()
) -> tuple[T | None, tuple[ValidationProblem, ...]]
value type-checked as shape, a TypedDict: a value of it or None, and every problem.
value is refined to JSON first -- arrays as tuples, string keys,
finite floats -- and then checked member by member, each problem
located under loc. What comes back holds what shape admits and
nothing else: a key a closed TypedDict does not declare is reported,
as unknown_key, and left out, and the value still comes back.
Anything else wrong and it does not. TypeError for a shape that is
not a TypedDict, or holds something no parser reads.
Source code in src/zarr_metadata/_typed_json.py
chunk_grid_lengths ¶
chunk_grid_lengths(
chunk_grid: Resolved[ChunkGridDefinition[Any]],
shape: tuple[int, ...],
loc: Loc = (),
) -> tuple[Lengths, Problems]
The lengths the chunks of chunk_grid, a chunk grid field a scope read, take along each axis of an array of shape, and what is wrong with the grid over it.
A chunk has an extent "for each dimension of the array"
(https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/core/index.rst#L277-L281),
so the lengths have an entry for each dimension of shape, None where
nothing says them. The grid's shape rules judge it first, and locate
their problems in the configuration, under loc, where the field
sits: a regular grid with a chunk length for each of two dimensions,
over an array of three. Only a grid that fits the shape says its
lengths; a grid the scope did not read, out of scope or invalid, is
left unjudged and says none. What a grid's chunk_lengths gives is
checked: lengths of another type are a TypeError, and of another
number of axes than shape has a ValueError, each a fault in the
definition, not the field.
Source code in src/zarr_metadata/v3/_definition.py
configuration_of ¶
configuration_of(
resolved: Resolved[Any], definition: Definition[C]
) -> C | None
The configuration resolved holds, typed as definition declares it, if definition read it.
Resolved holds a configuration as the mapping every one is; asked
with the definition that read the field, this is the same mapping, as
its TypedDict. None when another definition read it, or none did.
Source code in src/zarr_metadata/v3/_definition.py
fill_value_problems ¶
fill_value_problems(
data_type: Resolved[DataTypeDefinition[Any]],
value: object,
loc: Loc = (),
) -> Problems
What is wrong with value as a fill value of data_type, a data type field a scope read.
value is refined to JSON first: not JSON is the first verdict,
whatever the data type. It is then checked against the JSON shape the
data type's definition declares, and judged by its fill value rules, as
judge judges a configuration: a key the shape does not declare is
reported and left out, and the rules still judge the rest. The rules
see the fields the configuration holds as the scope read them: a
struct judges each field's fill value by that field's own type. A data
type the scope did not read, out of scope or invalid, leaves a JSON fill
value unjudged. loc prefixes every problem.
Source code in src/zarr_metadata/v3/_definition.py
read_pipeline ¶
read_pipeline(
codecs: Sequence[Resolved[CodecDefinition[Any]]],
chunk: Chunk,
loc: Loc = (),
) -> tuple[tuple[Stage, ...], Problems]
Each of codecs, codec fields a scope read, as a pipeline handed chunk, with the chunk it is handed, and what is wrong with them.
The order first: array -> array codecs, then one array -> bytes codec,
then bytes -> bytes codecs. Then each codec in turn, handed the chunk
the one before it handed on: judged by its chunk rules, which locate
their problems in its configuration, and, an array -> array codec,
asked what it hands on. Problems are located under loc, where the
codecs sit, each codec at its index.
Source code in src/zarr_metadata/v3/_pipeline.py
resolve ¶
resolve(
data: object,
kind: type[D],
context: Context,
loc: Loc = (),
) -> tuple[Resolved[D], Problems]
data, one metadata field, read as a kind in context: what the scope made of it, and every problem.
All three steps for one field. data is refined to JSON and its
envelope judged -- an extra member, a configuration that is not an
object, a must_understand that is not a boolean or is false, each
a problem. The name is related to a definition in context; the
configuration is checked against its TypedDict and judged by its
rules; each nested field the check met is read the same way, in the
same scope. A name nothing claims is out_of_scope: an unmodelled
extension, left unjudged, which is what keeps the format open. loc
prefixes every problem. kind is one of KINDS, with or without
type arguments; anything else is a TypeError.
Source code in src/zarr_metadata/v3/_definition.py
storage_of ¶
storage_of(
data_type: Resolved[DataTypeDefinition[Any]],
) -> StorageClass | None
How the values of data_type, a data type field a scope read, are stored; None when unknown.
Unknown when the scope did not read it, or its definition does not
say. Its storage is the extension author's code: what it gives is
checked to be a storage class, and an error it raises says which data
type's storage raised it.