Skip to content

zarr_metadata.model

zarr_metadata.model

In-memory models for Zarr metadata documents.

Models are frozen dataclasses that hold a canonical, semantically lossless representation of the JSON documents. Validators check a document's JSON structure and, in a v3 document, read each extension point (codecs, chunk grids, data types, ...) through the definition that claims its name in a scope, CORE_AND_EXTENSIONS unless a context is passed, and judge the fill value against the data type it names, the chunk grid against the shape, and the codecs as a pipeline, each against the chunk it is handed. Each document concept gets a validate_* function returning every problem found (a tuple of ValidationProblem, each with a machine-readable kind), an is_* type guard, and a parse_* function that narrows or raises MetadataValidationError; a v3 array or group document also gets read_array_metadata_v3, read_group_metadata_v3 or read_array_metadata_v2, one read that returns what it read, the problems, and the model when there are none. A store another writer made, holding a known writer bug, is read by read_repaired_node_metadata_v3, which undoes each one with repair_node_metadata_v3 before the strict read and says what it changed. node_metadata_json_schema_v3 writes what the v3 validators read as a JSON Schema, but for the rules. Model from_json / from_key_value constructors raise MetadataValidationError for every ingestion failure, including missing store keys and undecodable bytes, and the v3 ones take the same context. A v3 model is its document and the scope it was read in: ZarrV3ArrayMetadata(document, context=None) reads the document in the scope and raises MetadataValidationError with every problem, so no model is built invalid; to_json writes the document as written; update reads new members in the model's own scope; with_context and refined_in read the document in another; to_key_value writes a model as it is. A group's consolidated_metadata takes node models as entries, each accepted when its claims refine into the group's scope and refused at its path otherwise. Every reader takes context=None for the default scope.

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: 2 in 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.

RepairKind module-attribute

RepairKind: TypeAlias = Literal[
    "zero_chunk_length",
    "null_consolidated_metadata",
    "consolidated_metadata_in_zgroup_entry",
]

Each writer bug a repair undoes, by name.

UNSET module-attribute

UNSET = Sentinel('UNSET')

Marks a metadata-document key as absent (PEP 661 sentinel; usable directly in type expressions, e.g. tuple[str, ...] | UNSET). Test with is UNSET.

ZARR_V2_ARRAY_METADATA_STORE_KEY module-attribute

ZARR_V2_ARRAY_METADATA_STORE_KEY: Final[
    ZarrV2ArrayMetadataStoreKey
] = ".zarray"

The store key a v2 array's metadata document is persisted under.

ZARR_V2_ATTRIBUTES_STORE_KEY module-attribute

ZARR_V2_ATTRIBUTES_STORE_KEY: Final[
    ZarrV2AttributesStoreKey
] = ".zattrs"

The store key a v2 node's user attributes are persisted under.

Shared by arrays and groups: both node types keep their attributes in a sibling .zattrs file.

ZARR_V2_CONSOLIDATED_METADATA_STORE_KEY module-attribute

ZARR_V2_CONSOLIDATED_METADATA_STORE_KEY: Final[
    ZarrV2ConsolidatedMetadataStoreKey
] = ".zmetadata"

The store key a v2 hierarchy's consolidated metadata is persisted under.

Like the document it names, this is a reference-implementation convention rather than a spec artifact; see the module docstring.

ZARR_V2_GROUP_METADATA_STORE_KEY module-attribute

ZARR_V2_GROUP_METADATA_STORE_KEY: Final[
    ZarrV2GroupMetadataStoreKey
] = ".zgroup"

The store key a v2 group's metadata document is persisted under.

ZARR_V3_ARRAY_METADATA_STORE_KEY module-attribute

ZARR_V3_ARRAY_METADATA_STORE_KEY: Final[
    ZarrV3ArrayMetadataStoreKey
] = "zarr.json"

The store key a v3 array's metadata document is persisted under.

v3 uses one key for both node types; the document's node_type field distinguishes an array from a group.

ZARR_V3_CONSOLIDATED_METADATA_KEY module-attribute

ZARR_V3_CONSOLIDATED_METADATA_KEY: Final = (
    "consolidated_metadata"
)

The key under which consolidated metadata is embedded in a v3 group document.

Unlike the v2 .zmetadata file, this is not a store key: consolidated metadata is carried as an additional field inside the group's own zarr.json. The core spec names the field and its envelope ("For historical reasons, group metadata documents may contain an additional field named consolidated_metadata"); the entry format, like the v2 counterpart, is a reference-implementation convention. https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/core/index.rst#L802-L816

ZARR_V3_GROUP_METADATA_STORE_KEY module-attribute

ZARR_V3_GROUP_METADATA_STORE_KEY: Final[
    ZarrV3GroupMetadataStoreKey
] = "zarr.json"

The store key a v3 group's metadata document is persisted under.

v3 uses one key for both node types; the document's node_type field distinguishes a group from an array.

ZarrV2ArrayMetadataStoreKey module-attribute

ZarrV2ArrayMetadataStoreKey = Literal['.zarray']

Literal type of the store key holding a v2 array's metadata document.

ZarrV2AttributesStoreKey module-attribute

ZarrV2AttributesStoreKey = Literal['.zattrs']

Literal type of the store key holding a v2 node's user attributes.

ZarrV2ConsolidatedMetadataStoreKey module-attribute

ZarrV2ConsolidatedMetadataStoreKey = Literal['.zmetadata']

Literal type of the store key holding a v2 hierarchy's consolidated metadata.

ZarrV2GroupMetadataStoreKey module-attribute

ZarrV2GroupMetadataStoreKey = Literal['.zgroup']

Literal type of the store key holding a v2 group's metadata document.

ZarrV2NodeMetadata module-attribute

ZarrV2NodeMetadata: TypeAlias = (
    "ZarrV2ArrayMetadata | ZarrV2GroupMetadata"
)

The model of one node a v2 .zmetadata document holds: an array, or a group.

ZarrV3ArrayMetadataStoreKey module-attribute

ZarrV3ArrayMetadataStoreKey = Literal['zarr.json']

Literal type of the store key holding a v3 array's metadata document.

ZarrV3GroupMetadataStoreKey module-attribute

ZarrV3GroupMetadataStoreKey = Literal['zarr.json']

Literal type of the store key holding a v3 group's metadata document.

ZarrV3NodeMetadata module-attribute

ZarrV3NodeMetadata = TypeAliasType(
    "ZarrV3NodeMetadata",
    "ZarrV3ArrayMetadata | ZarrV3GroupMetadata",
)

The model of a v3 zarr.json: an array's or a group's, as its node_type says.

ZarrV3NodeMetadataInput module-attribute

ZarrV3NodeMetadataInput = TypeAliasType(
    "ZarrV3NodeMetadataInput",
    "ZarrV3ArrayMetadataJSON | ZarrV3GroupMetadataJSON | ZarrV3ArrayMetadata | ZarrV3GroupMetadata",
)

What consolidated metadata lists at a path when given to a constructor or update: a document, or a model of it.

ZarrV3NodeMetadataReading module-attribute

ZarrV3NodeMetadataReading = TypeAliasType(
    "ZarrV3NodeMetadataReading",
    "ZarrV3ArrayMetadataReading | ZarrV3GroupMetadataReading | ZarrV3UnknownNodeReading",
)

A v3 zarr.json as read_node_metadata_v3 reads it: as the array or group its node_type says, or as neither.

__all__ module-attribute

__all__ = [
    "UNSET",
    "ZARR_V2_ARRAY_METADATA_STORE_KEY",
    "ZARR_V2_ATTRIBUTES_STORE_KEY",
    "ZARR_V2_CONSOLIDATED_METADATA_STORE_KEY",
    "ZARR_V2_GROUP_METADATA_STORE_KEY",
    "ZARR_V3_ARRAY_METADATA_STORE_KEY",
    "ZARR_V3_CONSOLIDATED_METADATA_KEY",
    "ZARR_V3_GROUP_METADATA_STORE_KEY",
    "MetadataValidationError",
    "ProblemKind",
    "Repair",
    "RepairKind",
    "ValidationProblem",
    "ZarrV2ArrayMetadata",
    "ZarrV2ArrayMetadataReading",
    "ZarrV2ArrayMetadataStoreKey",
    "ZarrV2ArrayMetadataUpdate",
    "ZarrV2AttributesStoreKey",
    "ZarrV2ConsolidatedMetadata",
    "ZarrV2ConsolidatedMetadataStoreKey",
    "ZarrV2GroupMetadata",
    "ZarrV2GroupMetadataStoreKey",
    "ZarrV2GroupMetadataUpdate",
    "ZarrV2NodeMetadata",
    "ZarrV2RepairedConsolidatedMetadataReading",
    "ZarrV3ArrayMetadata",
    "ZarrV3ArrayMetadataReading",
    "ZarrV3ArrayMetadataStoreKey",
    "ZarrV3ArrayMetadataUpdate",
    "ZarrV3ConsolidatedMetadata",
    "ZarrV3ConsolidatedMetadataInput",
    "ZarrV3GroupMetadata",
    "ZarrV3GroupMetadataReading",
    "ZarrV3GroupMetadataStoreKey",
    "ZarrV3GroupMetadataUpdate",
    "ZarrV3NodeMetadata",
    "ZarrV3NodeMetadataInput",
    "ZarrV3NodeMetadataReading",
    "ZarrV3RepairedNodeMetadataReading",
    "ZarrV3UnknownNodeReading",
    "is_array_metadata_v2",
    "is_array_metadata_v3",
    "is_group_metadata_v2",
    "is_group_metadata_v3",
    "is_metadata_field_v3",
    "is_node_name_v3",
    "is_node_path_v3",
    "node_metadata_from_json_v3",
    "node_metadata_from_key_value_v3",
    "node_metadata_json_schema_v3",
    "parse_array_metadata_v2",
    "parse_array_metadata_v3",
    "parse_group_metadata_v2",
    "parse_group_metadata_v3",
    "parse_metadata_field_v3",
    "parse_node_name_v3",
    "parse_node_path_v3",
    "read_array_metadata_v2",
    "read_array_metadata_v3",
    "read_group_metadata_v3",
    "read_node_metadata_v3",
    "read_repaired_consolidated_metadata_v2",
    "read_repaired_node_metadata_v3",
    "repair_consolidated_metadata_v2",
    "repair_node_metadata_v3",
    "validate_array_metadata_v2",
    "validate_array_metadata_v3",
    "validate_group_metadata_v2",
    "validate_group_metadata_v3",
    "validate_metadata_field_v3",
    "validate_node_metadata_v3",
    "validate_node_name_v3",
    "validate_node_path_v3",
]

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
class MetadataValidationError(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.
    """

    problems: tuple[ValidationProblem, ...]

    def __init__(self, problems: Sequence[ValidationProblem]) -> None:
        self.problems = tuple(problems)
        for entry in cast("tuple[object, ...]", self.problems):
            # The runtime half of the annotation: a caller that is not
            # type-checked, and hands over anything else, fails here
            # rather than far away, where a `loc` is read off it.
            if not isinstance(entry, ValidationProblem):
                msg = (
                    "MetadataValidationError takes ValidationProblem values, "
                    f"got {type(entry).__name__}"
                )
                raise TypeError(msg)
        super().__init__("\n".join(str(problem) for problem in self.problems))

    def __reduce__(
        self,
    ) -> tuple[
        type[MetadataValidationError], tuple[tuple[ValidationProblem, ...]], dict[str, object]
    ]:
        # An exception pickles and copies as its class called with its
        # `args`, which here are the message; it is built from its problems,
        # and the rest of its state -- its notes among it -- follows.
        return (type(self), (self.problems,), self.__dict__)

problems instance-attribute

problems: tuple[ValidationProblem, ...] = tuple(problems)

__init__

__init__(problems: Sequence[ValidationProblem]) -> None
Source code in src/zarr_metadata/_json.py
def __init__(self, problems: Sequence[ValidationProblem]) -> None:
    self.problems = tuple(problems)
    for entry in cast("tuple[object, ...]", self.problems):
        # The runtime half of the annotation: a caller that is not
        # type-checked, and hands over anything else, fails here
        # rather than far away, where a `loc` is read off it.
        if not isinstance(entry, ValidationProblem):
            msg = (
                "MetadataValidationError takes ValidationProblem values, "
                f"got {type(entry).__name__}"
            )
            raise TypeError(msg)
    super().__init__("\n".join(str(problem) for problem in self.problems))

__reduce__

Source code in src/zarr_metadata/_json.py
def __reduce__(
    self,
) -> tuple[
    type[MetadataValidationError], tuple[tuple[ValidationProblem, ...]], dict[str, object]
]:
    # An exception pickles and copies as its class called with its
    # `args`, which here are the message; it is built from its problems,
    # and the rest of its state -- its notes among it -- follows.
    return (type(self), (self.problems,), self.__dict__)

Repair dataclass

One change a repair made to a document: where, which bug it undid, and what it did.

Source code in src/zarr_metadata/model/_repair.py
@dataclass(frozen=True, slots=True)
class Repair:
    """One change a repair made to a document: where, which bug it undid, and what it did."""

    loc: Loc
    """Where the change is, in the document: the value changed, or the key removed."""
    kind: RepairKind
    """The writer bug the change undoes."""
    message: str
    """What was written, by which writer, and what it became."""

kind instance-attribute

kind: RepairKind

The writer bug the change undoes.

loc instance-attribute

loc: Loc

Where the change is, in the document: the value changed, or the key removed.

message instance-attribute

message: str

What was written, by which writer, and what it became.

__init__

__init__(loc: Loc, kind: RepairKind, message: str) -> None

ValidationProblem dataclass

A single problem found in a value: where it is, what is wrong, what kind of wrong, and the data the message is made of.

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.

input and ctx are what the message says, as data, as pydantic's ErrorDetails and zod's issues carry theirs. input is the JSON found at loc -- 12, for a gzip level of 12 -- and UNSET where nothing is there, as zod has it for a key that is missing (pydantic gives the object missing it), or where what is there is not JSON, which the message shows. It is the object the caller handed in, as pydantic's is, not a copy: a caller that changes its document afterwards changes what input shows. ctx is what was expected, where that is more than a type:

  • gt, ge, lt and le: the bounds the value's type carries, as pydantic names them -- {"ge": 0, "le": 9} for a gzip level, whose type is Annotated[int, Interval(ge=0, le=9)] -- or a rule says.
  • expected: the values of a closed set, as zod's values holds them, in the order the message lists them -- a Literal's, node_type's, zarr_format's.

Neither takes part in equality or the repr: a problem is the same problem when it is found at the same place and says the same thing. Every function that returns or raises problems fills input from the value its caller handed it, so a rule says only where a problem is.

Source code in src/zarr_metadata/_json.py
@dataclass(frozen=True, slots=True)
class ValidationProblem:
    """A single problem found in a value: where it is, what is wrong, what kind of wrong, and the data the message is made of.

    `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.

    `input` and `ctx` are what the message says, as data, as pydantic's
    `ErrorDetails` and zod's issues carry theirs. `input` is the JSON found
    at `loc` -- `12`, for a gzip `level` of 12 -- and `UNSET` where nothing
    is there, as zod has it for a key that is missing (pydantic gives the
    object missing it), or where what is there is not JSON, which the
    message shows. It is the object the caller handed in, as pydantic's
    is, not a copy: a caller that changes its document afterwards changes
    what `input` shows. `ctx` is what was expected, where that is more
    than a type:

    - `gt`, `ge`, `lt` and `le`: the bounds the value's type carries, as
      pydantic names them -- `{"ge": 0, "le": 9}` for a gzip `level`,
      whose type is `Annotated[int, Interval(ge=0, le=9)]` -- or a rule
      says.
    - `expected`: the values of a closed set, as zod's `values` holds
      them, in the order the message lists them -- a `Literal`'s,
      `node_type`'s, `zarr_format`'s.

    Neither takes part in equality or the repr: a problem is the same
    problem when it is found at the same place and says the same thing.
    Every function that returns or raises problems fills `input` from the
    value its caller handed it, so a rule says only where a problem is.
    """

    loc: tuple[str | int, ...]
    message: str
    kind: ProblemKind
    input: JSONValue | UNSET = dataclasses.field(
        default=UNSET, kw_only=True, compare=False, repr=False
    )
    ctx: Mapping[str, JSONValue] = dataclasses.field(
        default_factory=_no_ctx, kw_only=True, compare=False, repr=False
    )

    def __post_init__(self) -> None:
        # The runtime half of the annotations: a rule written without a type
        # checker, as an extension's may be, fails where it builds a problem
        # rather than reporting one at a location that is not one.
        loc = cast("object", self.loc)
        if not isinstance(loc, tuple) or not all(
            isinstance(part, str) or (isinstance(part, int) and not isinstance(part, bool))
            for part in cast("tuple[object, ...]", loc)
        ):
            msg = f"a ValidationProblem's loc is a tuple of keys and indices, got {loc!r}"
            raise TypeError(msg)
        message = cast("object", self.message)
        if not isinstance(message, str):
            msg = f"a ValidationProblem's message is a string, got {message!r}"
            raise TypeError(msg)
        kind = cast("object", self.kind)
        if not isinstance(kind, str) or kind not in get_args(ProblemKind):
            msg = f"a ValidationProblem's kind is one of {get_args(ProblemKind)!r}, got {kind!r}"
            raise TypeError(msg)
        ctx = cast("object", self.ctx)
        if isinstance(ctx, _Ctx):
            # A problem's own, checked when it was made: what `replace`
            # hands a copy.
            return
        if (
            not isinstance(ctx, Mapping)
            or not all(isinstance(key, str) for key in cast("Mapping[object, object]", ctx))
            or not is_json(dict(cast("Mapping[str, object]", ctx)))
        ):
            msg = f"a ValidationProblem's ctx is an object of JSON values, got {ctx!r}"
            raise TypeError(msg)
        # Held as a view of a copy of its own at every level, arrays as
        # tuples, so a raised error, a finished report, cannot be edited
        # through it, nor through what was handed in.
        held = cast(
            "dict[str, JSONValue]",
            copied(cast("JSONValue", arrays_to_tuples(dict(cast("Mapping[str, object]", ctx))))),
        )
        object.__setattr__(self, "ctx", _Ctx(held))

    def __str__(self) -> str:
        location = ".".join(str(part) for part in self.loc) if self.loc else "<root>"
        return f"{location}: {self.message}"

    def __reduce__(
        self,
    ) -> tuple[Callable[..., ValidationProblem], tuple[object, ...]]:
        # Pickled and copied as its constructor called again: the view
        # `ctx` is held as does not pickle, and the dict it views does.
        return (_problem, (self.loc, self.message, self.kind, self.input, dict(self.ctx)))

ctx class-attribute instance-attribute

ctx: Mapping[str, JSONValue] = dataclasses.field(
    default_factory=_no_ctx,
    kw_only=True,
    compare=False,
    repr=False,
)

input class-attribute instance-attribute

input: JSONValue | UNSET = dataclasses.field(
    default=UNSET, kw_only=True, compare=False, repr=False
)

kind instance-attribute

loc instance-attribute

loc: tuple[str | int, ...]

message instance-attribute

message: str

__init__

__init__(
    loc: tuple[str | int, ...],
    message: str,
    kind: ProblemKind,
    *,
    input: JSONValue | UNSET = UNSET,
    ctx: Mapping[str, JSONValue] = _no_ctx(),
) -> None

__post_init__

__post_init__() -> None
Source code in src/zarr_metadata/_json.py
def __post_init__(self) -> None:
    # The runtime half of the annotations: a rule written without a type
    # checker, as an extension's may be, fails where it builds a problem
    # rather than reporting one at a location that is not one.
    loc = cast("object", self.loc)
    if not isinstance(loc, tuple) or not all(
        isinstance(part, str) or (isinstance(part, int) and not isinstance(part, bool))
        for part in cast("tuple[object, ...]", loc)
    ):
        msg = f"a ValidationProblem's loc is a tuple of keys and indices, got {loc!r}"
        raise TypeError(msg)
    message = cast("object", self.message)
    if not isinstance(message, str):
        msg = f"a ValidationProblem's message is a string, got {message!r}"
        raise TypeError(msg)
    kind = cast("object", self.kind)
    if not isinstance(kind, str) or kind not in get_args(ProblemKind):
        msg = f"a ValidationProblem's kind is one of {get_args(ProblemKind)!r}, got {kind!r}"
        raise TypeError(msg)
    ctx = cast("object", self.ctx)
    if isinstance(ctx, _Ctx):
        # A problem's own, checked when it was made: what `replace`
        # hands a copy.
        return
    if (
        not isinstance(ctx, Mapping)
        or not all(isinstance(key, str) for key in cast("Mapping[object, object]", ctx))
        or not is_json(dict(cast("Mapping[str, object]", ctx)))
    ):
        msg = f"a ValidationProblem's ctx is an object of JSON values, got {ctx!r}"
        raise TypeError(msg)
    # Held as a view of a copy of its own at every level, arrays as
    # tuples, so a raised error, a finished report, cannot be edited
    # through it, nor through what was handed in.
    held = cast(
        "dict[str, JSONValue]",
        copied(cast("JSONValue", arrays_to_tuples(dict(cast("Mapping[str, object]", ctx))))),
    )
    object.__setattr__(self, "ctx", _Ctx(held))

__reduce__

__reduce__() -> tuple[
    Callable[..., ValidationProblem], tuple[object, ...]
]
Source code in src/zarr_metadata/_json.py
def __reduce__(
    self,
) -> tuple[Callable[..., ValidationProblem], tuple[object, ...]]:
    # Pickled and copied as its constructor called again: the view
    # `ctx` is held as does not pickle, and the dict it views does.
    return (_problem, (self.loc, self.message, self.kind, self.input, dict(self.ctx)))

__str__

__str__() -> str
Source code in src/zarr_metadata/_json.py
def __str__(self) -> str:
    location = ".".join(str(part) for part in self.loc) if self.loc else "<root>"
    return f"{location}: {self.message}"

ZarrV2ArrayMetadata

Bases: Keyed

A v2 array document, and the scope it was read in.

The pair, as the v3 models are: to_json is the merged document -- the .zarray members, and attributes when a .zattrs holds them -- as written, refined, with one spelling put in: a .zarray that omits dimension_separator means "." by the v2 convention (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L81-L86), which the model holds and writes. dtype, compressor and each filter are views of the reading: AcceptedField by the definition in scope that claims the typestr or id, or UnclaimedField. attributes is UNSET when no .zattrs exists, distinct from an empty one. A member the spec does not define is kept, in extra_fields. Built only by reading: the constructor reads document in context and raises MetadataValidationError with every problem, so no model is invalid. Two models are equal when their documents mean the same in their scopes, as its key says. update reads new members in the model's own scope; with_context and refined_in read the document in another. A model pickles as its pair.

Source code in src/zarr_metadata/model/_array.py
class ZarrV2ArrayMetadata(Keyed):
    """A v2 array document, and the scope it was read in.

    The pair, as the v3 models are: `to_json` is the merged document --
    the `.zarray` members, and `attributes` when a `.zattrs` holds them --
    as written, refined, with one spelling put in: a `.zarray` that omits
    `dimension_separator` means `"."` by the v2 convention
    (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L81-L86),
    which the model holds and writes. `dtype`, `compressor` and each
    filter are views of the reading: `AcceptedField` by the definition in scope
    that claims the typestr or id, or `UnclaimedField`. `attributes` is
    `UNSET` when no `.zattrs` exists, distinct from an empty one. A
    member the spec does not define is kept, in `extra_fields`. Built
    only by reading: the constructor reads `document` in `context` and
    raises `MetadataValidationError` with every problem, so no model is
    invalid. Two models are equal when their documents mean the same in
    their scopes, as its key says. `update` reads new members in
    the model's own scope; `with_context` and `refined_in` read the
    document in another. A model pickles as its pair.
    """

    __slots__ = ("_claims", "_context", "_document", "_members", "_reading", "_shown")

    zarr_format: Final = 2

    @property
    def claims(self) -> Claims:
        """What the reading claimed of each typestr and codec id the document writes, keyed as the scope files them."""
        return self._claims

    def __init__(self, document: object, context: Context | None = None) -> None:
        scope = CORE_V2 if context is None else context
        reading, members = read_array_v2(document, scope)
        if members is None:
            raise MetadataValidationError(reading.problems)
        held = refined_object(document)
        if "dimension_separator" not in held:
            held = {**held, "dimension_separator": "."}
        self._adopt(held, scope, reading, members)

    @classmethod
    def _of(
        cls,
        document: dict[str, JSONValue],
        context: Context,
        reading: ZarrV2ArrayMetadataReading,
        members: ArrayMembersV2,
    ) -> ZarrV2ArrayMetadata:
        """A model of a document a read found nothing wrong with, holding that reading: no second read. The readers of this package build models through this, the private use pyright reports."""
        model = object.__new__(cls)
        model._adopt(document, context, reading, members)
        return model

    def _adopt(
        self,
        document: dict[str, JSONValue],
        context: Context,
        reading: ZarrV2ArrayMetadataReading,
        members: ArrayMembersV2,
    ) -> None:
        self._document = document
        self._context = context
        self._reading = dataclasses.replace(reading, metadata=self)
        self._members = members
        # What the model shows of its members, read-only at every level.
        self._shown = (
            frozen(members.fill_value),
            UNSET if members.attributes is UNSET else frozen(members.attributes),
            frozen(members.extra_fields),
        )
        self._key = self._key_of()
        self._claims = MappingProxyType(claims_of(reading.fields()))

    # --- the pair ---------------------------------------------------------

    @property
    def context(self) -> Context:
        """The scope the document was read in, which `update` reads new members in."""
        return self._context

    @property
    def reading(self) -> ZarrV2ArrayMetadataReading:
        """The document as the scope read it: the dtype, the compressor, each filter."""
        return self._reading

    def to_json(self) -> ZarrV2ArrayMetadataJSON:
        """The merged document as written, refined, sharing nothing with the model.

        `attributes` is included when set, even empty. This is not the
        on-disk `.zarray`, which excludes them: `to_key_value` splits the
        document as a store holds it
        (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L323-L330).
        """
        return cast("ZarrV2ArrayMetadataJSON", copied(self._document))

    def to_key_value(
        self, *, indent: int | str | None = None
    ) -> Mapping[ZarrV2ArrayMetadataStoreKey | ZarrV2AttributesStoreKey, bytes]:
        """The document as a store holds it: `.zarray` without the attributes, and `.zattrs` with them when they are set, even empty."""
        zarray = {key: value for key, value in self._document.items() if key != "attributes"}
        out: dict[ZarrV2ArrayMetadataStoreKey | ZarrV2AttributesStoreKey, bytes] = {
            ZARR_V2_ARRAY_METADATA_STORE_KEY: dump_store_json(zarray, indent=indent)
        }
        if "attributes" in self._document:
            out[ZARR_V2_ATTRIBUTES_STORE_KEY] = dump_store_json(
                self._document["attributes"], indent=indent
            )
        return out

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

    def _key_of(self) -> tuple[object, ...]:
        """What `==` and `hash` compare of a v2 array model: what its document means.

        Each field by its `field_key`, the fill value in its canonical spelling
        as JSON text when a definition in scope read the dtype, and every other
        member as it is, the JSON ones as text; `attributes` as `UNSET` when
        there is no `.zattrs`.
        """
        members = self._members
        return (
            members.shape,
            members.chunks,
            members.order,
            members.dimension_separator,
            self._fill_value_key(),
            field_key(self.dtype),
            None if self.compressor is None else field_key(self.compressor),
            None if self.filters is None else tuple(field_key(entry) for entry in self.filters),
            UNSET if members.attributes is UNSET else json_text(members.attributes),
            json_text(members.extra_fields),
        )

    def _plain_key(
        self, dtype: AcceptedField[ZarrV2DataTypeDefinition[Any]] | UnclaimedField
    ) -> tuple[object, ...]:
        """What `refines` compares of a model other than its fields, the fill value spelled as `dtype` -- the more informed side's -- spells it."""
        members = self._members
        return (
            members.shape,
            members.chunks,
            members.order,
            members.dimension_separator,
            json_text(spelled_canonically(dtype, members.fill_value)),
            UNSET if members.attributes is UNSET else json_text(members.attributes),
            json_text(members.extra_fields),
        )

    def _fill_value_key(self) -> str:
        """What `==` compares of the fill value: its canonical spelling as JSON text when a definition in scope read the dtype, and the fill value as written when none did."""
        fill_value = self._members.fill_value
        if isinstance(self.dtype, AcceptedField):
            return json_text(spelled_canonically(self.dtype, fill_value))
        return json_text(fill_value)

    def __reduce__(self) -> tuple[type[ZarrV2ArrayMetadata], tuple[object, Context]]:
        return type(self), (self._document, self._context)

    # --- typed views ------------------------------------------------------

    @property
    def shape(self) -> tuple[int, ...]:
        """The array's shape."""
        return self._members.shape

    @property
    def chunks(self) -> tuple[int, ...]:
        """The shape of each chunk."""
        return self._members.chunks

    @property
    def fill_value(self) -> JSONValue:
        """The fill value as written, refined; read-only at every level."""
        return self._shown[0]

    @property
    def order(self) -> ZarrV2ArrayOrder:
        """The in-chunk layout, `"C"` or `"F"`."""
        return self._members.order

    @property
    def dimension_separator(self) -> ZarrV2ArrayDimensionSeparator:
        """What joins the chunk indices in a key: `"."` when the document writes none."""
        return self._members.dimension_separator

    @property
    def attributes(self) -> Mapping[str, JSONValue] | UNSET:
        """The user attributes a `.zattrs` holds, read-only at every level; `UNSET` when there is no `.zattrs`."""
        return self._shown[1]

    @property
    def extra_fields(self) -> Mapping[str, JSONValue]:
        """Every member the spec does not define, as written, read-only at every level."""
        return self._shown[2]

    @property
    def dtype(self) -> AcceptedField[ZarrV2DataTypeDefinition[Any]] | UnclaimedField:
        """The dtype as the scope read it: by its family's definition, or unclaimed."""
        return held(self._reading.dtype)

    @property
    def compressor(self) -> AcceptedField[ZarrV2CodecDefinition[Any]] | UnclaimedField | None:
        """The compressor as the scope read it; None when written as `null`."""
        compressor = self._reading.compressor
        return None if compressor is None else held(compressor)

    @property
    def filters(
        self,
    ) -> tuple[AcceptedField[ZarrV2CodecDefinition[Any]] | UnclaimedField, ...] | None:
        """The filters, each as the scope read it; None when written as `null`."""
        filters = self._reading.filters
        if filters is None:
            return None
        if filters is UNSET:
            msg = "expected filters a read found nothing wrong with, got UNSET"
            raise TypeError(msg)
        return tuple(held(entry) for entry in filters)

    # --- changing ---------------------------------------------------------

    def update(self, **members: Unpack[ZarrV2ArrayMetadataUpdate]) -> ZarrV2ArrayMetadata:
        """This model with `members`, JSON, in place of the document's, `UNSET` leaving one out, read in this model's own scope.

        `MetadataValidationError` when the document they make has a
        problem, so members that go together are passed together: a
        `dtype` with a fill value of it.
        """
        document: dict[str, object] = {**self._document, **members}
        for key, value in members.items():
            if value is UNSET:
                del document[key]
        return type(self)(document, context=self._context)

    def with_context(self, context: Context | None = None) -> ZarrV2ArrayMetadata:
        """This document read in `context`, whatever that changes: a gain, a loss, a conflict.

        `MetadataValidationError` when the document has a problem there.
        The reading is kept when `context` reads every claim identically.
        """
        scope = CORE_V2 if context is None else context
        if scope.disagreements(self._claims).agrees:
            return self._of(self._document, scope, self._reading, self._members)
        return type(self)(self._document, context=scope)

    def refined_in(self, context: Context | None = None) -> ZarrV2ArrayMetadata:
        """This document read in `context`, which may claim what this scope left unclaimed and contradict nothing.

        `ScopeConflictError` naming each typestr or id `context` reads by
        another definition, or by none, where this scope read it by one,
        and where each sits in the document. `MetadataValidationError`
        when a definition `context` claims refuses what was written.
        """
        scope = CORE_V2 if context is None else context
        found = scope.disagreements(self._claims)
        if len(found.conflicts) != 0:
            raise ScopeConflictError(located_conflicts(self._reading.fields(), found.conflicts))
        return self.with_context(scope)

    def refines(self, other: ZarrV2ArrayMetadata) -> bool:
        """Whether this model holds everything `other` holds: each field refines its counterpart, a `null` compressor or filters only a `null`, and every other member is the same, the fill value as the more informed dtype spells it; a fill value that dtype refuses is no refinement."""
        if type(other) is not type(self):
            return False
        if (self.compressor is None) != (other.compressor is None):
            return False
        if (self.filters is None) != (other.filters is None):
            return False
        mine = () if self.filters is None else self.filters
        theirs = () if other.filters is None else other.filters
        if len(mine) != len(theirs):
            return False
        pairs = [(self.dtype, other.dtype), *zip(mine, theirs, strict=True)]
        if self.compressor is not None and other.compressor is not None:
            pairs.append((self.compressor, other.compressor))
        if not all(refines_field(one, another) for one, another in pairs):
            return False
        if len(fill_value_problems(self.dtype, other._members.fill_value)) != 0:
            return False
        return self._plain_key(self.dtype) == other._plain_key(self.dtype)

    # --- constructors -----------------------------------------------------

    @classmethod
    def create_default(
        cls, *, context: Context | None = None, **overrides: Unpack[ZarrV2ArrayMetadataUpdate]
    ) -> ZarrV2ArrayMetadata:
        """A scalar `|u1` array, or the one `overrides`, members of its document, make of it, read in `context`.

        `MetadataValidationError` when the document they make has a
        problem. Overriding `shape` without `chunks` derives `chunks`
        equal to `shape`, one chunk covering the array; overriding `chunks`
        without `shape` keeps the scalar default shape, which chunks of
        another rank do not fit. A dtype given without a fill value takes
        `0` when its family takes it, and `null` otherwise, which every
        family takes.
        """
        document: dict[str, object] = {
            "zarr_format": 2,
            "shape": (),
            "chunks": (),
            "dtype": "|u1",
            "fill_value": 0,
            "order": "C",
            "compressor": None,
            "filters": None,
            "dimension_separator": ".",
        }
        given: dict[str, object] = dict(overrides)
        if "shape" in given and "chunks" not in given:
            lengths, _ = dimension_lengths(given, "shape")
            if lengths is not None:
                given["chunks"] = lengths
        if "dtype" in given and "fill_value" not in given:
            dtype, _ = resolve_dtype_v2(given["dtype"], context)
            if len(fill_value_problems(dtype, 0)) != 0:
                given["fill_value"] = None
        merged = {key: value for key, value in {**document, **given}.items() if value is not UNSET}
        return cls(merged, context=context)

    @classmethod
    def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV2ArrayMetadata:
        """The model of `data`, a v2 array document with its attributes under `attributes`, read in `context`.

        `MetadataValidationError` with every problem the read finds.
        `read_array_metadata_v2` gives the reading this model is built
        from, and the problems of a document with some.
        """
        return cls(data, context=context)

    @classmethod
    def from_key_value(
        cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
    ) -> ZarrV2ArrayMetadata:
        """The model of the array at `.zarray` in `mapping`, with the attributes at `.zattrs` when there is one, read in `context`.

        `MetadataValidationError` when `.zarray` is missing, bytes are not
        JSON, `.zarray` holds `attributes`, or the document is not valid.
        """
        zarray_raw = load_store_json(mapping, ZARR_V2_ARRAY_METADATA_STORE_KEY)
        if not is_object(zarray_raw):
            return cls(zarray_raw, context=context)
        zarray = zarray_raw
        if "attributes" in zarray:
            refused = ValidationProblem(
                ("attributes",), "unexpected document member", "invalid_value"
            )
            raise MetadataValidationError(with_input((refused,), zarray))
        if ZARR_V2_ATTRIBUTES_STORE_KEY in mapping:
            zattrs = load_store_json(mapping, ZARR_V2_ATTRIBUTES_STORE_KEY)
            return cls({**zarray, "attributes": zattrs}, context=context)
        return cls(zarray, context=context)

__slots__ class-attribute instance-attribute

__slots__ = (
    "_claims",
    "_context",
    "_document",
    "_members",
    "_reading",
    "_shown",
)

attributes property

attributes: Mapping[str, JSONValue] | UNSET

The user attributes a .zattrs holds, read-only at every level; UNSET when there is no .zattrs.

chunks property

chunks: tuple[int, ...]

The shape of each chunk.

claims property

claims: Claims

What the reading claimed of each typestr and codec id the document writes, keyed as the scope files them.

compressor property

The compressor as the scope read it; None when written as null.

context property

context: Context

The scope the document was read in, which update reads new members in.

dimension_separator property

dimension_separator: ZarrV2ArrayDimensionSeparator

What joins the chunk indices in a key: "." when the document writes none.

dtype property

The dtype as the scope read it: by its family's definition, or unclaimed.

extra_fields property

extra_fields: Mapping[str, JSONValue]

Every member the spec does not define, as written, read-only at every level.

fill_value property

fill_value: JSONValue

The fill value as written, refined; read-only at every level.

filters property

filters: (
    tuple[
        AcceptedField[ZarrV2CodecDefinition[Any]]
        | UnclaimedField,
        ...,
    ]
    | None
)

The filters, each as the scope read it; None when written as null.

order property

The in-chunk layout, "C" or "F".

reading property

The document as the scope read it: the dtype, the compressor, each filter.

shape property

shape: tuple[int, ...]

The array's shape.

zarr_format class-attribute instance-attribute

zarr_format: Final = 2

__eq__

__eq__(other: object) -> bool
Source code in src/zarr_metadata/model/_keyed.py
def __eq__(self, other: object) -> bool:
    if type(other) is not type(self):
        return NotImplemented
    return self._key == other._key

__hash__

__hash__() -> int
Source code in src/zarr_metadata/model/_keyed.py
def __hash__(self) -> int:
    return hash(self._key)

__init__

__init__(
    document: object, context: Context | None = None
) -> None
Source code in src/zarr_metadata/model/_array.py
def __init__(self, document: object, context: Context | None = None) -> None:
    scope = CORE_V2 if context is None else context
    reading, members = read_array_v2(document, scope)
    if members is None:
        raise MetadataValidationError(reading.problems)
    held = refined_object(document)
    if "dimension_separator" not in held:
        held = {**held, "dimension_separator": "."}
    self._adopt(held, scope, reading, members)

__reduce__

Source code in src/zarr_metadata/model/_array.py
def __reduce__(self) -> tuple[type[ZarrV2ArrayMetadata], tuple[object, Context]]:
    return type(self), (self._document, self._context)

__repr__

__repr__() -> str
Source code in src/zarr_metadata/model/_array.py
def __repr__(self) -> str:
    return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

create_default classmethod

create_default(
    *,
    context: Context | None = None,
    **overrides: Unpack[ZarrV2ArrayMetadataUpdate],
) -> ZarrV2ArrayMetadata

A scalar |u1 array, or the one overrides, members of its document, make of it, read in context.

MetadataValidationError when the document they make has a problem. Overriding shape without chunks derives chunks equal to shape, one chunk covering the array; overriding chunks without shape keeps the scalar default shape, which chunks of another rank do not fit. A dtype given without a fill value takes 0 when its family takes it, and null otherwise, which every family takes.

Source code in src/zarr_metadata/model/_array.py
@classmethod
def create_default(
    cls, *, context: Context | None = None, **overrides: Unpack[ZarrV2ArrayMetadataUpdate]
) -> ZarrV2ArrayMetadata:
    """A scalar `|u1` array, or the one `overrides`, members of its document, make of it, read in `context`.

    `MetadataValidationError` when the document they make has a
    problem. Overriding `shape` without `chunks` derives `chunks`
    equal to `shape`, one chunk covering the array; overriding `chunks`
    without `shape` keeps the scalar default shape, which chunks of
    another rank do not fit. A dtype given without a fill value takes
    `0` when its family takes it, and `null` otherwise, which every
    family takes.
    """
    document: dict[str, object] = {
        "zarr_format": 2,
        "shape": (),
        "chunks": (),
        "dtype": "|u1",
        "fill_value": 0,
        "order": "C",
        "compressor": None,
        "filters": None,
        "dimension_separator": ".",
    }
    given: dict[str, object] = dict(overrides)
    if "shape" in given and "chunks" not in given:
        lengths, _ = dimension_lengths(given, "shape")
        if lengths is not None:
            given["chunks"] = lengths
    if "dtype" in given and "fill_value" not in given:
        dtype, _ = resolve_dtype_v2(given["dtype"], context)
        if len(fill_value_problems(dtype, 0)) != 0:
            given["fill_value"] = None
    merged = {key: value for key, value in {**document, **given}.items() if value is not UNSET}
    return cls(merged, context=context)

from_json classmethod

from_json(
    data: object, *, context: Context | None = None
) -> ZarrV2ArrayMetadata

The model of data, a v2 array document with its attributes under attributes, read in context.

MetadataValidationError with every problem the read finds. read_array_metadata_v2 gives the reading this model is built from, and the problems of a document with some.

Source code in src/zarr_metadata/model/_array.py
@classmethod
def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV2ArrayMetadata:
    """The model of `data`, a v2 array document with its attributes under `attributes`, read in `context`.

    `MetadataValidationError` with every problem the read finds.
    `read_array_metadata_v2` gives the reading this model is built
    from, and the problems of a document with some.
    """
    return cls(data, context=context)

from_key_value classmethod

from_key_value(
    mapping: Mapping[StoreKey, bytes],
    *,
    context: Context | None = None,
) -> ZarrV2ArrayMetadata

The model of the array at .zarray in mapping, with the attributes at .zattrs when there is one, read in context.

MetadataValidationError when .zarray is missing, bytes are not JSON, .zarray holds attributes, or the document is not valid.

Source code in src/zarr_metadata/model/_array.py
@classmethod
def from_key_value(
    cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
) -> ZarrV2ArrayMetadata:
    """The model of the array at `.zarray` in `mapping`, with the attributes at `.zattrs` when there is one, read in `context`.

    `MetadataValidationError` when `.zarray` is missing, bytes are not
    JSON, `.zarray` holds `attributes`, or the document is not valid.
    """
    zarray_raw = load_store_json(mapping, ZARR_V2_ARRAY_METADATA_STORE_KEY)
    if not is_object(zarray_raw):
        return cls(zarray_raw, context=context)
    zarray = zarray_raw
    if "attributes" in zarray:
        refused = ValidationProblem(
            ("attributes",), "unexpected document member", "invalid_value"
        )
        raise MetadataValidationError(with_input((refused,), zarray))
    if ZARR_V2_ATTRIBUTES_STORE_KEY in mapping:
        zattrs = load_store_json(mapping, ZARR_V2_ATTRIBUTES_STORE_KEY)
        return cls({**zarray, "attributes": zattrs}, context=context)
    return cls(zarray, context=context)

refined_in

refined_in(
    context: Context | None = None,
) -> ZarrV2ArrayMetadata

This document read in context, which may claim what this scope left unclaimed and contradict nothing.

ScopeConflictError naming each typestr or id context reads by another definition, or by none, where this scope read it by one, and where each sits in the document. MetadataValidationError when a definition context claims refuses what was written.

Source code in src/zarr_metadata/model/_array.py
def refined_in(self, context: Context | None = None) -> ZarrV2ArrayMetadata:
    """This document read in `context`, which may claim what this scope left unclaimed and contradict nothing.

    `ScopeConflictError` naming each typestr or id `context` reads by
    another definition, or by none, where this scope read it by one,
    and where each sits in the document. `MetadataValidationError`
    when a definition `context` claims refuses what was written.
    """
    scope = CORE_V2 if context is None else context
    found = scope.disagreements(self._claims)
    if len(found.conflicts) != 0:
        raise ScopeConflictError(located_conflicts(self._reading.fields(), found.conflicts))
    return self.with_context(scope)

refines

refines(other: ZarrV2ArrayMetadata) -> bool

Whether this model holds everything other holds: each field refines its counterpart, a null compressor or filters only a null, and every other member is the same, the fill value as the more informed dtype spells it; a fill value that dtype refuses is no refinement.

Source code in src/zarr_metadata/model/_array.py
def refines(self, other: ZarrV2ArrayMetadata) -> bool:
    """Whether this model holds everything `other` holds: each field refines its counterpart, a `null` compressor or filters only a `null`, and every other member is the same, the fill value as the more informed dtype spells it; a fill value that dtype refuses is no refinement."""
    if type(other) is not type(self):
        return False
    if (self.compressor is None) != (other.compressor is None):
        return False
    if (self.filters is None) != (other.filters is None):
        return False
    mine = () if self.filters is None else self.filters
    theirs = () if other.filters is None else other.filters
    if len(mine) != len(theirs):
        return False
    pairs = [(self.dtype, other.dtype), *zip(mine, theirs, strict=True)]
    if self.compressor is not None and other.compressor is not None:
        pairs.append((self.compressor, other.compressor))
    if not all(refines_field(one, another) for one, another in pairs):
        return False
    if len(fill_value_problems(self.dtype, other._members.fill_value)) != 0:
        return False
    return self._plain_key(self.dtype) == other._plain_key(self.dtype)

to_json

The merged document as written, refined, sharing nothing with the model.

attributes is included when set, even empty. This is not the on-disk .zarray, which excludes them: to_key_value splits the document as a store holds it (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L323-L330).

Source code in src/zarr_metadata/model/_array.py
def to_json(self) -> ZarrV2ArrayMetadataJSON:
    """The merged document as written, refined, sharing nothing with the model.

    `attributes` is included when set, even empty. This is not the
    on-disk `.zarray`, which excludes them: `to_key_value` splits the
    document as a store holds it
    (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L323-L330).
    """
    return cast("ZarrV2ArrayMetadataJSON", copied(self._document))

to_key_value

to_key_value(
    *, indent: int | str | None = None
) -> Mapping[
    ZarrV2ArrayMetadataStoreKey | ZarrV2AttributesStoreKey,
    bytes,
]

The document as a store holds it: .zarray without the attributes, and .zattrs with them when they are set, even empty.

Source code in src/zarr_metadata/model/_array.py
def to_key_value(
    self, *, indent: int | str | None = None
) -> Mapping[ZarrV2ArrayMetadataStoreKey | ZarrV2AttributesStoreKey, bytes]:
    """The document as a store holds it: `.zarray` without the attributes, and `.zattrs` with them when they are set, even empty."""
    zarray = {key: value for key, value in self._document.items() if key != "attributes"}
    out: dict[ZarrV2ArrayMetadataStoreKey | ZarrV2AttributesStoreKey, bytes] = {
        ZARR_V2_ARRAY_METADATA_STORE_KEY: dump_store_json(zarray, indent=indent)
    }
    if "attributes" in self._document:
        out[ZARR_V2_ATTRIBUTES_STORE_KEY] = dump_store_json(
            self._document["attributes"], indent=indent
        )
    return out

update

update(
    **members: Unpack[ZarrV2ArrayMetadataUpdate],
) -> ZarrV2ArrayMetadata

This model with members, JSON, in place of the document's, UNSET leaving one out, read in this model's own scope.

MetadataValidationError when the document they make has a problem, so members that go together are passed together: a dtype with a fill value of it.

Source code in src/zarr_metadata/model/_array.py
def update(self, **members: Unpack[ZarrV2ArrayMetadataUpdate]) -> ZarrV2ArrayMetadata:
    """This model with `members`, JSON, in place of the document's, `UNSET` leaving one out, read in this model's own scope.

    `MetadataValidationError` when the document they make has a
    problem, so members that go together are passed together: a
    `dtype` with a fill value of it.
    """
    document: dict[str, object] = {**self._document, **members}
    for key, value in members.items():
        if value is UNSET:
            del document[key]
    return type(self)(document, context=self._context)

with_context

with_context(
    context: Context | None = None,
) -> ZarrV2ArrayMetadata

This document read in context, whatever that changes: a gain, a loss, a conflict.

MetadataValidationError when the document has a problem there. The reading is kept when context reads every claim identically.

Source code in src/zarr_metadata/model/_array.py
def with_context(self, context: Context | None = None) -> ZarrV2ArrayMetadata:
    """This document read in `context`, whatever that changes: a gain, a loss, a conflict.

    `MetadataValidationError` when the document has a problem there.
    The reading is kept when `context` reads every claim identically.
    """
    scope = CORE_V2 if context is None else context
    if scope.disagreements(self._claims).agrees:
        return self._of(self._document, scope, self._reading, self._members)
    return type(self)(self._document, context=scope)

ZarrV2ArrayMetadataReading dataclass

A v2 array document as a scope read it, whatever it holds: its dtype, compressor and filters, every problem, and the model when there is none.

A field the document does not hold is UNSET; a compressor or filters written as null is None.

Source code in src/zarr_metadata/model/_validation.py
@dataclass(frozen=True, slots=True)
class ZarrV2ArrayMetadataReading:
    """A v2 array document as a scope read it, whatever it holds: its dtype, compressor and filters, every problem, and the model when there is none.

    A field the document does not hold is `UNSET`; a `compressor` or
    `filters` written as `null` is None.
    """

    dtype: ResolvedField[ZarrV2DataTypeDefinition[Any]] | UNSET = UNSET
    """The dtype, as the scope read it."""
    compressor: ResolvedField[ZarrV2CodecDefinition[Any]] | UNSET | None = UNSET
    """The compressor, as the scope read it; None when written as `null`."""
    filters: tuple[ResolvedField[ZarrV2CodecDefinition[Any]], ...] | UNSET | None = UNSET
    """The filters, each as the scope read it; None when written as `null`."""
    problems: tuple[ValidationProblem, ...] = ()
    """Every reason the document is not a valid one."""
    metadata: ZarrV2ArrayMetadata | None = None
    """The document's model, holding these fields, when there is no problem; None otherwise."""

    def __reduce__(self) -> str | tuple[object, ...]:
        # As the v3 reading: through its model, when it holds one.
        if self.metadata is not None:
            return (reading_of, (self.metadata,))
        return object.__reduce__(self)

    def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
        """Each field the document holds, as the scope read it, where it sits: the dtype, a struct's record types after it, the compressor, each filter at its index."""
        if self.dtype is not UNSET:
            yield from fields_of(self.dtype, ("dtype",))
        if self.compressor is not UNSET and self.compressor is not None:
            yield from fields_of(self.compressor, ("compressor",))
        if self.filters is not UNSET and self.filters is not None:
            for index, entry in enumerate(self.filters):
                yield from fields_of(entry, ("filters", index))

compressor class-attribute instance-attribute

compressor: (
    ResolvedField[ZarrV2CodecDefinition[Any]] | UNSET | None
) = UNSET

The compressor, as the scope read it; None when written as null.

dtype class-attribute instance-attribute

The dtype, as the scope read it.

filters class-attribute instance-attribute

filters: (
    tuple[ResolvedField[ZarrV2CodecDefinition[Any]], ...]
    | UNSET
    | None
) = UNSET

The filters, each as the scope read it; None when written as null.

metadata class-attribute instance-attribute

metadata: ZarrV2ArrayMetadata | None = None

The document's model, holding these fields, when there is no problem; None otherwise.

problems class-attribute instance-attribute

problems: tuple[ValidationProblem, ...] = ()

Every reason the document is not a valid one.

__init__

__init__(
    dtype: ResolvedField[ZarrV2DataTypeDefinition[Any]]
    | UNSET = UNSET,
    compressor: ResolvedField[ZarrV2CodecDefinition[Any]]
    | UNSET
    | None = UNSET,
    filters: tuple[
        ResolvedField[ZarrV2CodecDefinition[Any]], ...
    ]
    | UNSET
    | None = UNSET,
    problems: tuple[ValidationProblem, ...] = (),
    metadata: ZarrV2ArrayMetadata | None = None,
) -> None

__reduce__

__reduce__() -> str | tuple[object, ...]
Source code in src/zarr_metadata/model/_validation.py
def __reduce__(self) -> str | tuple[object, ...]:
    # As the v3 reading: through its model, when it holds one.
    if self.metadata is not None:
        return (reading_of, (self.metadata,))
    return object.__reduce__(self)

fields

Each field the document holds, as the scope read it, where it sits: the dtype, a struct's record types after it, the compressor, each filter at its index.

Source code in src/zarr_metadata/model/_validation.py
def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
    """Each field the document holds, as the scope read it, where it sits: the dtype, a struct's record types after it, the compressor, each filter at its index."""
    if self.dtype is not UNSET:
        yield from fields_of(self.dtype, ("dtype",))
    if self.compressor is not UNSET and self.compressor is not None:
        yield from fields_of(self.compressor, ("compressor",))
    if self.filters is not UNSET and self.filters is not None:
        for index, entry in enumerate(self.filters):
            yield from fields_of(entry, ("filters", index))

ZarrV2ArrayMetadataUpdate

Bases: TypedDict

The members ZarrV2ArrayMetadata.update puts in place: each as a document writes it, or UNSET to leave out one a document may leave out.

Those are attributes (no .zattrs), dimension_separator (read as "."), and a member the spec does not define.

Source code in src/zarr_metadata/model/_array.py
class ZarrV2ArrayMetadataUpdate(TypedDict, total=False, extra_items=JSONValue | UNSET):
    """The members `ZarrV2ArrayMetadata.update` puts in place: each as a document writes it, or `UNSET` to leave out one a document may leave out.

    Those are `attributes` (no `.zattrs`), `dimension_separator` (read as
    `"."`), and a member the spec does not define.
    """

    shape: tuple[int, ...]
    dtype: ZarrV2DataTypeMetadata
    chunks: tuple[int, ...]
    fill_value: JSONValue
    order: ZarrV2ArrayOrder
    compressor: ZarrV2CodecMetadata | None
    filters: tuple[ZarrV2CodecMetadata, ...] | None
    dimension_separator: ZarrV2ArrayDimensionSeparator | UNSET
    attributes: Mapping[str, JSONValue] | UNSET

attributes instance-attribute

attributes: Mapping[str, JSONValue] | UNSET

chunks instance-attribute

chunks: tuple[int, ...]

compressor instance-attribute

compressor: ZarrV2CodecMetadata | None

dimension_separator instance-attribute

dimension_separator: ZarrV2ArrayDimensionSeparator | UNSET

dtype instance-attribute

fill_value instance-attribute

fill_value: JSONValue

filters instance-attribute

filters: tuple[ZarrV2CodecMetadata, ...] | None

order instance-attribute

shape instance-attribute

shape: tuple[int, ...]

ZarrV2ConsolidatedMetadata

Bases: Keyed

A v2 .zmetadata document, and the scope its nodes were read in.

metadata holds the flat file-keyed entries ("path/.zarray", "path/.zattrs", ...) as written, refined: which nodes had a .zattrs at all is kept. nodes is each .zarray or .zgroup entry, merged with its sibling .zattrs, as a model of this scope, keyed by the node's path, "" for the root; a .zattrs with no sibling is kept and makes no node, and any other entry is JSON, kept. Built only by reading: the constructor raises MetadataValidationError with every problem, each located under its entry. Two documents are equal when each node means the same and the other entries are written alike, as its key says; refines, with_context and refined_in go through the nodes.

Source code in src/zarr_metadata/model/_group.py
class ZarrV2ConsolidatedMetadata(Keyed):
    """A v2 `.zmetadata` document, and the scope its nodes were read in.

    `metadata` holds the flat file-keyed entries (`"path/.zarray"`,
    `"path/.zattrs"`, ...) as written, refined: which nodes had a
    `.zattrs` at all is kept. `nodes` is each `.zarray` or `.zgroup`
    entry, merged with its sibling `.zattrs`, as a model of this scope,
    keyed by the node's path, `""` for the root; a `.zattrs` with no
    sibling is kept and makes no node, and any other entry is JSON, kept.
    Built only by reading: the constructor raises `MetadataValidationError`
    with every problem, each located under its entry. Two documents are
    equal when each node means the same and the other entries are written
    alike, as its key says; `refines`, `with_context` and
    `refined_in` go through the nodes.
    """

    __slots__ = ("_context", "_document", "_nodes", "_shown")

    _context: Context
    _document: dict[str, JSONValue]
    _nodes: dict[str, ZarrV2NodeMetadata]
    _shown: Mapping[str, JSONValue]

    zarr_consolidated_format: Final = 1

    def __init__(self, document: object, context: Context | None = None) -> None:
        scope = CORE_V2 if context is None else context
        entries, nodes, problems = _read_consolidated_v2(document, scope)
        if len(problems) != 0:
            raise MetadataValidationError(problems)
        self._adopt({"zarr_consolidated_format": 1, "metadata": entries}, scope, nodes)

    @classmethod
    def _of(
        cls, document: dict[str, JSONValue], context: Context, nodes: dict[str, ZarrV2NodeMetadata]
    ) -> ZarrV2ConsolidatedMetadata:
        """A model of a document a read found nothing wrong with, holding the nodes that read built. The readers of this package build models through this, the private use pyright reports."""
        model = object.__new__(cls)
        model._adopt(document, context, nodes)
        return model

    def _adopt(
        self, document: dict[str, JSONValue], context: Context, nodes: dict[str, ZarrV2NodeMetadata]
    ) -> None:
        self._document = document
        self._context = context
        self._nodes = nodes
        self._shown = frozen(object_at(document, "metadata"))
        self._key = self._key_of()

    @property
    def context(self) -> Context:
        """The scope the nodes were read in."""
        return self._context

    @property
    def metadata(self) -> Mapping[str, JSONValue]:
        """The entries as written, refined, by store key; read-only at every level."""
        return self._shown

    @property
    def nodes(self) -> Mapping[str, ZarrV2NodeMetadata]:
        """The model of each node, by its path below the root, `""` for the root: a read-only view."""
        return MappingProxyType(self._nodes)

    def to_json(self) -> dict[str, JSONValue]:
        """The `.zmetadata` document as written, refined, sharing nothing with the model."""
        return cast("dict[str, JSONValue]", copied(self._document))

    def to_key_value(
        self, *, indent: int | str | None = None
    ) -> Mapping[ZarrV2ConsolidatedMetadataStoreKey, bytes]:
        """The document as a store holds it: JSON bytes at `.zmetadata`, indented by `indent`."""
        return {
            ZARR_V2_CONSOLIDATED_METADATA_STORE_KEY: dump_store_json(self._document, indent=indent)
        }

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

    def _key_of(self) -> tuple[object, ...]:
        """What `==` and `hash` compare of v2 consolidated metadata: each node by its path and its own key, and every other entry as JSON text."""
        nodes = self._nodes
        return (
            tuple(sorted((path, node._key) for path, node in nodes.items())),
            self._other_entries_text(),
        )

    def _other_entries_text(self) -> str:
        """The entries no node is read from -- an orphan `.zattrs`, any other key -- as JSON text: what `==` compares of them."""
        entries = object_at(self._document, "metadata")
        consumed: set[str] = set()
        for path, names in _entries_by_path(entries)[0].items():
            if path in self._nodes:
                consumed.update(names.values())
        return json_text({key: value for key, value in entries.items() if key not in consumed})

    def __reduce__(self) -> tuple[type[ZarrV2ConsolidatedMetadata], tuple[object, Context]]:
        return type(self), (self._document, self._context)

    def with_context(self, context: Context | None = None) -> ZarrV2ConsolidatedMetadata:
        """This document with every node read in `context`, whatever that changes; `MetadataValidationError` when a node has a problem there."""
        scope = CORE_V2 if context is None else context
        return type(self)(self._document, context=scope)

    def refined_in(self, context: Context | None = None) -> ZarrV2ConsolidatedMetadata:
        """This document with every node read in `context`, which may claim what this scope left unclaimed and contradict nothing.

        `ScopeConflictError` naming each conflict, located at the node's
        entry; `MetadataValidationError` when a gain surfaces a problem.
        """
        scope = CORE_V2 if context is None else context
        conflicts: list[Conflict] = []
        entries = object_at(self._document, "metadata")
        by_path, _ = _entries_by_path(entries)
        for path, node in self._nodes.items():
            if not isinstance(node, ZarrV2ArrayMetadata):
                continue
            found = scope.disagreements(node.claims)
            key = by_path[path][ZARR_V2_ARRAY_METADATA_STORE_KEY]
            conflicts.extend(
                dataclasses.replace(
                    conflict,
                    loc=("metadata", key, *(() if conflict.loc is None else conflict.loc)),
                )
                for conflict in located_conflicts(node.reading.fields(), found.conflicts)
            )
        if len(conflicts) != 0:
            raise ScopeConflictError(conflicts)
        return self.with_context(scope)

    def refines(self, other: ZarrV2ConsolidatedMetadata) -> bool:
        """Whether every node this holds refines the one `other` holds at the same path, neither holds a path the other does not, and the other entries are written alike; False of what is not v2 consolidated metadata."""
        if type(other) is not type(self):
            return False
        if self._nodes.keys() != other._nodes.keys():
            return False
        if self._other_entries_text() != other._other_entries_text():
            return False
        return all(_v2_node_refines(self._nodes[path], other._nodes[path]) for path in self._nodes)

    @classmethod
    def from_json(
        cls, data: object, *, context: Context | None = None
    ) -> ZarrV2ConsolidatedMetadata:
        """The model of `data`, a `.zmetadata` document, its nodes read in `context`; `MetadataValidationError` with every problem."""
        return cls(data, context=context)

    @classmethod
    def from_key_value(
        cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
    ) -> ZarrV2ConsolidatedMetadata:
        """The model of the document at `.zmetadata` in `mapping`, read in `context`.

        `MetadataValidationError` when the key is missing, its bytes are not
        JSON, or the document is not valid.
        """
        return cls(
            load_store_json(mapping, ZARR_V2_CONSOLIDATED_METADATA_STORE_KEY), context=context
        )

__slots__ class-attribute instance-attribute

__slots__ = ('_context', '_document', '_nodes', '_shown')

context property

context: Context

The scope the nodes were read in.

metadata property

metadata: Mapping[str, JSONValue]

The entries as written, refined, by store key; read-only at every level.

nodes property

The model of each node, by its path below the root, "" for the root: a read-only view.

zarr_consolidated_format class-attribute instance-attribute

zarr_consolidated_format: Final = 1

__eq__

__eq__(other: object) -> bool
Source code in src/zarr_metadata/model/_keyed.py
def __eq__(self, other: object) -> bool:
    if type(other) is not type(self):
        return NotImplemented
    return self._key == other._key

__hash__

__hash__() -> int
Source code in src/zarr_metadata/model/_keyed.py
def __hash__(self) -> int:
    return hash(self._key)

__init__

__init__(
    document: object, context: Context | None = None
) -> None
Source code in src/zarr_metadata/model/_group.py
def __init__(self, document: object, context: Context | None = None) -> None:
    scope = CORE_V2 if context is None else context
    entries, nodes, problems = _read_consolidated_v2(document, scope)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    self._adopt({"zarr_consolidated_format": 1, "metadata": entries}, scope, nodes)

__reduce__

Source code in src/zarr_metadata/model/_group.py
def __reduce__(self) -> tuple[type[ZarrV2ConsolidatedMetadata], tuple[object, Context]]:
    return type(self), (self._document, self._context)

__repr__

__repr__() -> str
Source code in src/zarr_metadata/model/_group.py
def __repr__(self) -> str:
    return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

from_json classmethod

from_json(
    data: object, *, context: Context | None = None
) -> ZarrV2ConsolidatedMetadata

The model of data, a .zmetadata document, its nodes read in context; MetadataValidationError with every problem.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def from_json(
    cls, data: object, *, context: Context | None = None
) -> ZarrV2ConsolidatedMetadata:
    """The model of `data`, a `.zmetadata` document, its nodes read in `context`; `MetadataValidationError` with every problem."""
    return cls(data, context=context)

from_key_value classmethod

from_key_value(
    mapping: Mapping[StoreKey, bytes],
    *,
    context: Context | None = None,
) -> ZarrV2ConsolidatedMetadata

The model of the document at .zmetadata in mapping, read in context.

MetadataValidationError when the key is missing, its bytes are not JSON, or the document is not valid.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def from_key_value(
    cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
) -> ZarrV2ConsolidatedMetadata:
    """The model of the document at `.zmetadata` in `mapping`, read in `context`.

    `MetadataValidationError` when the key is missing, its bytes are not
    JSON, or the document is not valid.
    """
    return cls(
        load_store_json(mapping, ZARR_V2_CONSOLIDATED_METADATA_STORE_KEY), context=context
    )

refined_in

refined_in(
    context: Context | None = None,
) -> ZarrV2ConsolidatedMetadata

This document with every node read in context, which may claim what this scope left unclaimed and contradict nothing.

ScopeConflictError naming each conflict, located at the node's entry; MetadataValidationError when a gain surfaces a problem.

Source code in src/zarr_metadata/model/_group.py
def refined_in(self, context: Context | None = None) -> ZarrV2ConsolidatedMetadata:
    """This document with every node read in `context`, which may claim what this scope left unclaimed and contradict nothing.

    `ScopeConflictError` naming each conflict, located at the node's
    entry; `MetadataValidationError` when a gain surfaces a problem.
    """
    scope = CORE_V2 if context is None else context
    conflicts: list[Conflict] = []
    entries = object_at(self._document, "metadata")
    by_path, _ = _entries_by_path(entries)
    for path, node in self._nodes.items():
        if not isinstance(node, ZarrV2ArrayMetadata):
            continue
        found = scope.disagreements(node.claims)
        key = by_path[path][ZARR_V2_ARRAY_METADATA_STORE_KEY]
        conflicts.extend(
            dataclasses.replace(
                conflict,
                loc=("metadata", key, *(() if conflict.loc is None else conflict.loc)),
            )
            for conflict in located_conflicts(node.reading.fields(), found.conflicts)
        )
    if len(conflicts) != 0:
        raise ScopeConflictError(conflicts)
    return self.with_context(scope)

refines

refines(other: ZarrV2ConsolidatedMetadata) -> bool

Whether every node this holds refines the one other holds at the same path, neither holds a path the other does not, and the other entries are written alike; False of what is not v2 consolidated metadata.

Source code in src/zarr_metadata/model/_group.py
def refines(self, other: ZarrV2ConsolidatedMetadata) -> bool:
    """Whether every node this holds refines the one `other` holds at the same path, neither holds a path the other does not, and the other entries are written alike; False of what is not v2 consolidated metadata."""
    if type(other) is not type(self):
        return False
    if self._nodes.keys() != other._nodes.keys():
        return False
    if self._other_entries_text() != other._other_entries_text():
        return False
    return all(_v2_node_refines(self._nodes[path], other._nodes[path]) for path in self._nodes)

to_json

to_json() -> dict[str, JSONValue]

The .zmetadata document as written, refined, sharing nothing with the model.

Source code in src/zarr_metadata/model/_group.py
def to_json(self) -> dict[str, JSONValue]:
    """The `.zmetadata` document as written, refined, sharing nothing with the model."""
    return cast("dict[str, JSONValue]", copied(self._document))

to_key_value

to_key_value(
    *, indent: int | str | None = None
) -> Mapping[ZarrV2ConsolidatedMetadataStoreKey, bytes]

The document as a store holds it: JSON bytes at .zmetadata, indented by indent.

Source code in src/zarr_metadata/model/_group.py
def to_key_value(
    self, *, indent: int | str | None = None
) -> Mapping[ZarrV2ConsolidatedMetadataStoreKey, bytes]:
    """The document as a store holds it: JSON bytes at `.zmetadata`, indented by `indent`."""
    return {
        ZARR_V2_CONSOLIDATED_METADATA_STORE_KEY: dump_store_json(self._document, indent=indent)
    }

with_context

with_context(
    context: Context | None = None,
) -> ZarrV2ConsolidatedMetadata

This document with every node read in context, whatever that changes; MetadataValidationError when a node has a problem there.

Source code in src/zarr_metadata/model/_group.py
def with_context(self, context: Context | None = None) -> ZarrV2ConsolidatedMetadata:
    """This document with every node read in `context`, whatever that changes; `MetadataValidationError` when a node has a problem there."""
    scope = CORE_V2 if context is None else context
    return type(self)(self._document, context=scope)

ZarrV2GroupMetadata

Bases: Keyed

A v2 group document, and the scope it was read in.

The pair, as the v3 models are: to_json is the merged document -- .zgroup, and attributes when a .zattrs holds them -- as written, refined. attributes is UNSET when no .zattrs exists, distinct from an empty one. A group holds no field a scope reads, so the scope is held for uniformity: update reads new attributes in it, and no other scope conflicts with the reading. Built only by reading: the constructor raises MetadataValidationError with every problem. Two groups are equal when their attributes are written alike, as group_key_v2 says.

Source code in src/zarr_metadata/model/_group.py
class ZarrV2GroupMetadata(Keyed):
    """A v2 group document, and the scope it was read in.

    The pair, as the v3 models are: `to_json` is the merged document --
    `.zgroup`, and `attributes` when a `.zattrs` holds them -- as written,
    refined. `attributes` is `UNSET` when no `.zattrs` exists, distinct
    from an empty one. A group holds no field a scope reads, so the scope
    is held for uniformity: `update` reads new attributes in it, and no
    other scope conflicts with the reading. Built only by reading: the
    constructor raises `MetadataValidationError` with every problem. Two
    groups are equal when their attributes are written alike, as
    `group_key_v2` says.
    """

    __slots__ = ("_attributes", "_context", "_document", "_shown")

    _attributes: dict[str, JSONValue] | UNSET
    _context: Context
    _document: dict[str, JSONValue]
    _shown: Mapping[str, JSONValue] | UNSET

    zarr_format: Final = 2

    @property
    def claims(self) -> Claims:
        """What the reading claimed: nothing, since a group holds no field."""
        return MappingProxyType({})

    def __init__(self, document: object, context: Context | None = None) -> None:
        scope = CORE_V2 if context is None else context
        parsed = parse_group_metadata_v2(document, context=scope)
        self._adopt(refined_object(document), scope, _v2_attributes(parsed))

    @classmethod
    def _of(
        cls,
        document: dict[str, JSONValue],
        context: Context,
        attributes: dict[str, JSONValue] | UNSET,
    ) -> ZarrV2GroupMetadata:
        """A model of a document a read found nothing wrong with: no second read. The readers of this package build models through this, the private use pyright reports."""
        model = object.__new__(cls)
        model._adopt(document, context, attributes)
        return model

    def _adopt(
        self,
        document: dict[str, JSONValue],
        context: Context,
        attributes: dict[str, JSONValue] | UNSET,
    ) -> None:
        self._document = document
        self._context = context
        self._attributes = attributes
        self._shown = UNSET if attributes is UNSET else frozen(attributes)
        self._key = self._key_of()

    @property
    def context(self) -> Context:
        """The scope the document was read in, which `update` reads new attributes in."""
        return self._context

    @property
    def attributes(self) -> Mapping[str, JSONValue] | UNSET:
        """The user attributes a `.zattrs` holds, read-only at every level; `UNSET` when there is no `.zattrs`."""
        return self._shown

    def to_json(self) -> ZarrV2GroupMetadataJSON:
        """The merged document as written, refined, sharing nothing with the model.

        `attributes` is included when set, even empty. This is not the
        on-disk `.zgroup`, which excludes them: `to_key_value` splits the
        document as a store holds it
        (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L313; https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L323-L330).
        """
        return cast("ZarrV2GroupMetadataJSON", copied(self._document))

    def to_key_value(
        self, *, indent: int | str | None = None
    ) -> Mapping[ZarrV2GroupMetadataStoreKey | ZarrV2AttributesStoreKey, bytes]:
        """The document as a store holds it: `.zgroup` without the attributes, and `.zattrs` with them when they are set, even empty."""
        zgroup = {key: value for key, value in self._document.items() if key != "attributes"}
        out: dict[ZarrV2GroupMetadataStoreKey | ZarrV2AttributesStoreKey, bytes] = {
            ZARR_V2_GROUP_METADATA_STORE_KEY: dump_store_json(zgroup, indent=indent)
        }
        if "attributes" in self._document:
            out[ZARR_V2_ATTRIBUTES_STORE_KEY] = dump_store_json(
                self._document["attributes"], indent=indent
            )
        return out

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

    def _key_of(self) -> tuple[object, ...]:
        """What `==` and `hash` compare of a v2 group model: its attributes as JSON text, or `UNSET` when there is no `.zattrs`."""
        attributes = self._attributes
        return (UNSET if attributes is UNSET else json_text(attributes),)

    def __reduce__(self) -> tuple[type[ZarrV2GroupMetadata], tuple[object, Context]]:
        return type(self), (self._document, self._context)

    def update(self, **members: Unpack[ZarrV2GroupMetadataUpdate]) -> ZarrV2GroupMetadata:
        """This model with `attributes` in place of the document's, `UNSET` leaving them out, read in this model's own scope; `MetadataValidationError` when the document they make has a problem."""
        document: dict[str, object] = {**self._document, **members}
        for key, value in members.items():
            if value is UNSET:
                del document[key]
        return type(self)(document, context=self._context)

    def with_context(self, context: Context | None = None) -> ZarrV2GroupMetadata:
        """This document read in `context`: the same group, holding that scope."""
        scope = CORE_V2 if context is None else context
        return self._of(self._document, scope, self._attributes)

    def refined_in(self, context: Context | None = None) -> ZarrV2GroupMetadata:
        """This document read in `context`: a group holds no field, so no scope conflicts with its reading, and this is `with_context`."""
        return self.with_context(context)

    def refines(self, other: ZarrV2GroupMetadata) -> bool:
        """Whether this group holds everything `other` holds: its attributes written alike; False of what is not a v2 group."""
        return type(other) is type(self) and self._key == other._key

    @classmethod
    def create_default(
        cls, *, context: Context | None = None, **overrides: Unpack[ZarrV2GroupMetadataUpdate]
    ) -> ZarrV2GroupMetadata:
        """A group with no `.zattrs`, or the one `overrides` make of it, read in `context`; `MetadataValidationError` when the document they make has a problem."""
        given = {key: value for key, value in overrides.items() if value is not UNSET}
        return cls({"zarr_format": 2, **given}, context=context)

    @classmethod
    def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV2GroupMetadata:
        """The model of `data`, a v2 group document with its attributes under `attributes`, read in `context`; `MetadataValidationError` with every problem."""
        return cls(data, context=context)

    @classmethod
    def from_key_value(
        cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
    ) -> ZarrV2GroupMetadata:
        """The model of the group at `.zgroup` in `mapping`, with the attributes at `.zattrs` when there is one, read in `context`.

        `MetadataValidationError` when `.zgroup` is missing, bytes are not
        JSON, `.zgroup` holds `attributes`, or the document is not valid.
        """
        zgroup_raw = load_store_json(mapping, ZARR_V2_GROUP_METADATA_STORE_KEY)
        if not is_object(zgroup_raw):
            return cls(zgroup_raw, context=context)
        zgroup = zgroup_raw
        if "attributes" in zgroup:
            # A key `.zgroup` does not declare: its attributes are `.zattrs`.
            refused = ValidationProblem(
                ("attributes",), "unexpected key 'attributes'", "unknown_key"
            )
            raise MetadataValidationError(with_input((refused,), zgroup))
        if ZARR_V2_ATTRIBUTES_STORE_KEY in mapping:
            zattrs = load_store_json(mapping, ZARR_V2_ATTRIBUTES_STORE_KEY)
            return cls({**zgroup, "attributes": zattrs}, context=context)
        return cls(zgroup, context=context)

__slots__ class-attribute instance-attribute

__slots__ = (
    "_attributes",
    "_context",
    "_document",
    "_shown",
)

attributes property

attributes: Mapping[str, JSONValue] | UNSET

The user attributes a .zattrs holds, read-only at every level; UNSET when there is no .zattrs.

claims property

claims: Claims

What the reading claimed: nothing, since a group holds no field.

context property

context: Context

The scope the document was read in, which update reads new attributes in.

zarr_format class-attribute instance-attribute

zarr_format: Final = 2

__eq__

__eq__(other: object) -> bool
Source code in src/zarr_metadata/model/_keyed.py
def __eq__(self, other: object) -> bool:
    if type(other) is not type(self):
        return NotImplemented
    return self._key == other._key

__hash__

__hash__() -> int
Source code in src/zarr_metadata/model/_keyed.py
def __hash__(self) -> int:
    return hash(self._key)

__init__

__init__(
    document: object, context: Context | None = None
) -> None
Source code in src/zarr_metadata/model/_group.py
def __init__(self, document: object, context: Context | None = None) -> None:
    scope = CORE_V2 if context is None else context
    parsed = parse_group_metadata_v2(document, context=scope)
    self._adopt(refined_object(document), scope, _v2_attributes(parsed))

__reduce__

Source code in src/zarr_metadata/model/_group.py
def __reduce__(self) -> tuple[type[ZarrV2GroupMetadata], tuple[object, Context]]:
    return type(self), (self._document, self._context)

__repr__

__repr__() -> str
Source code in src/zarr_metadata/model/_group.py
def __repr__(self) -> str:
    return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

create_default classmethod

create_default(
    *,
    context: Context | None = None,
    **overrides: Unpack[ZarrV2GroupMetadataUpdate],
) -> ZarrV2GroupMetadata

A group with no .zattrs, or the one overrides make of it, read in context; MetadataValidationError when the document they make has a problem.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def create_default(
    cls, *, context: Context | None = None, **overrides: Unpack[ZarrV2GroupMetadataUpdate]
) -> ZarrV2GroupMetadata:
    """A group with no `.zattrs`, or the one `overrides` make of it, read in `context`; `MetadataValidationError` when the document they make has a problem."""
    given = {key: value for key, value in overrides.items() if value is not UNSET}
    return cls({"zarr_format": 2, **given}, context=context)

from_json classmethod

from_json(
    data: object, *, context: Context | None = None
) -> ZarrV2GroupMetadata

The model of data, a v2 group document with its attributes under attributes, read in context; MetadataValidationError with every problem.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV2GroupMetadata:
    """The model of `data`, a v2 group document with its attributes under `attributes`, read in `context`; `MetadataValidationError` with every problem."""
    return cls(data, context=context)

from_key_value classmethod

from_key_value(
    mapping: Mapping[StoreKey, bytes],
    *,
    context: Context | None = None,
) -> ZarrV2GroupMetadata

The model of the group at .zgroup in mapping, with the attributes at .zattrs when there is one, read in context.

MetadataValidationError when .zgroup is missing, bytes are not JSON, .zgroup holds attributes, or the document is not valid.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def from_key_value(
    cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
) -> ZarrV2GroupMetadata:
    """The model of the group at `.zgroup` in `mapping`, with the attributes at `.zattrs` when there is one, read in `context`.

    `MetadataValidationError` when `.zgroup` is missing, bytes are not
    JSON, `.zgroup` holds `attributes`, or the document is not valid.
    """
    zgroup_raw = load_store_json(mapping, ZARR_V2_GROUP_METADATA_STORE_KEY)
    if not is_object(zgroup_raw):
        return cls(zgroup_raw, context=context)
    zgroup = zgroup_raw
    if "attributes" in zgroup:
        # A key `.zgroup` does not declare: its attributes are `.zattrs`.
        refused = ValidationProblem(
            ("attributes",), "unexpected key 'attributes'", "unknown_key"
        )
        raise MetadataValidationError(with_input((refused,), zgroup))
    if ZARR_V2_ATTRIBUTES_STORE_KEY in mapping:
        zattrs = load_store_json(mapping, ZARR_V2_ATTRIBUTES_STORE_KEY)
        return cls({**zgroup, "attributes": zattrs}, context=context)
    return cls(zgroup, context=context)

refined_in

refined_in(
    context: Context | None = None,
) -> ZarrV2GroupMetadata

This document read in context: a group holds no field, so no scope conflicts with its reading, and this is with_context.

Source code in src/zarr_metadata/model/_group.py
def refined_in(self, context: Context | None = None) -> ZarrV2GroupMetadata:
    """This document read in `context`: a group holds no field, so no scope conflicts with its reading, and this is `with_context`."""
    return self.with_context(context)

refines

refines(other: ZarrV2GroupMetadata) -> bool

Whether this group holds everything other holds: its attributes written alike; False of what is not a v2 group.

Source code in src/zarr_metadata/model/_group.py
def refines(self, other: ZarrV2GroupMetadata) -> bool:
    """Whether this group holds everything `other` holds: its attributes written alike; False of what is not a v2 group."""
    return type(other) is type(self) and self._key == other._key

to_json

The merged document as written, refined, sharing nothing with the model.

attributes is included when set, even empty. This is not the on-disk .zgroup, which excludes them: to_key_value splits the document as a store holds it (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L313; https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L323-L330).

Source code in src/zarr_metadata/model/_group.py
def to_json(self) -> ZarrV2GroupMetadataJSON:
    """The merged document as written, refined, sharing nothing with the model.

    `attributes` is included when set, even empty. This is not the
    on-disk `.zgroup`, which excludes them: `to_key_value` splits the
    document as a store holds it
    (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L313; https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v2/v2.0.rst#L323-L330).
    """
    return cast("ZarrV2GroupMetadataJSON", copied(self._document))

to_key_value

to_key_value(
    *, indent: int | str | None = None
) -> Mapping[
    ZarrV2GroupMetadataStoreKey | ZarrV2AttributesStoreKey,
    bytes,
]

The document as a store holds it: .zgroup without the attributes, and .zattrs with them when they are set, even empty.

Source code in src/zarr_metadata/model/_group.py
def to_key_value(
    self, *, indent: int | str | None = None
) -> Mapping[ZarrV2GroupMetadataStoreKey | ZarrV2AttributesStoreKey, bytes]:
    """The document as a store holds it: `.zgroup` without the attributes, and `.zattrs` with them when they are set, even empty."""
    zgroup = {key: value for key, value in self._document.items() if key != "attributes"}
    out: dict[ZarrV2GroupMetadataStoreKey | ZarrV2AttributesStoreKey, bytes] = {
        ZARR_V2_GROUP_METADATA_STORE_KEY: dump_store_json(zgroup, indent=indent)
    }
    if "attributes" in self._document:
        out[ZARR_V2_ATTRIBUTES_STORE_KEY] = dump_store_json(
            self._document["attributes"], indent=indent
        )
    return out

update

update(
    **members: Unpack[ZarrV2GroupMetadataUpdate],
) -> ZarrV2GroupMetadata

This model with attributes in place of the document's, UNSET leaving them out, read in this model's own scope; MetadataValidationError when the document they make has a problem.

Source code in src/zarr_metadata/model/_group.py
def update(self, **members: Unpack[ZarrV2GroupMetadataUpdate]) -> ZarrV2GroupMetadata:
    """This model with `attributes` in place of the document's, `UNSET` leaving them out, read in this model's own scope; `MetadataValidationError` when the document they make has a problem."""
    document: dict[str, object] = {**self._document, **members}
    for key, value in members.items():
        if value is UNSET:
            del document[key]
    return type(self)(document, context=self._context)

with_context

with_context(
    context: Context | None = None,
) -> ZarrV2GroupMetadata

This document read in context: the same group, holding that scope.

Source code in src/zarr_metadata/model/_group.py
def with_context(self, context: Context | None = None) -> ZarrV2GroupMetadata:
    """This document read in `context`: the same group, holding that scope."""
    scope = CORE_V2 if context is None else context
    return self._of(self._document, scope, self._attributes)

ZarrV2GroupMetadataUpdate

Bases: TypedDict

The members ZarrV2GroupMetadata.update puts in place: attributes as a .zattrs writes them, or UNSET for no .zattrs.

Source code in src/zarr_metadata/model/_group.py
class ZarrV2GroupMetadataUpdate(TypedDict, total=False):
    """The members `ZarrV2GroupMetadata.update` puts in place: `attributes` as a `.zattrs` writes them, or `UNSET` for no `.zattrs`."""

    attributes: Mapping[str, JSONValue] | UNSET

attributes instance-attribute

attributes: Mapping[str, JSONValue] | UNSET

ZarrV2RepairedConsolidatedMetadataReading dataclass

A v2 .zmetadata read after its known writer bugs were undone: the repaired document's problems, its model when there are none, and the repairs.

Source code in src/zarr_metadata/model/_repair.py
@dataclass(frozen=True, slots=True)
class ZarrV2RepairedConsolidatedMetadataReading:
    """A v2 `.zmetadata` read after its known writer bugs were undone: the repaired document's problems, its model when there are none, and the repairs."""

    problems: tuple[ValidationProblem, ...]
    """Every problem of the repaired document."""
    metadata: ZarrV2ConsolidatedMetadata | None
    """The repaired document's model, when it has no problem; None otherwise."""
    repairs: tuple[Repair, ...]
    """What was changed to make the document that was read."""

metadata instance-attribute

metadata: ZarrV2ConsolidatedMetadata | None

The repaired document's model, when it has no problem; None otherwise.

problems instance-attribute

problems: tuple[ValidationProblem, ...]

Every problem of the repaired document.

repairs instance-attribute

repairs: tuple[Repair, ...]

What was changed to make the document that was read.

__init__

__init__(
    problems: tuple[ValidationProblem, ...],
    metadata: ZarrV2ConsolidatedMetadata | None,
    repairs: tuple[Repair, ...],
) -> None

ZarrV3ArrayMetadata

Bases: Keyed

A v3 array document, and the scope it was read in.

The model is the pair: to_json is the document as written, refined -- arrays as tuples, string keys -- and context the scope. Every typed member is a view of the reading the pair gives: data_type, chunk_grid, chunk_key_encoding, each codec and storage transformer as the scope read it, AcceptedField by the definition that claims its name or UnclaimedField; shape, fill_value, dimension_names, attributes and extra_fields as the read refined them. Built only by reading: the constructor reads document in context and raises MetadataValidationError with every problem, so no model is invalid. Two models are equal when their documents mean the same in their scopes, as its key says; the scope itself takes no part. update reads new members in the model's own scope; with_context and refined_in read the document in another. A model pickles as its pair, when the definitions its scope holds do: ones whose functions are defined at a module's top level.

Source code in src/zarr_metadata/model/_array.py
class ZarrV3ArrayMetadata(Keyed):
    """A v3 array document, and the scope it was read in.

    The model is the pair: `to_json` is the document as written, refined
    -- arrays as tuples, string keys -- and `context` the scope. Every
    typed member is a view of the reading the pair gives: `data_type`,
    `chunk_grid`, `chunk_key_encoding`, each codec and storage transformer
    as the scope read it, `AcceptedField` by the definition that claims its name or
    `UnclaimedField`; `shape`, `fill_value`, `dimension_names`, `attributes` and
    `extra_fields` as the read refined them. Built only by reading: the
    constructor reads `document` in `context` and raises
    `MetadataValidationError` with every problem, so no model is invalid.
    Two models are equal when their documents mean the same in their
    scopes, as its key says; the scope itself takes no part. `update`
    reads new members in the model's own scope; `with_context` and
    `refined_in` read the document in another. A model pickles as its
    pair, when the definitions its scope holds do: ones whose functions
    are defined at a module's top level.
    """

    __slots__ = ("_claims", "_context", "_document", "_members", "_reading", "_shown")

    zarr_format: Final = 3
    node_type: Final = "array"

    @property
    def claims(self) -> Claims:
        """What the reading claimed of each name the document writes, keyed as the scope files it."""
        return self._claims

    def __init__(self, document: object, context: Context | None = None) -> None:
        scope = CORE_AND_EXTENSIONS if context is None else context
        reading, members = read_array_v3(document, scope)
        if members is None:
            raise MetadataValidationError(reading.problems)
        self._adopt(refined_object(document), scope, reading, members)

    @classmethod
    def _of(
        cls,
        document: dict[str, JSONValue],
        context: Context,
        reading: ZarrV3ArrayMetadataReading,
        members: ArrayMembersV3,
    ) -> ZarrV3ArrayMetadata:
        """A model of a document a read found nothing wrong with, holding that reading: no second read. The readers of this package build models through this, the private use pyright reports."""
        model = object.__new__(cls)
        model._adopt(document, context, reading, members)
        return model

    def _adopt(
        self,
        document: dict[str, JSONValue],
        context: Context,
        reading: ZarrV3ArrayMetadataReading,
        members: ArrayMembersV3,
    ) -> None:
        self._document = document
        self._context = context
        # The reading holds the model it built, as `read_array_metadata_v3`
        # hands it back, however the model was built.
        self._reading = dataclasses.replace(reading, metadata=self)
        self._members = members
        # What the model shows of its members, read-only at every level.
        self._shown = (
            frozen(members.fill_value),
            frozen(members.attributes),
            frozen(members.extra_fields),
        )
        self._key = self._key_of()
        self._claims = MappingProxyType(claims_of(reading.fields()))

    # --- the pair ---------------------------------------------------------

    @property
    def context(self) -> Context:
        """The scope the document was read in, which `update` reads new members in."""
        return self._context

    @property
    def reading(self) -> ZarrV3ArrayMetadataReading:
        """The document as the scope read it: each field, the pipeline, the chunk each codec is handed."""
        return self._reading

    def to_json(self) -> ZarrV3ArrayMetadataJSON:
        """The document as written, refined, sharing nothing with the model."""
        return cast("ZarrV3ArrayMetadataJSON", copied(self._document))

    def to_key_value(
        self, *, indent: int | str | None = None
    ) -> Mapping[ZarrV3ArrayMetadataStoreKey, bytes]:
        """The document as a store holds it: JSON bytes at `zarr.json`, indented by `indent`.

        `NaN`, `Infinity` and `-Infinity` in `attributes` are written as
        those bare tokens, as zarr-python writes them, which a strict JSON
        parser refuses.
        """
        return {ZARR_V3_ARRAY_METADATA_STORE_KEY: dump_store_json(self._document, indent=indent)}

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

    def _key_of(self) -> tuple[object, ...]:
        """What `==` and `hash` compare of a v3 array model: what its document means.

        Each field by its `field_key`, the fill value in its canonical spelling
        as JSON text when a definition in scope read the data type, and every
        other member as it is, the JSON ones as text.
        """
        members = self._members
        return (
            members.shape,
            self._fill_value_key(),
            field_key(self.data_type),
            field_key(self.chunk_grid),
            tuple(field_key(codec) for codec in self.codecs),
            field_key(self.chunk_key_encoding),
            members.dimension_names,
            json_text(members.attributes),
            tuple(field_key(transformer) for transformer in self.storage_transformers),
            json_text(members.extra_fields),
        )

    def _plain_key(
        self, data_type: AcceptedField[DataTypeDefinition[Any]] | UnclaimedField
    ) -> tuple[object, ...]:
        """What `refines` compares of a model other than its fields, the fill value spelled as `data_type` -- the more informed side's -- spells it."""
        members = self._members
        return (
            members.shape,
            json_text(spelled_canonically(data_type, members.fill_value)),
            members.dimension_names,
            json_text(members.attributes),
            json_text(members.extra_fields),
        )

    def _fill_value_key(self) -> str:
        """What `==` compares of the fill value: its canonical spelling as JSON text when a definition in scope read the data type, and the fill value as written when none did."""
        fill_value = self._members.fill_value
        if isinstance(self.data_type, AcceptedField):
            return json_text(spelled_canonically(self.data_type, fill_value))
        return json_text(fill_value)

    def __reduce__(self) -> tuple[type[ZarrV3ArrayMetadata], tuple[object, Context]]:
        # The pair, read again on load: a model's reading never disagrees
        # with its document.
        return type(self), (self._document, self._context)

    # --- typed views ------------------------------------------------------

    @property
    def shape(self) -> tuple[int, ...]:
        """The array's shape."""
        return self._members.shape

    @property
    def fill_value(self) -> JSONValue:
        """The fill value as written, read-only at every level."""
        return self._shown[0]

    @property
    def dimension_names(self) -> tuple[str | None, ...] | UNSET:
        """The dimension names; `UNSET` when the document writes none."""
        return self._members.dimension_names

    @property
    def attributes(self) -> Mapping[str, JSONValue]:
        """The attributes, read-only at every level; empty when the document writes none."""
        return self._shown[1]

    @property
    def extra_fields(self) -> Mapping[str, ZarrV3ExtensionField]:
        """Each member the spec does not define, by name, read-only at every level."""
        return self._shown[2]

    @property
    def must_understand_fields(self) -> dict[str, ZarrV3ExtensionField]:
        """Extra fields the reader is obligated to understand.

        Everything in `extra_fields` not explicitly waived with
        `must_understand: false` (the spec's implicit-true rule, https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/core/index.rst#L1571-L1578). A compliant
        reader MUST fail to open the array if this contains any field it does
        not recognize; the model layer only partitions by obligation, since
        recognition is reader-specific.
        """
        return must_understand_subset(self.extra_fields)

    @property
    def data_type(self) -> AcceptedField[DataTypeDefinition[Any]] | UnclaimedField:
        """The data type, as the scope read it."""
        return held(self._reading.data_type)

    @property
    def chunk_grid(self) -> AcceptedField[ChunkGridDefinition[Any]] | UnclaimedField:
        """The chunk grid, as the scope read it."""
        return held(self._reading.chunk_grid)

    @property
    def chunk_key_encoding(self) -> AcceptedField[ChunkKeyEncodingDefinition[Any]] | UnclaimedField:
        """The chunk key encoding, as the scope read it."""
        return held(self._reading.chunk_key_encoding)

    @property
    def codecs(self) -> tuple[AcceptedField[CodecDefinition[Any]] | UnclaimedField, ...]:
        """The codecs, each as the scope read it, in pipeline order."""
        return tuple(held(stage.codec) for stage in self._reading.pipeline)

    @property
    def storage_transformers(
        self,
    ) -> tuple[AcceptedField[StorageTransformerDefinition[Any]] | UnclaimedField, ...]:
        """The storage transformers, each as the scope read it."""
        return tuple(held(entry) for entry in self._reading.storage_transformers)

    # --- changing ---------------------------------------------------------

    def update(self, **members: Unpack[ZarrV3ArrayMetadataUpdate]) -> ZarrV3ArrayMetadata:
        """This model with `members`, JSON, in place of the document's, `UNSET` leaving one out, read in this model's own scope.

        `MetadataValidationError` when the document they make has a
        problem, so members that go together are passed together: a
        `shape` with a grid that fits it.
        """
        document: dict[str, object] = {**self._document, **members}
        for key, value in members.items():
            if value is UNSET:
                del document[key]
        return type(self)(document, context=self._context)

    def with_context(self, context: Context | None = None) -> ZarrV3ArrayMetadata:
        """This document read in `context`, whatever that changes: a gain, a loss, a conflict.

        `MetadataValidationError` when the document has a problem there.
        The reading is kept when `context` reads every claim identically.
        """
        scope = CORE_AND_EXTENSIONS if context is None else context
        if scope.disagreements(self._claims).agrees:
            return self._of(self._document, scope, self._reading, self._members)
        return type(self)(self._document, context=scope)

    def refined_in(self, context: Context | None = None) -> ZarrV3ArrayMetadata:
        """This document read in `context`, which may claim what this scope left unclaimed and contradict nothing.

        `ScopeConflictError` naming each name `context` reads by another
        definition, or by none, where this scope read it by one -- a loss
        of meaning is refused as a conflict is -- and where each sits in
        the document. `MetadataValidationError` when a name `context`
        claims refuses what was written under it: a gain can surface a
        problem. `with_context` reads the document in any scope.
        """
        scope = CORE_AND_EXTENSIONS if context is None else context
        found = scope.disagreements(self._claims)
        if len(found.conflicts) != 0:
            raise ScopeConflictError(located_conflicts(self._reading.fields(), found.conflicts))
        return self.with_context(scope)

    def refines(self, other: ZarrV3ArrayMetadata) -> bool:
        """Whether this model holds everything `other` holds: each field refines its counterpart, as `refines` orders fields -- the fields a field holds with it -- and every other member is the same, the fill value as the more informed data type spells it; a fill value that data type refuses is no refinement."""
        if type(other) is not type(self):
            return False
        if len(self.codecs) != len(other.codecs) or len(self.storage_transformers) != len(
            other.storage_transformers
        ):
            return False
        pairs = (
            (self.data_type, other.data_type),
            (self.chunk_grid, other.chunk_grid),
            (self.chunk_key_encoding, other.chunk_key_encoding),
            *zip(self.codecs, other.codecs, strict=True),
            *zip(self.storage_transformers, other.storage_transformers, strict=True),
        )
        if not all(refines_field(mine, theirs) for mine, theirs in pairs):
            return False
        if len(fill_value_problems(self.data_type, other._members.fill_value)) != 0:
            return False
        return self._plain_key(self.data_type) == other._plain_key(self.data_type)

    # --- constructors -----------------------------------------------------

    @classmethod
    def create_default(
        cls,
        *,
        context: Context | None = None,
        **overrides: Unpack[ZarrV3ArrayMetadataJSONPartial],
    ) -> ZarrV3ArrayMetadata:
        """A scalar `uint8` array, or the one `overrides`, members of its document, make of it, read in `context`.

        `MetadataValidationError` when the document they make has a
        problem, so members that go together are passed together: a data
        type with a fill value of it, a grid with the shape it fits. The
        default codec is `bytes` with a little `endian`, which takes a data
        type of any fixed size. Overriding `shape` without `chunk_grid`
        derives a consistent default grid: one regular chunk covering the
        array (`chunk_shape` equal to `shape`, with a length of 1 for a
        dimension of length 0, since "Chunk sizes must be greater than
        zero",
        https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/chunk-grids/regular-grid/index.rst#L40).
        """
        # The grid derives from a shape the read takes; one it refuses is
        # reported by the read, and derives nothing.
        lengths, _ = dimension_lengths(overrides, "shape")
        document: dict[str, object] = {
            "zarr_format": 3,
            "node_type": "array",
            "shape": (),
            "fill_value": 0,
            "data_type": "uint8",
            "chunk_grid": {
                "name": "regular",
                "configuration": {"chunk_shape": tuple(max(length, 1) for length in lengths or ())},
            },
            "codecs": ({"name": "bytes", "configuration": {"endian": "little"}},),
            "chunk_key_encoding": {"name": "default"},
        }
        return cls({**document, **overrides}, context=context)

    @classmethod
    def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV3ArrayMetadata:
        """The model of `data`, a v3 array document read in `context`.

        `MetadataValidationError` with every problem the read finds.
        `read_array_metadata_v3` gives the reading this model is built
        from, and the problems of a document with some.
        """
        return cls(data, context=context)

    @classmethod
    def from_key_value(
        cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
    ) -> ZarrV3ArrayMetadata:
        """The model of the array document at `zarr.json` in `mapping`, read in `context`.

        `MetadataValidationError` when the key is missing, its bytes are not
        JSON, or the document is not valid.
        """
        return cls(load_store_json(mapping, ZARR_V3_ARRAY_METADATA_STORE_KEY), context=context)

__slots__ class-attribute instance-attribute

__slots__ = (
    "_claims",
    "_context",
    "_document",
    "_members",
    "_reading",
    "_shown",
)

attributes property

attributes: Mapping[str, JSONValue]

The attributes, read-only at every level; empty when the document writes none.

chunk_grid property

The chunk grid, as the scope read it.

chunk_key_encoding property

The chunk key encoding, as the scope read it.

claims property

claims: Claims

What the reading claimed of each name the document writes, keyed as the scope files it.

codecs property

The codecs, each as the scope read it, in pipeline order.

context property

context: Context

The scope the document was read in, which update reads new members in.

data_type property

The data type, as the scope read it.

dimension_names property

dimension_names: tuple[str | None, ...] | UNSET

The dimension names; UNSET when the document writes none.

extra_fields property

Each member the spec does not define, by name, read-only at every level.

fill_value property

fill_value: JSONValue

The fill value as written, read-only at every level.

must_understand_fields property

must_understand_fields: dict[str, ZarrV3ExtensionField]

Extra fields the reader is obligated to understand.

Everything in extra_fields not explicitly waived with must_understand: false (the spec's implicit-true rule, https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/core/index.rst#L1571-L1578). A compliant reader MUST fail to open the array if this contains any field it does not recognize; the model layer only partitions by obligation, since recognition is reader-specific.

node_type class-attribute instance-attribute

node_type: Final = 'array'

reading property

The document as the scope read it: each field, the pipeline, the chunk each codec is handed.

shape property

shape: tuple[int, ...]

The array's shape.

storage_transformers property

The storage transformers, each as the scope read it.

zarr_format class-attribute instance-attribute

zarr_format: Final = 3

__eq__

__eq__(other: object) -> bool
Source code in src/zarr_metadata/model/_keyed.py
def __eq__(self, other: object) -> bool:
    if type(other) is not type(self):
        return NotImplemented
    return self._key == other._key

__hash__

__hash__() -> int
Source code in src/zarr_metadata/model/_keyed.py
def __hash__(self) -> int:
    return hash(self._key)

__init__

__init__(
    document: object, context: Context | None = None
) -> None
Source code in src/zarr_metadata/model/_array.py
def __init__(self, document: object, context: Context | None = None) -> None:
    scope = CORE_AND_EXTENSIONS if context is None else context
    reading, members = read_array_v3(document, scope)
    if members is None:
        raise MetadataValidationError(reading.problems)
    self._adopt(refined_object(document), scope, reading, members)

__reduce__

Source code in src/zarr_metadata/model/_array.py
def __reduce__(self) -> tuple[type[ZarrV3ArrayMetadata], tuple[object, Context]]:
    # The pair, read again on load: a model's reading never disagrees
    # with its document.
    return type(self), (self._document, self._context)

__repr__

__repr__() -> str
Source code in src/zarr_metadata/model/_array.py
def __repr__(self) -> str:
    return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

create_default classmethod

create_default(
    *,
    context: Context | None = None,
    **overrides: Unpack[ZarrV3ArrayMetadataJSONPartial],
) -> ZarrV3ArrayMetadata

A scalar uint8 array, or the one overrides, members of its document, make of it, read in context.

MetadataValidationError when the document they make has a problem, so members that go together are passed together: a data type with a fill value of it, a grid with the shape it fits. The default codec is bytes with a little endian, which takes a data type of any fixed size. Overriding shape without chunk_grid derives a consistent default grid: one regular chunk covering the array (chunk_shape equal to shape, with a length of 1 for a dimension of length 0, since "Chunk sizes must be greater than zero", https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/chunk-grids/regular-grid/index.rst#L40).

Source code in src/zarr_metadata/model/_array.py
@classmethod
def create_default(
    cls,
    *,
    context: Context | None = None,
    **overrides: Unpack[ZarrV3ArrayMetadataJSONPartial],
) -> ZarrV3ArrayMetadata:
    """A scalar `uint8` array, or the one `overrides`, members of its document, make of it, read in `context`.

    `MetadataValidationError` when the document they make has a
    problem, so members that go together are passed together: a data
    type with a fill value of it, a grid with the shape it fits. The
    default codec is `bytes` with a little `endian`, which takes a data
    type of any fixed size. Overriding `shape` without `chunk_grid`
    derives a consistent default grid: one regular chunk covering the
    array (`chunk_shape` equal to `shape`, with a length of 1 for a
    dimension of length 0, since "Chunk sizes must be greater than
    zero",
    https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/chunk-grids/regular-grid/index.rst#L40).
    """
    # The grid derives from a shape the read takes; one it refuses is
    # reported by the read, and derives nothing.
    lengths, _ = dimension_lengths(overrides, "shape")
    document: dict[str, object] = {
        "zarr_format": 3,
        "node_type": "array",
        "shape": (),
        "fill_value": 0,
        "data_type": "uint8",
        "chunk_grid": {
            "name": "regular",
            "configuration": {"chunk_shape": tuple(max(length, 1) for length in lengths or ())},
        },
        "codecs": ({"name": "bytes", "configuration": {"endian": "little"}},),
        "chunk_key_encoding": {"name": "default"},
    }
    return cls({**document, **overrides}, context=context)

from_json classmethod

from_json(
    data: object, *, context: Context | None = None
) -> ZarrV3ArrayMetadata

The model of data, a v3 array document read in context.

MetadataValidationError with every problem the read finds. read_array_metadata_v3 gives the reading this model is built from, and the problems of a document with some.

Source code in src/zarr_metadata/model/_array.py
@classmethod
def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV3ArrayMetadata:
    """The model of `data`, a v3 array document read in `context`.

    `MetadataValidationError` with every problem the read finds.
    `read_array_metadata_v3` gives the reading this model is built
    from, and the problems of a document with some.
    """
    return cls(data, context=context)

from_key_value classmethod

from_key_value(
    mapping: Mapping[StoreKey, bytes],
    *,
    context: Context | None = None,
) -> ZarrV3ArrayMetadata

The model of the array document at zarr.json in mapping, read in context.

MetadataValidationError when the key is missing, its bytes are not JSON, or the document is not valid.

Source code in src/zarr_metadata/model/_array.py
@classmethod
def from_key_value(
    cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
) -> ZarrV3ArrayMetadata:
    """The model of the array document at `zarr.json` in `mapping`, read in `context`.

    `MetadataValidationError` when the key is missing, its bytes are not
    JSON, or the document is not valid.
    """
    return cls(load_store_json(mapping, ZARR_V3_ARRAY_METADATA_STORE_KEY), context=context)

refined_in

refined_in(
    context: Context | None = None,
) -> ZarrV3ArrayMetadata

This document read in context, which may claim what this scope left unclaimed and contradict nothing.

ScopeConflictError naming each name context reads by another definition, or by none, where this scope read it by one -- a loss of meaning is refused as a conflict is -- and where each sits in the document. MetadataValidationError when a name context claims refuses what was written under it: a gain can surface a problem. with_context reads the document in any scope.

Source code in src/zarr_metadata/model/_array.py
def refined_in(self, context: Context | None = None) -> ZarrV3ArrayMetadata:
    """This document read in `context`, which may claim what this scope left unclaimed and contradict nothing.

    `ScopeConflictError` naming each name `context` reads by another
    definition, or by none, where this scope read it by one -- a loss
    of meaning is refused as a conflict is -- and where each sits in
    the document. `MetadataValidationError` when a name `context`
    claims refuses what was written under it: a gain can surface a
    problem. `with_context` reads the document in any scope.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    found = scope.disagreements(self._claims)
    if len(found.conflicts) != 0:
        raise ScopeConflictError(located_conflicts(self._reading.fields(), found.conflicts))
    return self.with_context(scope)

refines

refines(other: ZarrV3ArrayMetadata) -> bool

Whether this model holds everything other holds: each field refines its counterpart, as refines orders fields -- the fields a field holds with it -- and every other member is the same, the fill value as the more informed data type spells it; a fill value that data type refuses is no refinement.

Source code in src/zarr_metadata/model/_array.py
def refines(self, other: ZarrV3ArrayMetadata) -> bool:
    """Whether this model holds everything `other` holds: each field refines its counterpart, as `refines` orders fields -- the fields a field holds with it -- and every other member is the same, the fill value as the more informed data type spells it; a fill value that data type refuses is no refinement."""
    if type(other) is not type(self):
        return False
    if len(self.codecs) != len(other.codecs) or len(self.storage_transformers) != len(
        other.storage_transformers
    ):
        return False
    pairs = (
        (self.data_type, other.data_type),
        (self.chunk_grid, other.chunk_grid),
        (self.chunk_key_encoding, other.chunk_key_encoding),
        *zip(self.codecs, other.codecs, strict=True),
        *zip(self.storage_transformers, other.storage_transformers, strict=True),
    )
    if not all(refines_field(mine, theirs) for mine, theirs in pairs):
        return False
    if len(fill_value_problems(self.data_type, other._members.fill_value)) != 0:
        return False
    return self._plain_key(self.data_type) == other._plain_key(self.data_type)

to_json

The document as written, refined, sharing nothing with the model.

Source code in src/zarr_metadata/model/_array.py
def to_json(self) -> ZarrV3ArrayMetadataJSON:
    """The document as written, refined, sharing nothing with the model."""
    return cast("ZarrV3ArrayMetadataJSON", copied(self._document))

to_key_value

to_key_value(
    *, indent: int | str | None = None
) -> Mapping[ZarrV3ArrayMetadataStoreKey, bytes]

The document as a store holds it: JSON bytes at zarr.json, indented by indent.

NaN, Infinity and -Infinity in attributes are written as those bare tokens, as zarr-python writes them, which a strict JSON parser refuses.

Source code in src/zarr_metadata/model/_array.py
def to_key_value(
    self, *, indent: int | str | None = None
) -> Mapping[ZarrV3ArrayMetadataStoreKey, bytes]:
    """The document as a store holds it: JSON bytes at `zarr.json`, indented by `indent`.

    `NaN`, `Infinity` and `-Infinity` in `attributes` are written as
    those bare tokens, as zarr-python writes them, which a strict JSON
    parser refuses.
    """
    return {ZARR_V3_ARRAY_METADATA_STORE_KEY: dump_store_json(self._document, indent=indent)}

update

update(
    **members: Unpack[ZarrV3ArrayMetadataUpdate],
) -> ZarrV3ArrayMetadata

This model with members, JSON, in place of the document's, UNSET leaving one out, read in this model's own scope.

MetadataValidationError when the document they make has a problem, so members that go together are passed together: a shape with a grid that fits it.

Source code in src/zarr_metadata/model/_array.py
def update(self, **members: Unpack[ZarrV3ArrayMetadataUpdate]) -> ZarrV3ArrayMetadata:
    """This model with `members`, JSON, in place of the document's, `UNSET` leaving one out, read in this model's own scope.

    `MetadataValidationError` when the document they make has a
    problem, so members that go together are passed together: a
    `shape` with a grid that fits it.
    """
    document: dict[str, object] = {**self._document, **members}
    for key, value in members.items():
        if value is UNSET:
            del document[key]
    return type(self)(document, context=self._context)

with_context

with_context(
    context: Context | None = None,
) -> ZarrV3ArrayMetadata

This document read in context, whatever that changes: a gain, a loss, a conflict.

MetadataValidationError when the document has a problem there. The reading is kept when context reads every claim identically.

Source code in src/zarr_metadata/model/_array.py
def with_context(self, context: Context | None = None) -> ZarrV3ArrayMetadata:
    """This document read in `context`, whatever that changes: a gain, a loss, a conflict.

    `MetadataValidationError` when the document has a problem there.
    The reading is kept when `context` reads every claim identically.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    if scope.disagreements(self._claims).agrees:
        return self._of(self._document, scope, self._reading, self._members)
    return type(self)(self._document, context=scope)

ZarrV3ArrayMetadataReading dataclass

A v3 array document as a scope read it, whatever it holds: each extension point, its codecs as a pipeline, every problem, and the model when there is none.

A field the document does not hold is UNSET, and a list of them it does not hold as a list is empty.

Source code in src/zarr_metadata/model/_validation.py
@dataclass(frozen=True, slots=True)
class ZarrV3ArrayMetadataReading:
    """A v3 array document as a scope read it, whatever it holds: each extension point, its codecs as a pipeline, every problem, and the model when there is none.

    A field the document does not hold is `UNSET`, and a list of them it does
    not hold as a list is empty.
    """

    data_type: ResolvedField[DataTypeDefinition[Any]] | UNSET = UNSET
    """The data type, as the scope read it."""
    chunk_grid: ResolvedField[ChunkGridDefinition[Any]] | UNSET = UNSET
    """The chunk grid, as the scope read it."""
    chunk_key_encoding: ResolvedField[ChunkKeyEncodingDefinition[Any]] | UNSET = UNSET
    """The chunk key encoding, as the scope read it."""
    chunk: Chunk = dataclasses.field(default_factory=Chunk)
    """The chunks the codecs are handed: the lengths the grid's chunks take along each axis of the shape, of the data type."""
    pipeline: tuple[Stage, ...] = ()
    """The codecs, read as a pipeline: each as the scope read it, with the chunk it is handed."""
    storage_transformers: tuple[ResolvedField[StorageTransformerDefinition[Any]], ...] = ()
    """The storage transformers, each as the scope read it."""
    problems: tuple[ValidationProblem, ...] = ()
    """Every reason the document is not a valid one."""
    metadata: ZarrV3ArrayMetadata | None = None
    """The document's model, holding these fields, when there is no problem; None otherwise."""

    def __reduce__(self) -> str | tuple[object, ...]:
        # A reading that holds its model pickles and copies as the model
        # does -- the pair, read once on load -- and comes back as that
        # model's own reading, so one model per document still.
        if self.metadata is not None:
            return (reading_of, (self.metadata,))
        return object.__reduce__(self)

    def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
        """Each field the document holds, as the scope read it, with where it sits in the document.

        The extension points, then each codec and storage transformer at its
        index, each followed by the fields it holds, as `fields_of` gives
        them: a shard's codecs, a struct's field types. `with_problems`
        gives each with its problems.
        """
        own: tuple[tuple[str, ResolvedField[Any] | UNSET], ...] = (
            ("data_type", self.data_type),
            ("chunk_grid", self.chunk_grid),
            ("chunk_key_encoding", self.chunk_key_encoding),
        )
        for key, field in own:
            if field is not UNSET:
                yield from fields_of(field, (key,))
        for index, stage in enumerate(self.pipeline):
            yield from fields_of(stage.codec, ("codecs", index))
        for index, transformer in enumerate(self.storage_transformers):
            yield from fields_of(transformer, ("storage_transformers", index))

chunk class-attribute instance-attribute

chunk: Chunk = dataclasses.field(default_factory=Chunk)

The chunks the codecs are handed: the lengths the grid's chunks take along each axis of the shape, of the data type.

chunk_grid class-attribute instance-attribute

The chunk grid, as the scope read it.

chunk_key_encoding class-attribute instance-attribute

chunk_key_encoding: (
    ResolvedField[ChunkKeyEncodingDefinition[Any]] | UNSET
) = UNSET

The chunk key encoding, as the scope read it.

data_type class-attribute instance-attribute

The data type, as the scope read it.

metadata class-attribute instance-attribute

metadata: ZarrV3ArrayMetadata | None = None

The document's model, holding these fields, when there is no problem; None otherwise.

pipeline class-attribute instance-attribute

pipeline: tuple[Stage, ...] = ()

The codecs, read as a pipeline: each as the scope read it, with the chunk it is handed.

problems class-attribute instance-attribute

problems: tuple[ValidationProblem, ...] = ()

Every reason the document is not a valid one.

storage_transformers class-attribute instance-attribute

storage_transformers: tuple[
    ResolvedField[StorageTransformerDefinition[Any]], ...
] = ()

The storage transformers, each as the scope read it.

__init__

__init__(
    data_type: ResolvedField[DataTypeDefinition[Any]]
    | UNSET = UNSET,
    chunk_grid: ResolvedField[ChunkGridDefinition[Any]]
    | UNSET = UNSET,
    chunk_key_encoding: ResolvedField[
        ChunkKeyEncodingDefinition[Any]
    ]
    | UNSET = UNSET,
    chunk: Chunk = Chunk(),
    pipeline: tuple[Stage, ...] = (),
    storage_transformers: tuple[
        ResolvedField[StorageTransformerDefinition[Any]],
        ...,
    ] = (),
    problems: tuple[ValidationProblem, ...] = (),
    metadata: ZarrV3ArrayMetadata | None = None,
) -> None

__reduce__

__reduce__() -> str | tuple[object, ...]
Source code in src/zarr_metadata/model/_validation.py
def __reduce__(self) -> str | tuple[object, ...]:
    # A reading that holds its model pickles and copies as the model
    # does -- the pair, read once on load -- and comes back as that
    # model's own reading, so one model per document still.
    if self.metadata is not None:
        return (reading_of, (self.metadata,))
    return object.__reduce__(self)

fields

Each field the document holds, as the scope read it, with where it sits in the document.

The extension points, then each codec and storage transformer at its index, each followed by the fields it holds, as fields_of gives them: a shard's codecs, a struct's field types. with_problems gives each with its problems.

Source code in src/zarr_metadata/model/_validation.py
def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
    """Each field the document holds, as the scope read it, with where it sits in the document.

    The extension points, then each codec and storage transformer at its
    index, each followed by the fields it holds, as `fields_of` gives
    them: a shard's codecs, a struct's field types. `with_problems`
    gives each with its problems.
    """
    own: tuple[tuple[str, ResolvedField[Any] | UNSET], ...] = (
        ("data_type", self.data_type),
        ("chunk_grid", self.chunk_grid),
        ("chunk_key_encoding", self.chunk_key_encoding),
    )
    for key, field in own:
        if field is not UNSET:
            yield from fields_of(field, (key,))
    for index, stage in enumerate(self.pipeline):
        yield from fields_of(stage.codec, ("codecs", index))
    for index, transformer in enumerate(self.storage_transformers):
        yield from fields_of(transformer, ("storage_transformers", index))

ZarrV3ArrayMetadataUpdate

Bases: TypedDict

The members ZarrV3ArrayMetadata.update puts in place: each as a document writes it, or UNSET to leave out one a document may leave out.

Those are dimension_names, attributes, storage_transformers, and a member the spec does not define.

Source code in src/zarr_metadata/model/_array.py
class ZarrV3ArrayMetadataUpdate(TypedDict, total=False, extra_items=ZarrV3ExtensionField | UNSET):
    """The members `ZarrV3ArrayMetadata.update` puts in place: each as a document writes it, or `UNSET` to leave out one a document may leave out.

    Those are `dimension_names`, `attributes`, `storage_transformers`, and
    a member the spec does not define.
    """

    shape: tuple[int, ...]
    data_type: ZarrV3MetadataFieldJSON
    chunk_grid: ZarrV3MetadataFieldJSON
    chunk_key_encoding: ZarrV3MetadataFieldJSON
    fill_value: JSONValue
    codecs: tuple[ZarrV3MetadataFieldJSON, ...]
    attributes: Mapping[str, JSONValue] | UNSET
    storage_transformers: tuple[ZarrV3MetadataFieldJSON, ...] | UNSET
    dimension_names: tuple[str | None, ...] | UNSET

attributes instance-attribute

attributes: Mapping[str, JSONValue] | UNSET

chunk_grid instance-attribute

chunk_key_encoding instance-attribute

chunk_key_encoding: ZarrV3MetadataFieldJSON

codecs instance-attribute

data_type instance-attribute

dimension_names instance-attribute

dimension_names: tuple[str | None, ...] | UNSET

fill_value instance-attribute

fill_value: JSONValue

shape instance-attribute

shape: tuple[int, ...]

storage_transformers instance-attribute

storage_transformers: (
    tuple[ZarrV3MetadataFieldJSON, ...] | UNSET
)

ZarrV3ConsolidatedMetadata

Bases: Keyed

A group's inline consolidated_metadata member, and the scope it was read in.

Models the reference-implementation convention where consolidated metadata is embedded as an extension field on a group's zarr.json. metadata maps each path to the model of the complete document there, array or group, of this scope: a view of the group's pair when a group holds it, built from the group's one read; or of its own pair, when the member is read on its own. kind is inline and must_understand False, by declaration. The documents and the group make the hierarchy below the group, the group its root, each at its node's path without the leading /: the node at /a/b at a/b.

Source code in src/zarr_metadata/model/_group.py
class ZarrV3ConsolidatedMetadata(Keyed):
    """A group's inline `consolidated_metadata` member, and the scope it was read in.

    Models the reference-implementation convention where consolidated
    metadata is embedded as an extension field on a group's `zarr.json`.
    `metadata` maps each path to the model of the complete document there,
    array or group, of this scope: a view of the group's pair when a group
    holds it, built from the group's one read; or of its own pair, when
    the member is read on its own. `kind` is `inline` and `must_understand`
    `False`, by declaration. The documents and the group make the
    hierarchy below the group, the group its root, each at its node's
    path without the leading `/`: the node at `/a/b` at `a/b`.
    """

    __slots__ = ("_context", "_document", "_metadata")

    kind: Final = "inline"
    must_understand: Final = False

    def __init__(self, member: object, context: Context | None = None) -> None:
        scope = CORE_AND_EXTENSIONS if context is None else context
        # The member sits under a group's key wherever it is read, so the
        # levels a reader walks are counted from there, as in the group.
        readings, members, problems = _read_consolidated_v3(
            member, scope, (ZARR_V3_CONSOLIDATED_METADATA_KEY,)
        )
        if len(problems) != 0:
            raise MetadataValidationError(problems)
        document = refined_object(_member_documents_for(member))
        documents = object_at(document, "metadata")
        self._adopt(document, scope, _nested_models(documents, scope, readings, members))

    @classmethod
    def _of(
        cls,
        document: dict[str, JSONValue],
        context: Context,
        metadata: dict[str, ZarrV3NodeMetadata],
    ) -> ZarrV3ConsolidatedMetadata:
        """The member of a group a read found nothing wrong with, holding the models that read built. The readers of this package build models through this, the private use pyright reports."""
        model = object.__new__(cls)
        model._adopt(document, context, metadata)
        return model

    def _adopt(
        self,
        document: dict[str, JSONValue],
        context: Context,
        metadata: dict[str, ZarrV3NodeMetadata],
    ) -> None:
        self._document = document
        self._context = context
        self._metadata = metadata
        self._key = self._key_of()
        # Hidden from the readings, which hold their own models; see `metadata`.

    @property
    def context(self) -> Context:
        """The scope the documents were read in."""
        return self._context

    @property
    def metadata(self) -> Mapping[str, ZarrV3NodeMetadata]:
        """The model of each document, by its path below the group: a read-only view."""
        return MappingProxyType(self._metadata)

    def to_json(self) -> ZarrV3ConsolidatedMetadataJSON:
        """The member as written, refined, sharing nothing with the model."""
        return cast("ZarrV3ConsolidatedMetadataJSON", copied(self._document))

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

    def _key_of(self) -> tuple[object, ...]:
        """What `==` and `hash` compare of consolidated metadata: each document's key, by its path, in path order."""
        return tuple(
            (path, node._key)
            for path, node in sorted(self.metadata.items(), key=lambda item: item[0])
        )

    def __reduce__(self) -> tuple[type[ZarrV3ConsolidatedMetadata], tuple[object, Context]]:
        return type(self), (self._document, self._context)

    def refines(self, other: ZarrV3ConsolidatedMetadata) -> bool:
        """Whether every document this holds refines the one `other` holds at the same path, and neither holds a path the other does not; False of what is not consolidated metadata."""
        if type(other) is not type(self):
            return False
        if self._metadata.keys() != other._metadata.keys():
            return False
        return all(
            _node_refines(self._metadata[path], other._metadata[path]) for path in self._metadata
        )

    @classmethod
    def from_json(
        cls, data: object, *, context: Context | None = None
    ) -> ZarrV3ConsolidatedMetadata:
        """The model of `data`, a group's `consolidated_metadata` member, each document read once in `context`, as the array or group its `node_type` says; `MetadataValidationError` with every problem found."""
        return cls(data, context=context)

__slots__ class-attribute instance-attribute

__slots__ = ('_context', '_document', '_metadata')

context property

context: Context

The scope the documents were read in.

kind class-attribute instance-attribute

kind: Final = 'inline'

metadata property

The model of each document, by its path below the group: a read-only view.

must_understand class-attribute instance-attribute

must_understand: Final = False

__eq__

__eq__(other: object) -> bool
Source code in src/zarr_metadata/model/_keyed.py
def __eq__(self, other: object) -> bool:
    if type(other) is not type(self):
        return NotImplemented
    return self._key == other._key

__hash__

__hash__() -> int
Source code in src/zarr_metadata/model/_keyed.py
def __hash__(self) -> int:
    return hash(self._key)

__init__

__init__(
    member: object, context: Context | None = None
) -> None
Source code in src/zarr_metadata/model/_group.py
def __init__(self, member: object, context: Context | None = None) -> None:
    scope = CORE_AND_EXTENSIONS if context is None else context
    # The member sits under a group's key wherever it is read, so the
    # levels a reader walks are counted from there, as in the group.
    readings, members, problems = _read_consolidated_v3(
        member, scope, (ZARR_V3_CONSOLIDATED_METADATA_KEY,)
    )
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    document = refined_object(_member_documents_for(member))
    documents = object_at(document, "metadata")
    self._adopt(document, scope, _nested_models(documents, scope, readings, members))

__reduce__

Source code in src/zarr_metadata/model/_group.py
def __reduce__(self) -> tuple[type[ZarrV3ConsolidatedMetadata], tuple[object, Context]]:
    return type(self), (self._document, self._context)

__repr__

__repr__() -> str
Source code in src/zarr_metadata/model/_group.py
def __repr__(self) -> str:
    return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

from_json classmethod

from_json(
    data: object, *, context: Context | None = None
) -> ZarrV3ConsolidatedMetadata

The model of data, a group's consolidated_metadata member, each document read once in context, as the array or group its node_type says; MetadataValidationError with every problem found.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def from_json(
    cls, data: object, *, context: Context | None = None
) -> ZarrV3ConsolidatedMetadata:
    """The model of `data`, a group's `consolidated_metadata` member, each document read once in `context`, as the array or group its `node_type` says; `MetadataValidationError` with every problem found."""
    return cls(data, context=context)

refines

refines(other: ZarrV3ConsolidatedMetadata) -> bool

Whether every document this holds refines the one other holds at the same path, and neither holds a path the other does not; False of what is not consolidated metadata.

Source code in src/zarr_metadata/model/_group.py
def refines(self, other: ZarrV3ConsolidatedMetadata) -> bool:
    """Whether every document this holds refines the one `other` holds at the same path, and neither holds a path the other does not; False of what is not consolidated metadata."""
    if type(other) is not type(self):
        return False
    if self._metadata.keys() != other._metadata.keys():
        return False
    return all(
        _node_refines(self._metadata[path], other._metadata[path]) for path in self._metadata
    )

to_json

The member as written, refined, sharing nothing with the model.

Source code in src/zarr_metadata/model/_group.py
def to_json(self) -> ZarrV3ConsolidatedMetadataJSON:
    """The member as written, refined, sharing nothing with the model."""
    return cast("ZarrV3ConsolidatedMetadataJSON", copied(self._document))

ZarrV3ConsolidatedMetadataInput

Bases: TypedDict

The consolidated_metadata member as a constructor or update takes it: as a document writes it, each entry a document or a node model.

A node model is accepted when the group's scope reads every claim of it identically, or claims what the model's scope left unclaimed -- it is then read again there -- and refused, with a problem at its path, where the two scopes read a name differently, or the group's scope leaves it unclaimed.

Source code in src/zarr_metadata/model/_group.py
class ZarrV3ConsolidatedMetadataInput(TypedDict, closed=True):
    """The `consolidated_metadata` member as a constructor or `update` takes it: as a document writes it, each entry a document or a node model.

    A node model is accepted when the group's scope reads every claim of
    it identically, or claims what the model's scope left unclaimed -- it
    is then read again there -- and refused, with a problem at its path,
    where the two scopes read a name differently, or the group's scope
    leaves it unclaimed.
    """

    kind: Literal["inline"]
    must_understand: Literal[False]
    metadata: Mapping[str, ZarrV3NodeMetadataInput]

kind instance-attribute

kind: Literal['inline']

metadata instance-attribute

must_understand instance-attribute

must_understand: Literal[False]

ZarrV3GroupMetadata

Bases: Keyed

A v3 group document, and the scope it was read in.

The model is the pair, as ZarrV3ArrayMetadata is: to_json is the document as written, refined, and context the scope. attributes and extra_fields are views of what the read refined. The consolidated_metadata reference-implementation convention is a ZarrV3ConsolidatedMetadata view of the same pair: each document it holds is a model of this scope, built from this one read. Built only by reading: the constructor reads document in context and raises MetadataValidationError with every problem, a nested document's located under consolidated_metadata.metadata.<path>.

Source code in src/zarr_metadata/model/_group.py
class ZarrV3GroupMetadata(Keyed):
    """A v3 group document, and the scope it was read in.

    The model is the pair, as `ZarrV3ArrayMetadata` is: `to_json` is the
    document as written, refined, and `context` the scope. `attributes`
    and `extra_fields` are views of what the read refined. The
    `consolidated_metadata` reference-implementation convention is a
    `ZarrV3ConsolidatedMetadata` view of the same pair: each document it
    holds is a model of this scope, built from this one read. Built only
    by reading: the constructor reads `document` in `context` and raises
    `MetadataValidationError` with every problem, a nested document's
    located under `consolidated_metadata.metadata.<path>`.
    """

    __slots__ = (
        "_claims",
        "_consolidated",
        "_context",
        "_document",
        "_members",
        "_reading",
        "_shown",
    )

    zarr_format: Final = 3
    node_type: Final = "group"

    @property
    def claims(self) -> Claims:
        """What the reading claimed of each name the document and its consolidated documents write, keyed as the scope files it."""
        return self._claims

    def __init__(self, document: object, context: Context | None = None) -> None:
        scope = CORE_AND_EXTENSIONS if context is None else context
        reading, members = read_group_v3(document, scope)
        if members is None or len(reading.problems) != 0:
            raise MetadataValidationError(reading.problems)
        self._adopt(refined_object(documents_for(document)), scope, reading, members)

    @classmethod
    def _of(
        cls,
        document: dict[str, JSONValue],
        context: Context,
        reading: ZarrV3GroupMetadataReading,
        members: GroupMembersV3,
    ) -> ZarrV3GroupMetadata:
        """A model of a document a read found nothing wrong with, holding that reading: no second read. The readers of this package build models through this, the private use pyright reports."""
        model = object.__new__(cls)
        model._adopt(document, context, reading, members)
        return model

    def _adopt(
        self,
        document: dict[str, JSONValue],
        context: Context,
        reading: ZarrV3GroupMetadataReading,
        members: GroupMembersV3,
    ) -> None:
        self._document = document
        self._context = context
        self._members = members
        if members.attributes is None:
            msg = "a group model holds members a read found nothing wrong with"
            raise TypeError(msg)
        # What the model shows of its members, read-only at every level.
        self._shown = (
            frozen(members.attributes),
            frozen(members.extra_fields),
        )
        held: Mapping[str, ZarrV3NodeMetadataReading] = reading.consolidated
        if members.consolidated is UNSET:
            self._consolidated: ZarrV3ConsolidatedMetadata | UNSET = UNSET
        else:
            member = object_at(document, ZARR_V3_CONSOLIDATED_METADATA_KEY)
            documents = object_at(member, "metadata")
            models = _nested_models(documents, context, reading.consolidated, members.consolidated)
            # One model per document, of this scope: each nested reading
            # holds the model the group holds.
            held = MappingProxyType(
                {
                    path: (models[path].reading if path in models else nested)
                    for path, nested in reading.consolidated.items()
                }
            )
            self._consolidated = ZarrV3ConsolidatedMetadata._of(  # pyright: ignore[reportPrivateUsage]
                member, context, models
            )
        # The reading holds the model it built, however the model was built;
        # what it holds of the nested documents is read-only, as the model is.
        self._reading = dataclasses.replace(
            reading, consolidated=MappingProxyType(dict(held)), metadata=self
        )
        self._key = self._key_of()
        self._claims = MappingProxyType(claims_of(reading.fields()))

    # --- the pair ---------------------------------------------------------

    @property
    def context(self) -> Context:
        """The scope the document was read in, which `update` reads new members in."""
        return self._context

    @property
    def reading(self) -> ZarrV3GroupMetadataReading:
        """The document as the scope read it: each document its consolidated metadata holds, as read."""
        return self._reading

    def to_json(self) -> ZarrV3GroupMetadataJSON:
        """The document as written, refined, sharing nothing with the model."""
        return cast("ZarrV3GroupMetadataJSON", copied(self._document))

    def to_key_value(
        self, *, indent: int | str | None = None
    ) -> Mapping[ZarrV3GroupMetadataStoreKey, bytes]:
        """The document as a store holds it: JSON bytes at `zarr.json`, indented by `indent`.

        `NaN`, `Infinity` and `-Infinity` in `attributes` are written as
        those bare tokens, as zarr-python writes them, which a strict JSON
        parser refuses.
        """
        return {ZARR_V3_GROUP_METADATA_STORE_KEY: dump_store_json(self._document, indent=indent)}

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

    def _key_of(self) -> tuple[object, ...]:
        """What `==` and `hash` compare of a v3 group model: its attributes and extra fields as JSON text, and what its consolidated metadata holds, by its key."""
        consolidated = self.consolidated_metadata
        members = self._members
        return (
            json_text(members.attributes),
            UNSET if consolidated is UNSET else consolidated._key,
            json_text(members.extra_fields),
        )

    def __reduce__(self) -> tuple[type[ZarrV3GroupMetadata], tuple[object, Context]]:
        # The pair, read again on load.
        return type(self), (self._document, self._context)

    # --- typed views ------------------------------------------------------

    @property
    def attributes(self) -> Mapping[str, JSONValue]:
        """The attributes, read-only at every level; empty when the document writes none."""
        return self._shown[0]

    @property
    def extra_fields(self) -> Mapping[str, ZarrV3ExtensionField]:
        """Each member the spec does not define, `consolidated_metadata` apart, by name: read-only at every level."""
        return self._shown[1]

    @property
    def consolidated_metadata(self) -> ZarrV3ConsolidatedMetadata | UNSET:
        """The `consolidated_metadata` member as a model of this scope; `UNSET` when the document writes none."""
        return self._consolidated

    @property
    def must_understand_fields(self) -> dict[str, ZarrV3ExtensionField]:
        """Extra fields the reader is obligated to understand.

        Everything in `extra_fields` not explicitly waived with
        `must_understand: false` (the spec's implicit-true rule, https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/core/index.rst#L1571-L1578). A compliant
        reader MUST fail to open the group if this contains any field it does
        not recognize; the model layer only partitions by obligation, since
        recognition is reader-specific.
        """
        return must_understand_subset(self.extra_fields)

    # --- changing ---------------------------------------------------------

    def update(self, **members: Unpack[ZarrV3GroupMetadataUpdate]) -> ZarrV3GroupMetadata:
        """This model with `members`, JSON, in place of the document's, `UNSET` leaving one out, read in this model's own scope.

        A `consolidated_metadata` given is read; left out, the document's
        is read again as part of the whole. `MetadataValidationError` when
        the document they make has a problem.
        """
        document: dict[str, object] = {**self._document, **members}
        for key, value in members.items():
            if value is UNSET:
                del document[key]
        return type(self)(document, context=self._context)

    def with_context(self, context: Context | None = None) -> ZarrV3GroupMetadata:
        """This document read in `context`, whatever that changes; `MetadataValidationError` when it has a problem there. The reading is kept when `context` reads every claim identically."""
        scope = CORE_AND_EXTENSIONS if context is None else context
        if scope.disagreements(self._claims).agrees:
            return self._of(self._document, scope, self._reading, self._members)
        return type(self)(self._document, context=scope)

    def refined_in(self, context: Context | None = None) -> ZarrV3GroupMetadata:
        """This document read in `context`, which may claim what this scope left unclaimed and contradict nothing.

        `ScopeConflictError` naming each name `context` reads by another
        definition, or by none, and where each sits, in the documents the
        consolidated metadata holds too; `MetadataValidationError` when a
        name `context` claims refuses what was written under it.
        """
        scope = CORE_AND_EXTENSIONS if context is None else context
        found = scope.disagreements(self._claims)
        if len(found.conflicts) != 0:
            raise ScopeConflictError(located_conflicts(self._reading.fields(), found.conflicts))
        return self.with_context(scope)

    def refines(self, other: ZarrV3GroupMetadata) -> bool:
        """Whether this model holds everything `other` holds: the same attributes and extra fields, and consolidated metadata whose every document refines its counterpart."""
        if type(other) is not type(self):
            return False
        mine, theirs = self._members, other._members
        if json_text(mine.attributes) != json_text(theirs.attributes):
            return False
        if json_text(mine.extra_fields) != json_text(theirs.extra_fields):
            return False
        mine, theirs = self._consolidated, other._consolidated
        if mine is UNSET or theirs is UNSET:
            return mine is UNSET and theirs is UNSET
        return mine.refines(theirs)

    # --- constructors -----------------------------------------------------

    @classmethod
    def create_default(
        cls,
        *,
        context: Context | None = None,
        **members: Unpack[ZarrV3GroupMetadataJSONPartial],
    ) -> ZarrV3GroupMetadata:
        """A group with no attributes, or the one `members` of its document make of it, read in `context`; `MetadataValidationError` when its document has a problem."""
        return cls({"zarr_format": 3, "node_type": "group", **members}, context=context)

    @classmethod
    def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV3GroupMetadata:
        """The model of `data`, a v3 group document read in `context`, with each document its consolidated metadata holds.

        `MetadataValidationError` with every problem the read finds. A
        `consolidated_metadata` of `null`, which a zarr-python 3.0.x bug
        wrote, is a value the document wrote, and no object: a problem,
        as the spec says an object; `read_repaired_node_metadata_v3` reads
        such a store. A member the spec does not define is held in
        `extra_fields`.
        """
        return cls(data, context=context)

    @classmethod
    def from_key_value(
        cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
    ) -> ZarrV3GroupMetadata:
        """The model of the group document at `zarr.json` in `mapping`, read in `context`.

        `MetadataValidationError` when the key is missing, its bytes are not
        JSON, or the document is not valid.
        """
        return cls(load_store_json(mapping, ZARR_V3_GROUP_METADATA_STORE_KEY), context=context)

__slots__ class-attribute instance-attribute

__slots__ = (
    "_claims",
    "_consolidated",
    "_context",
    "_document",
    "_members",
    "_reading",
    "_shown",
)

attributes property

attributes: Mapping[str, JSONValue]

The attributes, read-only at every level; empty when the document writes none.

claims property

claims: Claims

What the reading claimed of each name the document and its consolidated documents write, keyed as the scope files it.

consolidated_metadata property

consolidated_metadata: ZarrV3ConsolidatedMetadata | UNSET

The consolidated_metadata member as a model of this scope; UNSET when the document writes none.

context property

context: Context

The scope the document was read in, which update reads new members in.

extra_fields property

Each member the spec does not define, consolidated_metadata apart, by name: read-only at every level.

must_understand_fields property

must_understand_fields: dict[str, ZarrV3ExtensionField]

Extra fields the reader is obligated to understand.

Everything in extra_fields not explicitly waived with must_understand: false (the spec's implicit-true rule, https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/core/index.rst#L1571-L1578). A compliant reader MUST fail to open the group if this contains any field it does not recognize; the model layer only partitions by obligation, since recognition is reader-specific.

node_type class-attribute instance-attribute

node_type: Final = 'group'

reading property

The document as the scope read it: each document its consolidated metadata holds, as read.

zarr_format class-attribute instance-attribute

zarr_format: Final = 3

__eq__

__eq__(other: object) -> bool
Source code in src/zarr_metadata/model/_keyed.py
def __eq__(self, other: object) -> bool:
    if type(other) is not type(self):
        return NotImplemented
    return self._key == other._key

__hash__

__hash__() -> int
Source code in src/zarr_metadata/model/_keyed.py
def __hash__(self) -> int:
    return hash(self._key)

__init__

__init__(
    document: object, context: Context | None = None
) -> None
Source code in src/zarr_metadata/model/_group.py
def __init__(self, document: object, context: Context | None = None) -> None:
    scope = CORE_AND_EXTENSIONS if context is None else context
    reading, members = read_group_v3(document, scope)
    if members is None or len(reading.problems) != 0:
        raise MetadataValidationError(reading.problems)
    self._adopt(refined_object(documents_for(document)), scope, reading, members)

__reduce__

Source code in src/zarr_metadata/model/_group.py
def __reduce__(self) -> tuple[type[ZarrV3GroupMetadata], tuple[object, Context]]:
    # The pair, read again on load.
    return type(self), (self._document, self._context)

__repr__

__repr__() -> str
Source code in src/zarr_metadata/model/_group.py
def __repr__(self) -> str:
    return f"{type(self).__name__}({self._document!r}, context={self._context!r})"

create_default classmethod

create_default(
    *,
    context: Context | None = None,
    **members: Unpack[ZarrV3GroupMetadataJSONPartial],
) -> ZarrV3GroupMetadata

A group with no attributes, or the one members of its document make of it, read in context; MetadataValidationError when its document has a problem.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def create_default(
    cls,
    *,
    context: Context | None = None,
    **members: Unpack[ZarrV3GroupMetadataJSONPartial],
) -> ZarrV3GroupMetadata:
    """A group with no attributes, or the one `members` of its document make of it, read in `context`; `MetadataValidationError` when its document has a problem."""
    return cls({"zarr_format": 3, "node_type": "group", **members}, context=context)

from_json classmethod

from_json(
    data: object, *, context: Context | None = None
) -> ZarrV3GroupMetadata

The model of data, a v3 group document read in context, with each document its consolidated metadata holds.

MetadataValidationError with every problem the read finds. A consolidated_metadata of null, which a zarr-python 3.0.x bug wrote, is a value the document wrote, and no object: a problem, as the spec says an object; read_repaired_node_metadata_v3 reads such a store. A member the spec does not define is held in extra_fields.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def from_json(cls, data: object, *, context: Context | None = None) -> ZarrV3GroupMetadata:
    """The model of `data`, a v3 group document read in `context`, with each document its consolidated metadata holds.

    `MetadataValidationError` with every problem the read finds. A
    `consolidated_metadata` of `null`, which a zarr-python 3.0.x bug
    wrote, is a value the document wrote, and no object: a problem,
    as the spec says an object; `read_repaired_node_metadata_v3` reads
    such a store. A member the spec does not define is held in
    `extra_fields`.
    """
    return cls(data, context=context)

from_key_value classmethod

from_key_value(
    mapping: Mapping[StoreKey, bytes],
    *,
    context: Context | None = None,
) -> ZarrV3GroupMetadata

The model of the group document at zarr.json in mapping, read in context.

MetadataValidationError when the key is missing, its bytes are not JSON, or the document is not valid.

Source code in src/zarr_metadata/model/_group.py
@classmethod
def from_key_value(
    cls, mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
) -> ZarrV3GroupMetadata:
    """The model of the group document at `zarr.json` in `mapping`, read in `context`.

    `MetadataValidationError` when the key is missing, its bytes are not
    JSON, or the document is not valid.
    """
    return cls(load_store_json(mapping, ZARR_V3_GROUP_METADATA_STORE_KEY), context=context)

refined_in

refined_in(
    context: Context | None = None,
) -> ZarrV3GroupMetadata

This document read in context, which may claim what this scope left unclaimed and contradict nothing.

ScopeConflictError naming each name context reads by another definition, or by none, and where each sits, in the documents the consolidated metadata holds too; MetadataValidationError when a name context claims refuses what was written under it.

Source code in src/zarr_metadata/model/_group.py
def refined_in(self, context: Context | None = None) -> ZarrV3GroupMetadata:
    """This document read in `context`, which may claim what this scope left unclaimed and contradict nothing.

    `ScopeConflictError` naming each name `context` reads by another
    definition, or by none, and where each sits, in the documents the
    consolidated metadata holds too; `MetadataValidationError` when a
    name `context` claims refuses what was written under it.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    found = scope.disagreements(self._claims)
    if len(found.conflicts) != 0:
        raise ScopeConflictError(located_conflicts(self._reading.fields(), found.conflicts))
    return self.with_context(scope)

refines

refines(other: ZarrV3GroupMetadata) -> bool

Whether this model holds everything other holds: the same attributes and extra fields, and consolidated metadata whose every document refines its counterpart.

Source code in src/zarr_metadata/model/_group.py
def refines(self, other: ZarrV3GroupMetadata) -> bool:
    """Whether this model holds everything `other` holds: the same attributes and extra fields, and consolidated metadata whose every document refines its counterpart."""
    if type(other) is not type(self):
        return False
    mine, theirs = self._members, other._members
    if json_text(mine.attributes) != json_text(theirs.attributes):
        return False
    if json_text(mine.extra_fields) != json_text(theirs.extra_fields):
        return False
    mine, theirs = self._consolidated, other._consolidated
    if mine is UNSET or theirs is UNSET:
        return mine is UNSET and theirs is UNSET
    return mine.refines(theirs)

to_json

The document as written, refined, sharing nothing with the model.

Source code in src/zarr_metadata/model/_group.py
def to_json(self) -> ZarrV3GroupMetadataJSON:
    """The document as written, refined, sharing nothing with the model."""
    return cast("ZarrV3GroupMetadataJSON", copied(self._document))

to_key_value

to_key_value(
    *, indent: int | str | None = None
) -> Mapping[ZarrV3GroupMetadataStoreKey, bytes]

The document as a store holds it: JSON bytes at zarr.json, indented by indent.

NaN, Infinity and -Infinity in attributes are written as those bare tokens, as zarr-python writes them, which a strict JSON parser refuses.

Source code in src/zarr_metadata/model/_group.py
def to_key_value(
    self, *, indent: int | str | None = None
) -> Mapping[ZarrV3GroupMetadataStoreKey, bytes]:
    """The document as a store holds it: JSON bytes at `zarr.json`, indented by `indent`.

    `NaN`, `Infinity` and `-Infinity` in `attributes` are written as
    those bare tokens, as zarr-python writes them, which a strict JSON
    parser refuses.
    """
    return {ZARR_V3_GROUP_METADATA_STORE_KEY: dump_store_json(self._document, indent=indent)}

update

update(
    **members: Unpack[ZarrV3GroupMetadataUpdate],
) -> ZarrV3GroupMetadata

This model with members, JSON, in place of the document's, UNSET leaving one out, read in this model's own scope.

A consolidated_metadata given is read; left out, the document's is read again as part of the whole. MetadataValidationError when the document they make has a problem.

Source code in src/zarr_metadata/model/_group.py
def update(self, **members: Unpack[ZarrV3GroupMetadataUpdate]) -> ZarrV3GroupMetadata:
    """This model with `members`, JSON, in place of the document's, `UNSET` leaving one out, read in this model's own scope.

    A `consolidated_metadata` given is read; left out, the document's
    is read again as part of the whole. `MetadataValidationError` when
    the document they make has a problem.
    """
    document: dict[str, object] = {**self._document, **members}
    for key, value in members.items():
        if value is UNSET:
            del document[key]
    return type(self)(document, context=self._context)

with_context

with_context(
    context: Context | None = None,
) -> ZarrV3GroupMetadata

This document read in context, whatever that changes; MetadataValidationError when it has a problem there. The reading is kept when context reads every claim identically.

Source code in src/zarr_metadata/model/_group.py
def with_context(self, context: Context | None = None) -> ZarrV3GroupMetadata:
    """This document read in `context`, whatever that changes; `MetadataValidationError` when it has a problem there. The reading is kept when `context` reads every claim identically."""
    scope = CORE_AND_EXTENSIONS if context is None else context
    if scope.disagreements(self._claims).agrees:
        return self._of(self._document, scope, self._reading, self._members)
    return type(self)(self._document, context=scope)

ZarrV3GroupMetadataReading dataclass

A v3 group document as a scope read it, whatever it holds: each document its consolidated metadata holds, as read, every problem, and the model when there is none.

Source code in src/zarr_metadata/model/_group.py
@dataclass(frozen=True, slots=True)
class ZarrV3GroupMetadataReading:
    """A v3 group document as a scope read it, whatever it holds: each document its consolidated metadata holds, as read, every problem, and the model when there is none."""

    consolidated: Mapping[str, ZarrV3NodeMetadataReading] = dataclasses.field(
        default_factory=_no_documents
    )
    """Each document its consolidated metadata holds, as `read_node_metadata_v3` reads one, by its path."""
    problems: tuple[ValidationProblem, ...] = ()
    """Every reason the document is not a valid one."""
    metadata: ZarrV3GroupMetadata | None = None
    """The document's model when there is no problem; None otherwise."""

    def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
        """Each field of each document its consolidated metadata holds, as read, with where it sits in this document."""
        for path, reading in self.consolidated.items():
            for loc, node in reading.fields():
                yield (ZARR_V3_CONSOLIDATED_METADATA_KEY, "metadata", path, *loc), node

    def __reduce__(self) -> tuple[Callable[..., object], tuple[object, ...]]:
        # A reading that holds its model pickles and copies as the model
        # does, and comes back as that model's own reading, so one model
        # per document still; one without is built again from the dict its
        # read-only view views, which pickles where the view does not.
        if self.metadata is not None:
            return (reading_of, (self.metadata,))
        return (_group_reading, (dict(self.consolidated), self.problems, None))

consolidated class-attribute instance-attribute

consolidated: Mapping[str, ZarrV3NodeMetadataReading] = (
    dataclasses.field(default_factory=_no_documents)
)

Each document its consolidated metadata holds, as read_node_metadata_v3 reads one, by its path.

metadata class-attribute instance-attribute

metadata: ZarrV3GroupMetadata | None = None

The document's model when there is no problem; None otherwise.

problems class-attribute instance-attribute

problems: tuple[ValidationProblem, ...] = ()

Every reason the document is not a valid one.

__init__

__init__(
    consolidated: Mapping[
        str, ZarrV3NodeMetadataReading
    ] = _no_documents(),
    problems: tuple[ValidationProblem, ...] = (),
    metadata: ZarrV3GroupMetadata | None = None,
) -> None

__reduce__

__reduce__() -> tuple[
    Callable[..., object], tuple[object, ...]
]
Source code in src/zarr_metadata/model/_group.py
def __reduce__(self) -> tuple[Callable[..., object], tuple[object, ...]]:
    # A reading that holds its model pickles and copies as the model
    # does, and comes back as that model's own reading, so one model
    # per document still; one without is built again from the dict its
    # read-only view views, which pickles where the view does not.
    if self.metadata is not None:
        return (reading_of, (self.metadata,))
    return (_group_reading, (dict(self.consolidated), self.problems, None))

fields

Each field of each document its consolidated metadata holds, as read, with where it sits in this document.

Source code in src/zarr_metadata/model/_group.py
def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
    """Each field of each document its consolidated metadata holds, as read, with where it sits in this document."""
    for path, reading in self.consolidated.items():
        for loc, node in reading.fields():
            yield (ZARR_V3_CONSOLIDATED_METADATA_KEY, "metadata", path, *loc), node

ZarrV3GroupMetadataUpdate

Bases: TypedDict

The members ZarrV3GroupMetadata.update puts in place: each as a document writes it, or UNSET to leave it out.

consolidated_metadata is given as a document writes it, each entry a document or a node model, as ZarrV3ConsolidatedMetadataInput says, or as another group's ZarrV3ConsolidatedMetadata, whose models are taken; left out, the document's are read again as part of the whole.

Source code in src/zarr_metadata/model/_group.py
class ZarrV3GroupMetadataUpdate(TypedDict, total=False, extra_items=ZarrV3ExtensionField | UNSET):
    """The members `ZarrV3GroupMetadata.update` puts in place: each as a document writes it, or `UNSET` to leave it out.

    `consolidated_metadata` is given as a document writes it, each entry a
    document or a node model, as `ZarrV3ConsolidatedMetadataInput` says,
    or as another group's `ZarrV3ConsolidatedMetadata`, whose models are
    taken; left out, the document's are read again as part of the whole.
    """

    attributes: Mapping[str, JSONValue] | UNSET
    consolidated_metadata: ZarrV3ConsolidatedMetadataInput | ZarrV3ConsolidatedMetadata | UNSET

attributes instance-attribute

attributes: Mapping[str, JSONValue] | UNSET

consolidated_metadata instance-attribute

ZarrV3RepairedNodeMetadataReading dataclass

A v3 zarr.json read after its known writer bugs were undone: the strict reading of the repaired document, and the repairs.

Source code in src/zarr_metadata/model/_repair.py
@dataclass(frozen=True, slots=True)
class ZarrV3RepairedNodeMetadataReading:
    """A v3 `zarr.json` read after its known writer bugs were undone: the strict reading of the repaired document, and the repairs."""

    reading: ZarrV3NodeMetadataReading
    """The repaired document, as `read_node_metadata_v3` reads it: its problems are the repaired document's, and its model when there are none."""
    repairs: tuple[Repair, ...]
    """What was changed to make the document that was read."""

reading instance-attribute

The repaired document, as read_node_metadata_v3 reads it: its problems are the repaired document's, and its model when there are none.

repairs instance-attribute

repairs: tuple[Repair, ...]

What was changed to make the document that was read.

__init__

__init__(
    reading: ZarrV3NodeMetadataReading,
    repairs: tuple[Repair, ...],
) -> None

ZarrV3UnknownNodeReading dataclass

A v3 document of no node type the spec defines -- its node_type missing, or neither "array" nor "group" -- or not an object at all: nothing else of it is read but its zarr_format, as its problems say.

So a document of another format says so: zarr-python 2's draft of v3 wrote a root zarr.json whose zarr_format is a URL, and a v2 document names format 2.

Source code in src/zarr_metadata/model/_group.py
@dataclass(frozen=True, slots=True)
class ZarrV3UnknownNodeReading:
    """A v3 document of no node type the spec defines -- its `node_type` missing, or neither `"array"` nor `"group"` -- or not an object at all: nothing else of it is read but its `zarr_format`, as its problems say.

    So a document of another format says so: zarr-python 2's draft of v3
    wrote a root `zarr.json` whose `zarr_format` is a URL, and a v2
    document names format 2.
    """

    problems: tuple[ValidationProblem, ...]
    """Why it is no node."""

    @property
    def metadata(self) -> None:
        """Its model: none, since no node type says which model it is."""
        return None

    def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
        """Its fields as read: none, since none of them is read."""
        return iter(())

metadata property

metadata: None

Its model: none, since no node type says which model it is.

problems instance-attribute

problems: tuple[ValidationProblem, ...]

Why it is no node.

__init__

__init__(problems: tuple[ValidationProblem, ...]) -> None

fields

Its fields as read: none, since none of them is read.

Source code in src/zarr_metadata/model/_group.py
def fields(self) -> Iterator[tuple[Loc, ResolvedField[Any]]]:
    """Its fields as read: none, since none of them is read."""
    return iter(())

is_array_metadata_v2

is_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV2ArrayMetadataJSON]

Whether value is a valid v2 array metadata document, read in context, CORE_V2 when none is given.

Source code in src/zarr_metadata/model/_validation.py
def is_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV2ArrayMetadataJSON]:
    """Whether `value` is a valid v2 array metadata document, read in `context`, `CORE_V2` when none is given."""
    return (
        _is_canonical_json(value, finite=False)
        and not validate_array_metadata_v2(value, context=context)
        and _is_canonical_array_metadata_v2(value)
    )

is_array_metadata_v3

is_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV3ArrayMetadataJSON]

Whether value is a v3 array document validate_array_metadata_v3 finds nothing wrong with, written with tuples.

Source code in src/zarr_metadata/model/_validation.py
def is_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV3ArrayMetadataJSON]:
    """Whether `value` is a v3 array document `validate_array_metadata_v3` finds nothing wrong with, written with tuples."""
    scope = CORE_AND_EXTENSIONS if context is None else context
    return (
        _is_canonical_json(value, finite=False)
        and not validate_array_metadata_v3(value, context=scope)
        and _is_canonical_array_metadata_v3(value)
    )

is_group_metadata_v2

is_group_metadata_v2(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV2GroupMetadataJSON]

Whether value is a structurally-valid v2 group metadata document; context is taken as every v2 reader takes it.

Source code in src/zarr_metadata/model/_validation.py
def is_group_metadata_v2(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV2GroupMetadataJSON]:
    """Whether `value` is a structurally-valid v2 group metadata document; `context` is taken as every v2 reader takes it."""
    return _is_canonical_json(value, finite=False) and not validate_group_metadata_v2(
        value, context=context
    )

is_group_metadata_v3

is_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV3GroupMetadataJSON]

Whether value is a v3 group document validate_group_metadata_v3 finds nothing wrong with, written with tuples.

Source code in src/zarr_metadata/model/_group.py
def is_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> TypeGuard[ZarrV3GroupMetadataJSON]:
    """Whether `value` is a v3 group document `validate_group_metadata_v3` finds nothing wrong with, written with tuples."""
    scope = CORE_AND_EXTENSIONS if context is None else context
    return is_canonical_json(value, finite=False) and not validate_group_metadata_v3(
        value, context=scope
    )

is_metadata_field_v3

is_metadata_field_v3(
    value: object,
) -> TypeGuard[ZarrV3MetadataFieldJSON]

Whether value is a v3 metadata field: a bare name as the spec names an extension, or a named config.

Source code in src/zarr_metadata/v3/_common.py
def is_metadata_field_v3(value: object) -> TypeGuard[ZarrV3MetadataFieldJSON]:
    """Whether `value` is a v3 metadata field: a bare name as the spec names an extension, or a named config."""
    if isinstance(value, str):
        return well_named(value)
    if not is_object(value) or not isinstance(value, dict):
        return False
    return is_canonical_json(value) and not validate_metadata_field_v3(value)

is_node_name_v3

is_node_name_v3(value: object) -> TypeGuard[NodeName]

Whether value is a v3 node name validate_node_name_v3 finds nothing wrong with.

Source code in src/zarr_metadata/v3/_hierarchy.py
def is_node_name_v3(value: object) -> TypeGuard[NodeName]:
    """Whether `value` is a v3 node name `validate_node_name_v3` finds nothing wrong with."""
    return len(validate_node_name_v3(value)) == 0

is_node_path_v3

is_node_path_v3(value: object) -> TypeGuard[NodePath]

Whether value is a v3 node path validate_node_path_v3 finds nothing wrong with.

Source code in src/zarr_metadata/v3/_hierarchy.py
def is_node_path_v3(value: object) -> TypeGuard[NodePath]:
    """Whether `value` is a v3 node path `validate_node_path_v3` finds nothing wrong with."""
    return len(validate_node_path_v3(value)) == 0

node_metadata_from_json_v3

node_metadata_from_json_v3(
    data: object, *, context: Context | None = None
) -> ZarrV3NodeMetadata

The model of data, a v3 zarr.json read in context, as the node its node_type says.

What ZarrV3ArrayMetadata.from_json or ZarrV3GroupMetadata.from_json gives, as pydantic's TypeAdapter validates a discriminated union. MetadataValidationError with every problem read_node_metadata_v3 finds, a node_type that says neither among them.

Source code in src/zarr_metadata/model/_group.py
def node_metadata_from_json_v3(
    data: object, *, context: Context | None = None
) -> ZarrV3NodeMetadata:
    """The model of `data`, a v3 `zarr.json` read in `context`, as the node its `node_type` says.

    What `ZarrV3ArrayMetadata.from_json` or `ZarrV3GroupMetadata.from_json`
    gives, as pydantic's `TypeAdapter` validates a discriminated union.
    `MetadataValidationError` with every problem `read_node_metadata_v3`
    finds, a `node_type` that says neither among them.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    reading = read_node_metadata_v3(data, context=scope)
    if reading.metadata is None:
        raise MetadataValidationError(reading.problems)
    return reading.metadata

node_metadata_from_key_value_v3

node_metadata_from_key_value_v3(
    mapping: Mapping[StoreKey, bytes],
    *,
    context: Context | None = None,
) -> ZarrV3NodeMetadata

The model of the document at zarr.json in mapping, read in context as the node its node_type says, as node_metadata_from_json_v3 reads one.

MetadataValidationError when the key is missing, its bytes are not JSON, or the document is not a valid array or group.

Source code in src/zarr_metadata/model/_group.py
def node_metadata_from_key_value_v3(
    mapping: Mapping[StoreKey, bytes], *, context: Context | None = None
) -> ZarrV3NodeMetadata:
    """The model of the document at `zarr.json` in `mapping`, read in `context` as the node its `node_type` says, as `node_metadata_from_json_v3` reads one.

    `MetadataValidationError` when the key is missing, its bytes are not
    JSON, or the document is not a valid array or group.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    # An array's document and a group's are both at `zarr.json`.
    document = load_store_json(mapping, ZARR_V3_GROUP_METADATA_STORE_KEY)
    return node_metadata_from_json_v3(document, context=scope)

node_metadata_json_schema_v3

node_metadata_json_schema_v3(
    *, context: Context | None = None
) -> JSONSchema

The JSON Schema of a v3 zarr.json read in context: an array document or a group document, as validate_node_metadata_v3 reads one, but for the rules.

For an editor that validates a zarr.json as it is written, or a validator in another language. JSON Schema draft 2020-12, as json_schema writes one. Each extension point is a field as field_json_schema writes one in context: one a definition in scope reads, or a name none of them claims. The fill value is the JSON shape the data type's definition declares for one -- an int8's an integer in [-128, 127] -- when the document names a data type in scope. A group's consolidated_metadata holds array and group documents, by path; a null one, which a zarr-python 3.0.x bug wrote, is refused, as the validator refuses it. Each document is in $defs under the name of its TypedDict: ZarrV3ArrayMetadataJSON is an array's alone.

A JSON Schema says what each member is, and what the rules say of members read together is not in it: one dimension name per dimension of the shape, a chunk grid that fits the shape, codecs in the order a pipeline takes them, each against the chunk it is handed, the hierarchy the documents of consolidated metadata make below their group, and what a definition's rules say. So a document it accepts may still have a problem, and a JSON document validate_node_metadata_v3 finds none with, it accepts. A validator reads JSON as a parser gives it, arrays as lists: a model's to_json writes tuples, which a Python validator does not take for arrays.

Source code in src/zarr_metadata/model/_json_schema.py
def node_metadata_json_schema_v3(*, context: Context | None = None) -> JSONSchema:
    """The JSON Schema of a v3 `zarr.json` read in `context`: an array document or a group document, as `validate_node_metadata_v3` reads one, but for the rules.

    For an editor that validates a `zarr.json` as it is written, or a
    validator in another language. JSON Schema draft 2020-12, as
    `json_schema` writes one. Each extension point is a field as
    `field_json_schema` writes one in `context`: one a definition in scope
    reads, or a name none of them claims. The fill value is the JSON shape
    the data type's definition declares for one -- an `int8`'s an integer
    in [-128, 127] -- when the document names a data type in scope. A
    group's `consolidated_metadata` holds array and group documents, by
    path; a `null` one, which a zarr-python 3.0.x bug wrote, is refused, as
    the validator refuses it. Each document is in `$defs` under the name of its
    TypedDict: `ZarrV3ArrayMetadataJSON` is an array's alone.

    A JSON Schema says what each member is, and what the rules say of
    members read together is not in it: one dimension name per dimension
    of the shape, a chunk grid that fits the shape, codecs in the order a
    pipeline takes them, each against the chunk it is handed, the
    hierarchy the documents of consolidated metadata make below their
    group, and what a definition's `rules` say. So a document it accepts may still have a
    problem, and a JSON document `validate_node_metadata_v3` finds none
    with, it accepts. A validator reads JSON as a parser gives it, arrays
    as lists: a model's `to_json` writes tuples, which a Python validator
    does not take for arrays.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    schemas = Schemas(_documents(scope))
    array = schemas.of(ZarrV3ArrayMetadataJSON)
    group = schemas.of(ZarrV3GroupMetadataJSON)
    return schemas.document({"anyOf": [array, group]})

parse_array_metadata_v2

parse_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2ArrayMetadataJSON

value as ZarrV2ArrayMetadataJSON, read in context, CORE_V2 when none is given; MetadataValidationError with every problem.

Source code in src/zarr_metadata/model/_validation.py
def parse_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2ArrayMetadataJSON:
    """`value` as `ZarrV2ArrayMetadataJSON`, read in `context`, `CORE_V2` when none is given; `MetadataValidationError` with every problem."""
    problems = validate_array_metadata_v2(value, context=context)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    return cast("ZarrV2ArrayMetadataJSON", arrays_to_tuples(value))

parse_array_metadata_v3

parse_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3ArrayMetadataJSON

Return value as ZarrV3ArrayMetadataJSON, or raise MetadataValidationError.

Source code in src/zarr_metadata/model/_validation.py
def parse_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3ArrayMetadataJSON:
    """Return `value` as `ZarrV3ArrayMetadataJSON`, or raise `MetadataValidationError`."""
    scope = CORE_AND_EXTENSIONS if context is None else context
    problems = validate_array_metadata_v3(value, context=scope)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    return cast("ZarrV3ArrayMetadataJSON", arrays_to_tuples(value))

parse_group_metadata_v2

parse_group_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2GroupMetadataJSON

value narrowed to ZarrV2GroupMetadataJSON, or MetadataValidationError; context is taken as every v2 reader takes it.

Source code in src/zarr_metadata/model/_validation.py
def parse_group_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2GroupMetadataJSON:
    """`value` narrowed to `ZarrV2GroupMetadataJSON`, or `MetadataValidationError`; `context` is taken as every v2 reader takes it."""
    problems = validate_group_metadata_v2(value, context=context)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    return cast(ZarrV2GroupMetadataJSON, arrays_to_tuples(value))

parse_group_metadata_v3

parse_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3GroupMetadataJSON

Return value narrowed to ZarrV3GroupMetadataJSON, or raise MetadataValidationError.

Source code in src/zarr_metadata/model/_group.py
def parse_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3GroupMetadataJSON:
    """Return `value` narrowed to `ZarrV3GroupMetadataJSON`, or raise `MetadataValidationError`."""
    scope = CORE_AND_EXTENSIONS if context is None else context
    problems = validate_group_metadata_v3(value, context=scope)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    return cast("ZarrV3GroupMetadataJSON", arrays_to_tuples(documents_for(value)))

parse_metadata_field_v3

parse_metadata_field_v3(
    value: object,
) -> ZarrV3MetadataFieldJSON

Return value narrowed to ZarrV3MetadataFieldJSON, or raise MetadataValidationError.

Source code in src/zarr_metadata/v3/_common.py
def parse_metadata_field_v3(value: object) -> ZarrV3MetadataFieldJSON:
    """Return `value` narrowed to `ZarrV3MetadataFieldJSON`, or raise `MetadataValidationError`."""
    problems = validate_metadata_field_v3(value)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    return cast(ZarrV3MetadataFieldJSON, arrays_to_tuples(value))

parse_node_name_v3

parse_node_name_v3(value: object) -> NodeName

value as a NodeName, or MetadataValidationError with every reason it is not one.

Source code in src/zarr_metadata/v3/_hierarchy.py
def parse_node_name_v3(value: object) -> NodeName:
    """`value` as a `NodeName`, or `MetadataValidationError` with every reason it is not one."""
    problems = validate_node_name_v3(value)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    return NodeName(cast("str", value))

parse_node_path_v3

parse_node_path_v3(value: object) -> NodePath

value as a NodePath, or MetadataValidationError with every reason it is not one.

Source code in src/zarr_metadata/v3/_hierarchy.py
def parse_node_path_v3(value: object) -> NodePath:
    """`value` as a `NodePath`, or `MetadataValidationError` with every reason it is not one."""
    problems = validate_node_path_v3(value)
    if len(problems) != 0:
        raise MetadataValidationError(problems)
    return NodePath(cast("str", value))

read_array_metadata_v2

read_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2ArrayMetadataReading

value, a v2 array document, as context read it, CORE_V2 when none is given, whatever it holds.

Everything a read finds, in one: the dtype, the compressor and each filter as the scope read them -- AcceptedField by the definition that claims the typestr or id, UnclaimedField, or RefusedField -- every problem validate_array_metadata_v2 finds, and, when there is none, the document's model.

Source code in src/zarr_metadata/model/_array.py
def read_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2ArrayMetadataReading:
    """`value`, a v2 array document, as `context` read it, `CORE_V2` when none is given, whatever it holds.

    Everything a read finds, in one: the dtype, the compressor and each
    filter as the scope read them -- `AcceptedField` by the definition that claims
    the typestr or id, `UnclaimedField`, or `RefusedField` -- every problem
    `validate_array_metadata_v2` finds, and, when there is none, the
    document's model.
    """
    scope = CORE_V2 if context is None else context
    reading, members = read_array_v2(value, scope)
    if members is None:
        return reading
    document = refined_object(value)
    if "dimension_separator" not in document:
        document = {**document, "dimension_separator": "."}
    model = ZarrV2ArrayMetadata._of(document, scope, reading, members)  # pyright: ignore[reportPrivateUsage]
    return model.reading

read_array_metadata_v3

read_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3ArrayMetadataReading

value, a v3 array document, as context read it, whatever it holds.

Everything a read finds, in one: each extension point as context read it -- AcceptedField by the definition that claims its name, UnclaimedField, or RefusedField -- the chunks the codecs are handed, each codec with the chunk it is handed, every problem validate_array_metadata_v3 finds, and, when there is none, the document's model, holding the same reading. A policy over the fields, the core spec's alone, say, is a walk over its fields(). A value that is not an object holds no field.

Source code in src/zarr_metadata/model/_array.py
def read_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3ArrayMetadataReading:
    """`value`, a v3 array document, as `context` read it, whatever it holds.

    Everything a read finds, in one: each extension point as `context`
    read it -- `AcceptedField` by the definition that claims its name, `UnclaimedField`,
    or `RefusedField` -- the chunks the codecs are handed, each codec with the
    chunk it is handed, every problem `validate_array_metadata_v3` finds,
    and, when there is none, the document's model, holding the same
    reading. A policy over the fields, the core spec's alone, say, is a
    walk over its `fields()`. A value that is not an object holds no
    field.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    reading, members = read_array_v3(value, scope)
    if members is None:
        return reading
    document = refined_object(value)
    model = ZarrV3ArrayMetadata._of(document, scope, reading, members)  # pyright: ignore[reportPrivateUsage]
    return model.reading

read_group_metadata_v3

read_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3GroupMetadataReading

value, a v3 group document, as context read it, whatever it holds.

Everything a read finds, in one: each document its consolidated metadata holds, read once, as read_array_metadata_v3 and this read one; every problem validate_group_metadata_v3 finds; and, when there is none, the group's model, whose consolidated metadata holds the models of those documents, which their readings hold too. A value that is not an object holds nothing.

Source code in src/zarr_metadata/model/_group.py
def read_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3GroupMetadataReading:
    """`value`, a v3 group document, as `context` read it, whatever it holds.

    Everything a read finds, in one: each document its consolidated
    metadata holds, read once, as `read_array_metadata_v3` and this read
    one; every problem `validate_group_metadata_v3` finds; and, when there
    is none, the group's model, whose consolidated metadata holds the
    models of those documents, which their readings hold too. A value that
    is not an object holds nothing.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    reading, members = read_group_v3(value, scope)
    if members is None:
        return reading
    return _with_models(reading, members, documents_for(value), scope)

read_node_metadata_v3

read_node_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3NodeMetadataReading

value, a v3 zarr.json, read in context as the node its node_type says it is.

The node type is the tag of a union, as pydantic's discriminator and zod's discriminated union read one: an array is read as read_array_metadata_v3 reads it, a group as read_group_metadata_v3 does, and a document that says neither, or is not an object, is ZarrV3UnknownNodeReading, with the problems, its zarr_format's among them. So no caller reads node_type from JSON it has not read, and a document of another format says it is not v3.

Source code in src/zarr_metadata/model/_group.py
def read_node_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3NodeMetadataReading:
    """`value`, a v3 `zarr.json`, read in `context` as the node its `node_type` says it is.

    The node type is the tag of a union, as pydantic's discriminator and
    zod's discriminated union read one: an array is read as
    `read_array_metadata_v3` reads it, a group as `read_group_metadata_v3`
    does, and a document that says neither, or is not an object, is
    `ZarrV3UnknownNodeReading`, with the problems, its `zarr_format`'s
    among them. So no caller reads `node_type` from JSON it has not read,
    and a document of another format says it is not v3.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    node_type, problems = _node_type(value)
    if node_type == "array":
        return read_array_metadata_v3(value, context=scope)
    if node_type == "group":
        return read_group_metadata_v3(value, context=scope)
    return ZarrV3UnknownNodeReading(problems)

read_repaired_consolidated_metadata_v2

read_repaired_consolidated_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2RepairedConsolidatedMetadataReading

value, a v2 .zmetadata, read in context as ZarrV2ConsolidatedMetadata reads it, once repair_consolidated_metadata_v2 has undone each known writer bug in it.

For a reader of stores other writers made, which asks for repairs by calling this rather than the strict model. Whatever no repair applies to is read as it is, and reported as the strict read reports it.

Source code in src/zarr_metadata/model/_repair.py
def read_repaired_consolidated_metadata_v2(
    value: object, *, context: Context | None = None
) -> ZarrV2RepairedConsolidatedMetadataReading:
    """`value`, a v2 `.zmetadata`, read in `context` as `ZarrV2ConsolidatedMetadata` reads it, once `repair_consolidated_metadata_v2` has undone each known writer bug in it.

    For a reader of stores other writers made, which asks for repairs by
    calling this rather than the strict model. Whatever no repair applies
    to is read as it is, and reported as the strict read reports it.
    """
    scope = CORE_V2 if context is None else context
    repaired, repairs = repair_consolidated_metadata_v2(value)
    try:
        model: ZarrV2ConsolidatedMetadata | None = ZarrV2ConsolidatedMetadata(repaired, scope)
    except MetadataValidationError as error:
        return ZarrV2RepairedConsolidatedMetadataReading(error.problems, None, repairs)
    return ZarrV2RepairedConsolidatedMetadataReading((), model, repairs)

read_repaired_node_metadata_v3

read_repaired_node_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3RepairedNodeMetadataReading

value, a v3 zarr.json, read in context as read_node_metadata_v3 reads it, once repair_node_metadata_v3 has undone each known writer bug in it.

For a reader of stores other writers made, which asks for repairs by calling this rather than read_node_metadata_v3. Whatever no repair applies to is read as it is, and reported as read_node_metadata_v3 reports it.

Source code in src/zarr_metadata/model/_repair.py
def read_repaired_node_metadata_v3(
    value: object, *, context: Context | None = None
) -> ZarrV3RepairedNodeMetadataReading:
    """`value`, a v3 `zarr.json`, read in `context` as `read_node_metadata_v3` reads it, once `repair_node_metadata_v3` has undone each known writer bug in it.

    For a reader of stores other writers made, which asks for repairs by
    calling this rather than `read_node_metadata_v3`. Whatever no repair
    applies to is read as it is, and reported as `read_node_metadata_v3`
    reports it.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    repaired, repairs = repair_node_metadata_v3(value)
    return ZarrV3RepairedNodeMetadataReading(
        read_node_metadata_v3(repaired, context=scope), repairs
    )

repair_consolidated_metadata_v2

repair_consolidated_metadata_v2(
    value: object,
) -> tuple[object, tuple[Repair, ...]]

value, a v2 .zmetadata, with each known writer bug in it undone, and what was changed.

zarr-python 3.x writes a consolidated_metadata member into each .zgroup entry below the root, which is removed; the root's is left, since no writer puts one there. What no repair applies to is left as it is, and value is not changed; a document with none of the bugs is given back, and no repairs.

Source code in src/zarr_metadata/model/_repair.py
def repair_consolidated_metadata_v2(value: object) -> tuple[object, tuple[Repair, ...]]:
    """`value`, a v2 `.zmetadata`, with each known writer bug in it undone, and what was changed.

    zarr-python 3.x writes a `consolidated_metadata` member into each
    `.zgroup` entry below the root, which is removed; the root's is left,
    since no writer puts one there. What no repair
    applies to is left as it is, and `value` is not changed; a document
    with none of the bugs is given back, and no repairs.
    """
    if not is_json_object(value):
        return value, ()
    document = value
    entries = document.get("metadata")
    if not is_object(entries):
        return value, ()
    repairs: list[Repair] = []
    held: dict[object, object] = {}
    for key, entry in entries.items():
        if isinstance(key, str):
            path, _, name = key.rpartition("/")
            # Below the root only: no writer puts the member in the root's .zgroup.
            if name == ZARR_V2_GROUP_METADATA_STORE_KEY and path.strip("/") != "":
                entry = _without_consolidated_metadata(entry, ("metadata", key), repairs)
        held[key] = entry
    if len(repairs) == 0:
        return value, ()
    return {**document, "metadata": held}, tuple(repairs)

repair_node_metadata_v3

repair_node_metadata_v3(
    value: object,
) -> tuple[object, tuple[Repair, ...]]

value, a v3 zarr.json, with each known writer bug in it undone, and what was changed.

Each document consolidated metadata holds is repaired too. What no repair applies to is left as it is, and value is not changed: a repaired document is a new one, sharing what it did not change with value. Repairing a document with none of the bugs gives it back, and no repairs.

Source code in src/zarr_metadata/model/_repair.py
def repair_node_metadata_v3(value: object) -> tuple[object, tuple[Repair, ...]]:
    """`value`, a v3 `zarr.json`, with each known writer bug in it undone, and what was changed.

    Each document consolidated metadata holds is repaired too. What no
    repair applies to is left as it is, and `value` is not changed: a
    repaired document is a new one, sharing what it did not change with
    `value`. Repairing a document with none of the bugs gives it back,
    and no repairs.
    """
    return _repaired(value, ())

validate_array_metadata_v2

validate_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]

Every reason value is not a valid v2 array document, read in context, CORE_V2 when none is given.

dtype, compressor and filters are read in the scope: a dtype or codec the scope refuses is a problem, one it does not claim is not; fill_value is judged by the dtype the scope read.

Source code in src/zarr_metadata/model/_validation.py
def validate_array_metadata_v2(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]:
    """Every reason `value` is not a valid v2 array document, read in `context`, `CORE_V2` when none is given.

    `dtype`, `compressor` and `filters` are read in the scope: a dtype or
    codec the scope refuses is a problem, one it does not claim is not;
    `fill_value` is judged by the dtype the scope read.
    """
    return read_array_v2(value, CORE_V2 if context is None else context)[0].problems

validate_array_metadata_v3

validate_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]

Return every reason value is not a valid v3 array document.

Its structure, and each extension point read through the definition that claims its name in context: a gzip level out of range, a key a codec's configuration does not declare. The fill value is judged against the data type as context read it -- an int8 fill value of 300 -- and the chunk grid against the shape: a regular grid with a chunk length for each of two dimensions, over an array of three. The codecs are read as a pipeline: in order, each judged against the chunk it is handed -- a transpose whose order has another number of axes, a shard its inner chunks do not divide -- and a shard's inner and index codecs too. A name nothing in context claims is left unjudged, with any fill value of it, and a codec of that name leaves the codec after it handed a chunk nothing is known of. Unknown top-level keys are allowed (they map to extra_fields); a reader must understand each one that does not say must_understand: false, which the model reports as must_understand_fields. These are the problems of read_array_metadata_v3, which holds what was read to find them.

Source code in src/zarr_metadata/model/_validation.py
def validate_array_metadata_v3(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]:
    """Return every reason `value` is not a valid v3 array document.

    Its structure, and each extension point read through the definition
    that claims its name in `context`: a gzip `level` out of range, a key a
    codec's configuration does not declare. The fill value is judged
    against the data type as `context` read it -- an `int8` fill value of
    300 -- and the chunk grid against the shape: a regular grid with a
    chunk length for each of two dimensions, over an array of three. The
    codecs are read as a pipeline: in order, each judged against the chunk
    it is handed -- a `transpose` whose `order` has another number of
    axes, a shard its inner chunks do not divide -- and a shard's inner
    and index codecs too.
    A name nothing in `context` claims is left unjudged, with any fill
    value of it, and a codec of that name leaves the codec after it
    handed a chunk nothing is known of. Unknown top-level keys are
    allowed (they map to `extra_fields`); a reader must understand each
    one that does not say `must_understand: false`, which the model
    reports as `must_understand_fields`. These are the `problems` of
    `read_array_metadata_v3`, which holds what was read to find them.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    return read_array_v3(value, scope)[0].problems

validate_group_metadata_v2

validate_group_metadata_v2(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]

Return every reason value is not a structurally-valid v2 group doc.

Validates the in-memory merged form: the .zgroup fields plus an optional attributes mapping folded in from .zattrs. A group holds no field a scope reads; context is taken as every v2 reader takes it.

Source code in src/zarr_metadata/model/_validation.py
def validate_group_metadata_v2(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]:
    """Return every reason `value` is not a structurally-valid v2 group doc.

    Validates the in-memory merged form: the `.zgroup` fields plus an
    optional `attributes` mapping folded in from `.zattrs`. A group holds
    no field a scope reads; `context` is taken as every v2 reader takes it.
    """
    if not is_object(value):
        return not_an_object(value)
    doc = value
    problems: list[ValidationProblem] = list(missing_keys(GROUP_METADATA_REQUIRED_KEYS_V2, doc))
    problems.extend(unexpected_keys(GROUP_METADATA_STANDARD_KEYS_V2, doc))
    problems.extend(check_literal(doc, "zarr_format", 2))
    if "attributes" in doc:
        problems.extend(validate_attributes(doc["attributes"]))
    return with_input(problems, doc)

validate_group_metadata_v3

validate_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]

Return every reason value is not a valid v3 group document.

Unknown top-level keys are allowed (they map to extra_fields); a reader must understand each one that does not say must_understand: false, which the model reports as must_understand_fields. A consolidated_metadata member, if present, is validated too: its envelope, and each document it holds by its path, each array read as validate_array_metadata_v3 reads one, in context. These are the problems of read_group_metadata_v3, which holds what was read to find them.

Source code in src/zarr_metadata/model/_group.py
def validate_group_metadata_v3(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]:
    """Return every reason `value` is not a valid v3 group document.

    Unknown top-level keys are allowed (they map to `extra_fields`); a
    reader must understand each one that does not say `must_understand:
    false`, which the model reports as `must_understand_fields`. A
    `consolidated_metadata` member, if present, is validated too: its
    envelope, and each document it holds by its path, each array read as
    `validate_array_metadata_v3` reads one, in `context`. These are the
    `problems` of `read_group_metadata_v3`, which holds what was read to
    find them.
    """
    scope = CORE_AND_EXTENSIONS if context is None else context
    return read_group_v3(value, scope)[0].problems

validate_metadata_field_v3

validate_metadata_field_v3(
    value: object,
    *,
    allow_must_understand_false: bool = True,
) -> tuple[ValidationProblem, ...]

Return every reason value is not a v3 metadata field.

A metadata field is a bare name, or an envelope around a configuration whose members are JSON: an object of a name as the spec names an extension, a configuration that is an object of string keys, a boolean must_understand, and nothing else.

Source code in src/zarr_metadata/v3/_common.py
def validate_metadata_field_v3(
    value: object, *, allow_must_understand_false: bool = True
) -> tuple[ValidationProblem, ...]:
    """Return every reason `value` is not a v3 metadata field.

    A metadata field is a bare name, or an envelope around a configuration
    whose members are JSON: an object of a `name` as the spec names an
    extension, a `configuration` that is an object of string keys, a
    boolean `must_understand`, and nothing else.
    """
    envelope = envelope_problems(value, allow_must_understand_false=allow_must_understand_false)
    return with_input((*envelope, *_configuration_json_problems(value)), value)

validate_node_metadata_v3

validate_node_metadata_v3(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]

Every reason value is not a valid v3 zarr.json: those validate_array_metadata_v3 or validate_group_metadata_v3 finds in the node its node_type says it is, or why it says neither.

Source code in src/zarr_metadata/model/_group.py
def validate_node_metadata_v3(
    value: object, *, context: Context | None = None
) -> tuple[ValidationProblem, ...]:
    """Every reason `value` is not a valid v3 `zarr.json`: those `validate_array_metadata_v3` or `validate_group_metadata_v3` finds in the node its `node_type` says it is, or why it says neither."""
    scope = CORE_AND_EXTENSIONS if context is None else context
    return _read_node_v3(value, scope)[0].problems

validate_node_name_v3

validate_node_name_v3(
    value: object,
) -> tuple[ValidationProblem, ...]

Every reason value is not a v3 node name, said in one invalid_value, or an invalid_type for what is not a string.

Source code in src/zarr_metadata/v3/_hierarchy.py
def validate_node_name_v3(value: object) -> tuple[ValidationProblem, ...]:
    """Every reason `value` is not a v3 node name, said in one `invalid_value`, or an `invalid_type` for what is not a string."""
    if not isinstance(value, str):
        return with_input(_string_problems(value, "a node name"), value)
    faults = name_faults(value)
    if len(faults) == 0:
        return ()
    message = f"expected a node name, got {shown(value)}, which {said(faults)}"
    return with_input((ValidationProblem((), message, "invalid_value"),), value)

validate_node_path_v3

validate_node_path_v3(
    value: object,
) -> tuple[ValidationProblem, ...]

Every reason value is not a v3 node path, said in one invalid_value, or an invalid_type for what is not a string.

Source code in src/zarr_metadata/v3/_hierarchy.py
def validate_node_path_v3(value: object) -> tuple[ValidationProblem, ...]:
    """Every reason `value` is not a v3 node path, said in one `invalid_value`, or an `invalid_type` for what is not a string."""
    if not isinstance(value, str):
        return with_input(_string_problems(value, "a node path"), value)
    faults = path_faults(value)
    if len(faults) == 0:
        return ()
    message = f"expected a node path, got {shown(value)}, which {said(faults)}"
    return with_input((ValidationProblem((), message, "invalid_value"),), value)