zarr_metadata.v3.entity
zarr_metadata.v3.entity ¶
The extension layer: what an entity is, and what is in scope.
Every Zarr v3 extension point -- codecs, data types, chunk grids, chunk key encodings, storage transformers -- is modelled as a class that answers for itself. This module is the public door to that layer, for two kinds of caller.
Reading metadata. ArrayDocumentV3.from_json is the fail-fast front
door: one call, and either every extension point is read or a single
MetadataValidationError carries every reason it is not. The entities it
yields know things the document does not spell out -- what a data type's
scalars are, which position a codec occupies, what a grid divides an
array into. A name the scope does not model is not a failure: it arrives
as an Opaque marked out_of_scope, for the reader to resolve
elsewhere.
from zarr_metadata.v3.entity import ArrayDocumentV3, CodecEntity
array = ArrayDocumentV3.from_json(json.loads(raw)) # or raises
array.parts.grid.rank
for codec in array.codecs:
if isinstance(codec, CodecEntity):
codec.kind # 'array_bytes'
else:
codec.json, codec.reason # 'out_of_scope': resolve it yourself
Writing an extension. Subclass CodecEntity, DataTypeEntity,
ChunkGridEntity or MetadataEntity, declare identifier and
member_types, and put it in a Context:
@dataclass(frozen=True)
class AcmeLz4Codec(CodecEntity):
acceleration: int = 1
identifier: ClassVar[str] = "acme.lz4"
kind: ClassVar[CodecKind] = "bytes_bytes"
member_types: ClassVar[MemberTypes] = {"acceleration": (False, is_int)}
SCOPE = Context({**CORE_AND_EXTENSIONS.entities,
"codecs": {**CORE_AND_EXTENSIONS.entities["codecs"],
AcmeLz4Codec.identifier: AcmeLz4Codec}})
validate_array_metadata_v3(document, context=SCOPE)
A name in no scope is not rejected -- that is what extension openness means -- so registering yours is how you get it judged rather than waved through.
One known friction, under mypy only. An entity's to_json returns its own
object TypedDict, and mypy does not accept that where a
ZarrV3MetadataFieldJSON is wanted: it reads every TypedDict as
Mapping[str, object], never as the Mapping[str, JSONValue] the
envelope declares. So putting to_json() output straight into a codecs
list needs a cast under mypy. Pyright accepts it.
That conversion is sound here, which is why pyright is the one that is
right. The rule mypy is applying exists because an ordinary TypedDict may
carry extra items of types it never declared, so the union of the
declared value types does not bound what is in the mapping. Every
TypedDict in this package is closed (PEP 728), which forbids exactly
that, and pyright implements PEP 728. Mypy does not yet -- see
python/mypy#8994 and python/mypy#18439.
The type therefore stays as it is. Widening configuration to
Mapping[str, object] or Mapping[str, Any] would satisfy mypy by
making the annotation say something false: a configuration's values are
JSON, and that is worth more than one checker's cast.
CORE
module-attribute
¶
CORE: Final = Context(
{
CODECS: _CORE_CODECS,
DATA_TYPE: _CORE_DATA_TYPES,
CHUNK_GRID: _CORE_CHUNK_GRIDS,
CHUNK_KEY_ENCODING: _CORE_CHUNK_KEY_ENCODINGS,
}
)
Only what the Zarr v3 specification defines.
CORE_AND_EXTENSIONS
module-attribute
¶
CORE_AND_EXTENSIONS: Final = Context(
{
CODECS: {**_CORE_CODECS, **_EXTENSION_CODECS},
DATA_TYPE: {
**_CORE_DATA_TYPES,
**_EXTENSION_DATA_TYPES,
},
CHUNK_GRID: {
**_CORE_CHUNK_GRIDS,
**_EXTENSION_CHUNK_GRIDS,
},
CHUNK_KEY_ENCODING: {**_CORE_CHUNK_KEY_ENCODINGS},
}
)
What the specification defines, plus what zarr-extensions registers.
CodecKind
module-attribute
¶
CodecKind = Literal[
"array_array", "array_bytes", "bytes_bytes"
]
The three pipeline positions the v3 spec sorts codecs into.
Here rather than in zarr_metadata.v3.codec.kind because each codec
declares its own kind, and that module imports every codec to build the
tuples it will no longer need once they all do.
Coerced
module-attribute
¶
Coerced: TypeAlias = tuple[
EntityT | None, tuple[ValidationProblem, ...]
]
The entity if it could be built, and every problem found.
One direction holds: no entity means at least one problem. The converse does not -- a survivable problem (an unknown key, an optional member of the wrong type) comes back with the entity, because the entity is still readable and saying so is more useful than refusing.
So test entity is None to decide whether to go on reading, and test the
problems to decide the verdict. They are different questions.
ExtensionPointField
module-attribute
¶
ExtensionPointField = Literal[
"data_type",
"chunk_grid",
"chunk_key_encoding",
"codecs",
"storage_transformers",
]
The v3 array metadata fields whose values name an extension.
Here rather than in _extension_points because an entity that contains
other entities has to say which point it is reading them at, and
_extension_points also folds r<N> names -- which means importing the
data types, which import this.
Extents
module-attribute
¶
Extents: TypeAlias = 'tuple[frozenset[int] | None, ...]'
One entry per dimension: the lengths that dimension's chunks take.
A singleton is a uniform axis. None is an axis whose lengths this
package cannot determine — distinct from an empty set, which would claim
the axis has no chunks at all.
FLOAT_SPECIALS
module-attribute
¶
FLOAT_SPECIALS: Final = ('NaN', 'Infinity', '-Infinity')
The three non-finite floats the spec spells as strings.
MemberTypes
module-attribute
¶
MemberTypes: TypeAlias = (
"Mapping[str, tuple[bool, TypeCheck]]"
)
Per configuration member: whether it is required, and its type check.
StorageClass
module-attribute
¶
StorageClass = Literal[
"single_byte", "multi_byte", "variable_length"
]
How one scalar of a data type occupies bytes.
single_byte and multi_byte are both fixed-size; they differ only in
whether a byte order applies, which is what the bytes codec's endian
member is about.
TypeCheck
module-attribute
¶
TypeCheck: TypeAlias = (
"Callable[[object, Loc], tuple[ValidationProblem, ...]]"
)
Whether one value has the type a member declares, and where if not.
UNKNOWN_GRID
module-attribute
¶
A grid nothing is known about — not even how many dimensions it has.
__all__
module-attribute
¶
__all__ = [
"CHUNK_GRID",
"CHUNK_KEY_ENCODING",
"CODECS",
"CORE",
"CORE_AND_EXTENSIONS",
"DATA_TYPE",
"FLOAT_SPECIALS",
"STORAGE_TRANSFORMERS",
"UNKNOWN_GRID",
"ArrayDocumentV3",
"ArrayParts",
"ChunkGrid",
"ChunkGridEntity",
"CodecEntity",
"CodecKind",
"Coerced",
"ComplexDataType",
"Context",
"DataTypeEntity",
"ExtensionPointField",
"Extents",
"FloatDataType",
"IntegerDataType",
"Loc",
"MemberTypes",
"MetadataEntity",
"NumpyTimeDataType",
"Opaque",
"StorageClass",
"TypeCheck",
"array_problems_v3",
"as_sequence",
"byte_values",
"chain_problems",
"coerce_members",
"is_bool",
"is_int",
"is_integer",
"is_json_value",
"is_str",
"named_configuration",
"one_of",
"order_problems",
"problem",
"read_array_v3",
"sequence_of",
"shard_index_grid",
"within",
]
ArrayDocumentV3
dataclass
¶
A v3 array document with its extension points read as entities.
A field that could not be read holds an Opaque, which carries the
JSON the document wrote and says whether the name was out of scope --
an extension this reader does not model, which is not an error -- or
claimed and refused. Both are narrowable: every field is an exhaustive
two-case union.
Source code in src/zarr_metadata/v3/_document.py
__init__ ¶
__init__(
document: Mapping[str, object],
data_type: DataTypeEntity | Opaque,
chunk_grid: ChunkGridEntity | Opaque,
chunk_key_encoding: MetadataEntity | Opaque,
codecs: tuple[CodecEntity | Opaque, ...],
storage_transformers: tuple[
MetadataEntity | Opaque, ...
],
) -> None
from_json
classmethod
¶
from_json(
value: object, *, context: Context = CORE_AND_EXTENSIONS
) -> ArrayDocumentV3
A v3 array document read into entities, or raise.
The reader's front door, and the one entry point that fails fast:
one call, and either every extension point is read or a single
MetadataValidationError carries every reason it is not --
structural and semantic together. Use validate_array_metadata_v3
instead when you want the problems as data.
A name this context does not model is not a failure. It comes
back as an Opaque marked out_of_scope, because a document may
legitimately use an extension this reader does not know, and
refusing it would make openness unimplementable. What fails is
metadata that is wrong, not metadata that is unfamiliar.
Source code in src/zarr_metadata/v3/_document.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every semantic problem this document has, once it has been read.
The type-space problems are read_array_v3's, because they are
the reasons some of this is Opaque rather than an entity.
Source code in src/zarr_metadata/v3/_document.py
ArrayParts
dataclass
¶
Every part of an array a codec will be handed, and their type.
The parts an array is divided into, not the fields of its metadata. Plural deliberately: one pipeline encodes every chunk, so a rule about it quantifies over all of them — a shard's inner chunk shape must divide every chunk, which under a rectilinear grid is several different lengths.
data_type is the coerced data type, so a rule asks it what it is
rather than comparing names, and it is None where the element type
is undetermined
while the array itself is not. That happens inside a shard: the inner
grid is the sharding codec's own chunk_shape whatever reached it, so
an unreadable codec upstream costs the type and not the parts. None
in place of the whole value means something else again — that there is
no array here at all, past the array->bytes boundary or beyond a codec
that could have changed anything.
Source code in src/zarr_metadata/v3/_parts.py
with_data_type ¶
with_data_type(
data_type: DataTypeEntity | None,
) -> ArrayParts
with_grid ¶
with_grid(grid: ChunkGrid) -> ArrayParts
ChunkGrid
dataclass
¶
The division of an array into the parts a codec pipeline encodes.
Nothing here is the metadata: a grid entity keeps its own, and what reaches a codec is the division, not the spelling of it. A derived grid -- the regular one a sharding codec imposes, or a transposed one -- has no metadata to keep anyway.
Source code in src/zarr_metadata/v3/_parts.py
axis ¶
The lengths dimension's chunks take, or None if undetermined.
derived
classmethod
¶
permuted ¶
This grid with its dimensions reordered by order.
A transposed grid is still a grid — permuting a regular one gives a regular one — but it is no longer the grid the document wrote, so the metadata does not survive the trip.
Declines on anything that is not a permutation of this grid's rank.
The caller checks that too and reports it, but an order is only
shape-validated as a tuple of integers, so this must not be the
thing that decides whether a validator raises IndexError.
Source code in src/zarr_metadata/v3/_parts.py
regular
classmethod
¶
unreadable
classmethod
¶
A grid nothing is known about but the rank the array pins.
Every third-party grid, and every modelled one whose own metadata could not be read: the array still has a rank, and a rule about rank is still answerable.
Source code in src/zarr_metadata/v3/_parts.py
ChunkGridEntity
dataclass
¶
Bases: MetadataEntity
An entity that divides an array into the parts a pipeline encodes.
Source code in src/zarr_metadata/v3/_entity.py
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
grid ¶
What this grid divides an array of array_shape into.
The array shape is a parameter because neither determines a grid alone: a grid whose own metadata cannot be read still has the array's rank, and rank is enough for several rules.
Source code in src/zarr_metadata/v3/_entity.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
shape_problems ¶
shape_problems(
array_shape: object,
) -> tuple[ValidationProblem, ...]
Why this grid does not divide an array of array_shape.
Locations are relative to the grid's configuration. Default:
nothing, for a grid this package reads but has no such rule for.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
CodecEntity
dataclass
¶
Bases: MetadataEntity
An entity that occupies a position in the codec pipeline.
Source code in src/zarr_metadata/v3/_entity.py
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
variable_size
class-attribute
¶
variable_size: bool = False
Whether this codec's output size depends on the bytes it is given.
A compressor's does, so a shard index encoded with one has no size derivable from metadata alone, and the shard cannot be read.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
incoming_problems ¶
incoming_problems(
incoming: ArrayParts | None,
) -> tuple[ValidationProblem, ...]
Why this codec cannot be applied to the array that reaches it.
incoming is None once the chain can no longer say what reaches
here, and the default answer to that is nothing: declining beats
guessing. Locations are relative to this codec's configuration,
as problems' are; an empty one lands on the codec itself.
Source code in src/zarr_metadata/v3/_entity.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
transition ¶
transition(incoming: ArrayParts) -> ArrayParts | None
What the next codec in the chain sees, or None if undeterminable.
Only an array-to-array codec has anything to say: the two later kinds end shape propagation by construction, one by consuming the array and the other by never having had it.
The default is None, so a modelled codec that forgets to say how it transforms the array stops propagation rather than silently claiming to leave it alone. Failing closed here costs a judgment; failing open would invent one.
Source code in src/zarr_metadata/v3/_entity.py
ComplexDataType
dataclass
¶
Bases: DataTypeEntity
A complex number: a [real, imag] pair of the component float type.
Source code in src/zarr_metadata/v3/data_type/_families.py
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
twos_complement
class-attribute
¶
twos_complement: bool = False
Whether this type's scalars are two's complement integers.
Asked by cast_value, whose out_of_range: "wrap" is defined only
for such a target. Required rather than defaulted: a data type added
later must decide, because either default would answer for it
silently -- and getting it wrong in one direction accepts a cast the
spec does not define.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
fill_value_problems ¶
fill_value_problems(
value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]
Why value is not a fill value of this type, if it is not.
Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.
Source code in src/zarr_metadata/v3/data_type/_families.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
storage_class ¶
storage_class() -> StorageClass | None
How one scalar occupies bytes, or None if undetermined.
None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
Context
dataclass
¶
The entities in scope while metadata is being read.
Passed to every coerce, and most entities ignore it: a gzip codec
is a gzip codec whatever else is in scope. The ones that do not
ignore it hold other entities inside their own configuration -- a
struct data type holds field data types, a sharding_indexed codec
holds two codec pipelines -- and cannot read those without knowing
what is in scope inside them.
A scope is not a property of the entities, it is a choice the reader
makes: judging against the specification alone, or against the
specification plus what zarr-extensions registers.
Source code in src/zarr_metadata/v3/_registry.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 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 224 225 226 227 228 229 230 231 | |
entities
instance-attribute
¶
entities: Mapping[
ExtensionPointField, Mapping[str, type[MetadataEntity]]
]
__init__ ¶
__init__(
entities: Mapping[
ExtensionPointField,
Mapping[str, type[MetadataEntity]],
],
) -> None
__post_init__ ¶
Refuse a table whose key an entity would not answer to.
resolve finds a candidate by key and then asks the entity
whether the name is really one of its own, so a key that is not
the entity's identifier can never resolve. If the two disagree
-- a typo, or a rename that missed one of the two places the name
is written -- registration appears to succeed, validation runs,
and the verdict is clean. Indistinguishable from extension
openness, and the easiest way to ship a broken extension.
The key is the identifier, not a name a document writes: the
raw-bytes family registers under an invented one that accepts
deliberately refuses.
Source code in src/zarr_metadata/v3/_registry.py
coerce ¶
coerce(
field: Literal["data_type"],
value: object,
loc: Loc = (),
*,
envelope_judged: bool = False,
) -> tuple[
DataTypeEntity | Opaque, tuple[ValidationProblem, ...]
]
coerce(
field: Literal["codecs"],
value: object,
loc: Loc = (),
*,
envelope_judged: bool = False,
) -> tuple[
CodecEntity | Opaque, tuple[ValidationProblem, ...]
]
coerce(
field: Literal["chunk_grid"],
value: object,
loc: Loc = (),
*,
envelope_judged: bool = False,
) -> tuple[
ChunkGridEntity | Opaque, tuple[ValidationProblem, ...]
]
coerce(
field: ExtensionPointField,
value: object,
loc: Loc = (),
*,
envelope_judged: bool = False,
) -> tuple[
MetadataEntity | Opaque, tuple[ValidationProblem, ...]
]
coerce(
field: ExtensionPointField,
value: object,
loc: Loc = (),
*,
envelope_judged: bool = False,
) -> tuple[
MetadataEntity | Opaque, tuple[ValidationProblem, ...]
]
One nested entity, read in this scope.
The primitive the containing entities are built from: a struct
data type reads its fields with it, a sharding_indexed codec its
two pipelines. Returns the entity when its name is in scope, and
the value untouched when it is not -- an unmodelled extension is
left unjudged, which is what makes the format open.
loc prefixes the problems, so they point at where in the
containing configuration the entity sat.
A metadata field is a metadata field wherever it appears, so the
envelope gets the same structural judgment here that the model
layer gives a top-level one -- an extra member, a configuration
that is not an object, a must_understand that is not a boolean.
envelope_judged says that judgment has already happened, which
it has for the fields of a document the model layer accepted.
Source code in src/zarr_metadata/v3/_registry.py
resolve ¶
resolve(
field: ExtensionPointField, name: str
) -> type[MetadataEntity] | None
The entity name denotes at field, or None if out of scope.
Out of scope is not an error: an unknown name may be an extension this reader does not model, and openness means leaving it unjudged.
The entity has the last word, via accepts. Folding is what finds
a candidate -- every r<N> spelling is tabled under one invented
identifier -- and the candidate is what says whether the name is
really one of its own. Otherwise the identifier itself would be a
name a document could write.
Source code in src/zarr_metadata/v3/_registry.py
DataTypeEntity
dataclass
¶
Bases: MetadataEntity
An entity that says how the array's scalars are stored.
Only data types answer that, and every rule that turns on it -- a
bytes codec is pointless before a single-byte type, a struct field
cannot be variable-length -- asks a data type rather than consulting
a table of names.
Source code in src/zarr_metadata/v3/_entity.py
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
twos_complement
class-attribute
¶
twos_complement: bool
Whether this type's scalars are two's complement integers.
Asked by cast_value, whose out_of_range: "wrap" is defined only
for such a target. Required rather than defaulted: a data type added
later must decide, because either default would answer for it
silently -- and getting it wrong in one direction accepts a cast the
spec does not define.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
fill_value_problems ¶
fill_value_problems(
value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]
Why value is not a fill value of this type, if it is not.
Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.
Source code in src/zarr_metadata/v3/_entity.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
storage_class ¶
storage_class() -> StorageClass | None
How one scalar occupies bytes, or None if undetermined.
None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
FloatDataType
dataclass
¶
Bases: DataTypeEntity
A binary float. A fill value may be a number, a named non-finite, or hex.
Source code in src/zarr_metadata/v3/data_type/_families.py
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
largest
class-attribute
¶
largest: float | None
The largest finite magnitude this width holds, or None for float64.
None because a Python float is a float64, so no literal that reaches here can exceed it.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
twos_complement
class-attribute
¶
twos_complement: bool = False
Whether this type's scalars are two's complement integers.
Asked by cast_value, whose out_of_range: "wrap" is defined only
for such a target. Required rather than defaulted: a data type added
later must decide, because either default would answer for it
silently -- and getting it wrong in one direction accepts a cast the
spec does not define.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
fill_value_problems ¶
fill_value_problems(
value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]
Why value is not a fill value of this type, if it is not.
Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.
Source code in src/zarr_metadata/v3/data_type/_families.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
storage_class ¶
storage_class() -> StorageClass | None
How one scalar occupies bytes, or None if undetermined.
None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
IntegerDataType
dataclass
¶
Bases: DataTypeEntity
A fixed-width integer. The width is the whole difference.
Source code in src/zarr_metadata/v3/data_type/_families.py
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
twos_complement
class-attribute
¶
twos_complement: bool = True
Whether this type's scalars are two's complement integers.
Asked by cast_value, whose out_of_range: "wrap" is defined only
for such a target. Required rather than defaulted: a data type added
later must decide, because either default would answer for it
silently -- and getting it wrong in one direction accepts a cast the
spec does not define.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
fill_value_problems ¶
fill_value_problems(
value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]
Why value is not a fill value of this type, if it is not.
Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.
Source code in src/zarr_metadata/v3/data_type/_families.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
storage_class ¶
storage_class() -> StorageClass | None
How one scalar occupies bytes, or None if undetermined.
None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
MetadataEntity
dataclass
¶
One named entity, coerced from its metadata.
Subclasses add their configuration members as fields, which is what
makes them well-typed by construction: an instance exists only if
coerce accepted the metadata that produced it. An optional member is
typed | None with a default of None, so absence is representable
and a canonical spelling can leave it out.
Frozen, so an entity of hashable members is hashable. One holding a
value out of scope is not, because that value is the JSON the document
wrote and a JSON object is a dict -- the same way any frozen
dataclass holding a list is unhashable. It cannot be an immutable
mapping instead: MappingProxyType is unhashable too, and anything
else stops json.dumps from serializing what to_json returns.
Most subclasses declare member_types and nothing else: the default
coerce and to_json are written once here against that table. The
ones that override are the ones with something particular to say --
a configuration containing other entities, a name that is a family
rather than a constant, a member another member renders meaningless.
Source code in src/zarr_metadata/v3/_entity.py
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | |
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
NumpyTimeDataType
dataclass
¶
Bases: DataTypeEntity
A numpy time scalar: a signed 64-bit count of units, or NaT.
Source code in src/zarr_metadata/v3/data_type/_families.py
configuration_required
class-attribute
¶
configuration_required: bool = False
Whether the bare-name spelling says too little for this entity.
The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.
identifier
class-attribute
¶
identifier: str
The name this entity is registered under.
Usually the name the metadata carries. The raw-bytes data types are
the exception: every r<N> spelling is one family, so the family gets
an invented identifier that no real name can collide with.
member_types
class-attribute
¶
member_types: MemberTypes = MappingProxyType({})
The configuration members, and the type each one takes.
The same keys as the configuration TypedDict, which is the same as the
constructor signature; tests/v3/test_entities.py holds the three
together.
must_understand
class-attribute
instance-attribute
¶
name
property
¶
name: str
The name this entity carries, as a document would write it.
Usually the identifier. They differ for the raw-bytes family,
whose identifier is invented and belongs in no message a reader
sees -- so anything user-facing wants this, and anything looking
something up wants identifier.
required_class_vars
class-attribute
¶
Every class variable a concrete entity of this kind must declare.
twos_complement
class-attribute
¶
twos_complement: bool = False
Whether this type's scalars are two's complement integers.
Asked by cast_value, whose out_of_range: "wrap" is defined only
for such a target. Required rather than defaulted: a data type added
later must decide, because either default would answer for it
silently -- and getting it wrong in one direction accepts a cast the
spec does not define.
__init_subclass__ ¶
Refuse a subclass that forgot to say what it is.
identifier and the per-kind class variables carry no default,
so a subclass omitting one type-checks cleanly and then raises
AttributeError from whichever method is reached first. Saying so
here makes it an import-time error in the extension's own module.
base=True for a class that exists to add a class variable
rather than to be an entity -- CodecEntity, IntegerDataType.
Source code in src/zarr_metadata/v3/_entity.py
accepts
classmethod
¶
Whether name denotes this entity.
Constant for all but the raw-bytes family, where one class covers
every r<N>.
canonical ¶
canonical() -> Self
This entity in the simplest form that means the same thing.
A transformation, asked for by canonicalize_array_metadata_v3
and by nothing else. to_json does not apply it, because writing
a document back is not the same as asking for it to be rewritten:
a reader that reads and writes should not change bytes it was not
asked to change.
Default: entities are already canonical. Override where two
spellings of a member mean the same -- a rectilinear dimension's
run-length encoding, a typesize that noshuffle ignores -- and
where a contained entity has its own canonical form.
Source code in src/zarr_metadata/v3/_entity.py
coerce
classmethod
¶
value as this entity, or the reasons it is not one.
context is the scope this reading is happening in; most entities
have no use for it and ignore it.
Source code in src/zarr_metadata/v3/_entity.py
configuration ¶
This entity's configuration, as the document would write it.
Faithful to every member the entity holds: to_json is
serialization, not canonicalization, so nothing is simplified
here. Override only to render a member that is not already JSON,
such as a contained entity.
Absent optional members are left out, which is what makes the
bare-name spelling reachable. Absence is UNSET, never None:
this package holds None to mean a JSON null the document
actually wrote, and scale_offset is a real case where null
and absent are different documents.
Source code in src/zarr_metadata/v3/_entity.py
fill_value_problems ¶
fill_value_problems(
value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]
Why value is not a fill value of this type, if it is not.
Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.
Source code in src/zarr_metadata/v3/data_type/_families.py
problems ¶
problems() -> tuple[ValidationProblem, ...]
Every value of this entity the spec disallows.
Locations are relative to the entity's configuration. Default:
an entity whose type admits only valid values has nothing to add.
Source code in src/zarr_metadata/v3/_entity.py
storage_class ¶
storage_class() -> StorageClass | None
How one scalar occupies bytes, or None if undetermined.
None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.
Source code in src/zarr_metadata/v3/_entity.py
to_json ¶
to_json() -> ZarrV3MetadataFieldJSON
This entity as a document would write it.
Faithful to every member: read a document, write it back, and the
members come out as they went in. Ask canonical first if you
want the simplest equivalent spelling.
What is not preserved is the envelope's spelling, because the
entity does not model it: a bare name, {"name": x}, and
{"name": x, "configuration": {}} all mean the same and all read
to the same entity, so all three write back as the bare name.
must_understand is omitted when true, which is its default; an
explicit false is kept, because that one says something.
Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.
Source code in src/zarr_metadata/v3/_entity.py
Opaque
dataclass
¶
A metadata field this reading did not turn into an entity.
Carrying the JSON rather than dropping it is what makes the result a
real union: CodecEntity | Opaque is exhaustive and narrows, where
CodecEntity | object is just object and narrows to nothing.
reason is the distinction a reader needs and could not otherwise
make. out_of_scope is a name no entity in this Context claims --
an extension this reader does not model, which is not an error and is
the reader's cue to resolve it elsewhere. invalid is a name that
was claimed and then refused; the reasons are in the problems
reported alongside.
Source code in src/zarr_metadata/v3/_entity.py
array_problems_v3 ¶
array_problems_v3(
document: Mapping[str, object], context: Context
) -> tuple[ValidationProblem, ...]
Every semantic problem in document, read in context.
Expects a document the model layer has already accepted, so every member is present and typed as its TypedDict declares.
Source code in src/zarr_metadata/v3/_document.py
as_sequence ¶
value as a tuple if it is a JSON array, else None.
A string is a sequence in Python and never a JSON array, so it is excluded.
Source code in src/zarr_metadata/v3/data_type/_families.py
byte_values ¶
byte_values(
value: object, expected: int | None, loc: Loc
) -> tuple[ValidationProblem, ...]
An array of expected integers in [0, 255], or any length if None.
Source code in src/zarr_metadata/v3/data_type/_families.py
chain_problems ¶
chain_problems(
codecs: Sequence[object],
start: ArrayParts | None,
loc: Loc,
) -> tuple[ValidationProblem, ...]
Every problem this pipeline has, ordering and per-codec alike.
start is what the first codec receives: the document's own array, or
a shard's inner chunk, or its index.
Source code in src/zarr_metadata/v3/_chain.py
coerce_members ¶
coerce_members(
configuration: Mapping[str, object], types: MemberTypes
) -> tuple[
dict[str, object],
tuple[ValidationProblem, ...],
frozenset[str],
]
The members types declares, taken from configuration.
Returns what was accepted, every problem found, and the names of the required members that could not be read. Three kinds of problem, and they differ in that last part:
- a key the entity does not declare says the value carries something extra, not that it is wrong;
- an optional member of the wrong type leaves that member absent,
and everything else about the entity is still readable -- a bad
index_locationsays nothing about whether a shard's pipelines are well formed, and silencing them would lose a real judgment; - a required member missing or of the wrong type does stop it.
There is no honest reading of a
bloscwhose level is a string.
Source code in src/zarr_metadata/v3/_entity.py
is_bool ¶
is_bool(
value: object, loc: Loc
) -> tuple[ValidationProblem, ...]
is_int ¶
is_int(
value: object, loc: Loc
) -> tuple[ValidationProblem, ...]
An integer, and not a bool -- JSON true is not the integer 1.
Source code in src/zarr_metadata/v3/_entity.py
is_integer ¶
A JSON integer: an int, and not a bool.
True is an int in Python and true is not a number in JSON, so
the two have to be told apart everywhere a number is expected.
Source code in src/zarr_metadata/v3/_entity.py
is_json_value ¶
is_json_value(
value: object, loc: Loc
) -> tuple[ValidationProblem, ...]
Any JSON value at all -- the widest type a member can declare.
Source code in src/zarr_metadata/v3/_entity.py
is_str ¶
is_str(
value: object, loc: Loc
) -> tuple[ValidationProblem, ...]
named_configuration ¶
Split metadata into (name, configuration, must_understand).
The shared shape every entity arrives in: a bare name, or an object
carrying one. A None name means the value is not a metadata field at
all; a None configuration means the bare spelling was used.
Source code in src/zarr_metadata/v3/_entity.py
one_of ¶
A member whose type is a closed set of names.
Source code in src/zarr_metadata/v3/_entity.py
order_problems ¶
order_problems(
codecs: Sequence[object], loc: Loc
) -> tuple[ValidationProblem, ...]
Whether the pipeline is shaped the way the spec orders it.
A codec out of scope is skipped: it imposes no ordering constraint,
and it makes the exactly-one-array->bytes count inconclusive,
because it might be the pipeline's own array->bytes stage. So that
count is only checked when every codec is in scope.
Source code in src/zarr_metadata/v3/_chain.py
problem ¶
problem(
loc: Loc,
message: str,
kind: ProblemKind = "invalid_type",
) -> tuple[ValidationProblem, ...]
One problem, as the tuple every check returns.
read_array_v3 ¶
read_array_v3(
document: Mapping[str, object], context: Context
) -> tuple[ArrayDocumentV3, tuple[ValidationProblem, ...]]
document's extension points, read in context.
Type-space only: what comes back is well-typed by construction, and the problems are the reasons some of it is not an entity.
Source code in src/zarr_metadata/v3/_document.py
sequence_of ¶
A member whose type is a sequence, checked element by element.
Source code in src/zarr_metadata/v3/_entity.py
shard_index_grid ¶
The grid of a shard's index array.
The spec derives it from the two shapes around it: "The index is an array with 64-bit unsigned integers with a shape that matches the chunks per shard tuple with an appended dimension of size 2." The index is one array rather than a divided one, so each axis holds a single length — except that under a rectilinear grid the shard itself varies, so the chunk count varies with it and the axis holds every value it takes.
Source code in src/zarr_metadata/v3/_parts.py
within ¶
within(
prefix: Loc, problems: Sequence[ValidationProblem]
) -> tuple[ValidationProblem, ...]
One entity's problems, located in the document that holds it.
An entity reports relative to its own configuration, so that is what
goes between the field and the member. A problem with an empty
location is about the entity itself -- a malformed r<N> name, a
codec that cannot encode what reaches it -- and lands on the field.