Skip to content

zarr_metadata.v3.data_type

zarr_metadata.v3.data_type

Zarr v3 data type spec types.

Each v3 data type has its own submodule:

  • Core primitives: bool, int8/16/32/64, uint8/16/32/64, float16/32/64, complex64/128, raw (for r<N>)
  • zarr-extensions: bytes, string, numpy_datetime64, numpy_timedelta64, struct

The two canonical types per dtype are re-exported here:

  • <X>DataTypeName -- the literal type of the dtype's data_type string (or, for named-config dtypes, the literal value of their name field)
  • <X>FillValue -- the permitted JSON shape of the fill_value field

Named-config dtypes (numpy_datetime64, numpy_timedelta64, struct) also expose their envelope TypedDict here. For configuration TypedDicts, branded HexFloat<N> / Base64Bytes types, and the corresponding validator functions, import directly from the leaf submodule.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

zarr_metadata.v3.data_type.bool

Zarr v3 bool data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

BOOL_DATA_TYPE_NAME module-attribute

BOOL_DATA_TYPE_NAME: Final = 'bool'

The data_type value for the bool type.

BoolDataTypeName module-attribute

BoolDataTypeName = Literal['bool']

Literal type of the data_type field for bool.

BoolFillValue module-attribute

BoolFillValue = bool

Permitted JSON shape of the fill_value field for bool: a JSON boolean.

__all__ module-attribute

__all__ = [
    "BOOL_DATA_TYPE_NAME",
    "BoolDataType",
    "BoolDataTypeName",
    "BoolFillValue",
]

BoolDataType dataclass

Bases: DataTypeEntity

The bool data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/bool.py
@dataclass(frozen=True)
class BoolDataType(DataTypeEntity):
    """The `bool` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "single_byte"
    twos_complement: ClassVar[bool] = False
    identifier: ClassVar[str] = BOOL_DATA_TYPE_NAME

    def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
        if not isinstance(value, bool):
            return problem(loc, f"expected a boolean, got {value!r}", "invalid_value")
        return ()

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = BOOL_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'single_byte'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/bool.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    if not isinstance(value, bool):
        return problem(loc, f"expected a boolean, got {value!r}", "invalid_value")
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.int8

Zarr v3 int8 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

INT8_DATA_TYPE_NAME module-attribute

INT8_DATA_TYPE_NAME: Final = 'int8'

The data_type value for the int8 type.

Int8DataTypeName module-attribute

Int8DataTypeName = Literal['int8']

Literal type of the data_type field for int8.

Int8FillValue module-attribute

Int8FillValue = int

Permitted JSON shape of the fill_value field for int8: a JSON integer in [-128, 127].

__all__ module-attribute

__all__ = [
    "INT8_DATA_TYPE_NAME",
    "Int8DataType",
    "Int8DataTypeName",
    "Int8FillValue",
]

Int8DataType dataclass

Bases: IntegerDataType

The int8 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/int8.py
@dataclass(frozen=True)
class Int8DataType(IntegerDataType):
    """The `int8` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "single_byte"
    bounds: ClassVar[tuple[int, int]] = (-128, 127)
    identifier: ClassVar[str] = INT8_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (-128, 127)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = INT8_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'single_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.int16

Zarr v3 int16 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

INT16_DATA_TYPE_NAME module-attribute

INT16_DATA_TYPE_NAME: Final = 'int16'

The data_type value for the int16 type.

Int16DataTypeName module-attribute

Int16DataTypeName = Literal['int16']

Literal type of the data_type field for int16.

Int16FillValue module-attribute

Int16FillValue = int

Permitted JSON shape of the fill_value field for int16: a JSON integer in [-32768, 32767].

__all__ module-attribute

__all__ = [
    "INT16_DATA_TYPE_NAME",
    "Int16DataType",
    "Int16DataTypeName",
    "Int16FillValue",
]

Int16DataType dataclass

Bases: IntegerDataType

The int16 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/int16.py
@dataclass(frozen=True)
class Int16DataType(IntegerDataType):
    """The `int16` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    bounds: ClassVar[tuple[int, int]] = (-32768, 32767)
    identifier: ClassVar[str] = INT16_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (-32768, 32767)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = INT16_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.int32

Zarr v3 int32 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

INT32_DATA_TYPE_NAME module-attribute

INT32_DATA_TYPE_NAME: Final = 'int32'

The data_type value for the int32 type.

Int32DataTypeName module-attribute

Int32DataTypeName = Literal['int32']

Literal type of the data_type field for int32.

Int32FillValue module-attribute

Int32FillValue = int

Permitted JSON shape of the fill_value field for int32: a JSON integer in [-231, 231 - 1].

__all__ module-attribute

__all__ = [
    "INT32_DATA_TYPE_NAME",
    "Int32DataType",
    "Int32DataTypeName",
    "Int32FillValue",
]

Int32DataType dataclass

Bases: IntegerDataType

The int32 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/int32.py
@dataclass(frozen=True)
class Int32DataType(IntegerDataType):
    """The `int32` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    bounds: ClassVar[tuple[int, int]] = (-2147483648, 2147483647)
    identifier: ClassVar[str] = INT32_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (-2147483648, 2147483647)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = INT32_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.int64

Zarr v3 int64 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

INT64_DATA_TYPE_NAME module-attribute

INT64_DATA_TYPE_NAME: Final = 'int64'

The data_type value for the int64 type.

Int64DataTypeName module-attribute

Int64DataTypeName = Literal['int64']

Literal type of the data_type field for int64.

Int64FillValue module-attribute

Int64FillValue = int

Permitted JSON shape of the fill_value field for int64: a JSON integer in [-263, 263 - 1].

__all__ module-attribute

__all__ = [
    "INT64_DATA_TYPE_NAME",
    "Int64DataType",
    "Int64DataTypeName",
    "Int64FillValue",
]

Int64DataType dataclass

Bases: IntegerDataType

The int64 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/int64.py
@dataclass(frozen=True)
class Int64DataType(IntegerDataType):
    """The `int64` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    bounds: ClassVar[tuple[int, int]] = (-9223372036854775808, 9223372036854775807)
    identifier: ClassVar[str] = INT64_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (
    -9223372036854775808,
    9223372036854775807,
)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = INT64_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.uint8

Zarr v3 uint8 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

UINT8_DATA_TYPE_NAME module-attribute

UINT8_DATA_TYPE_NAME: Final = 'uint8'

The data_type value for the uint8 type.

Uint8DataTypeName module-attribute

Uint8DataTypeName = Literal['uint8']

Literal type of the data_type field for uint8.

Uint8FillValue module-attribute

Uint8FillValue = int

Permitted JSON shape of the fill_value field for uint8: a JSON integer in [0, 255].

__all__ module-attribute

__all__ = [
    "UINT8_DATA_TYPE_NAME",
    "Uint8DataType",
    "Uint8DataTypeName",
    "Uint8FillValue",
]

Uint8DataType dataclass

Bases: IntegerDataType

The uint8 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/uint8.py
@dataclass(frozen=True)
class Uint8DataType(IntegerDataType):
    """The `uint8` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "single_byte"
    bounds: ClassVar[tuple[int, int]] = (0, 255)
    identifier: ClassVar[str] = UINT8_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (0, 255)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = UINT8_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'single_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.uint16

Zarr v3 uint16 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

UINT16_DATA_TYPE_NAME module-attribute

UINT16_DATA_TYPE_NAME: Final = 'uint16'

The data_type value for the uint16 type.

Uint16DataTypeName module-attribute

Uint16DataTypeName = Literal['uint16']

Literal type of the data_type field for uint16.

Uint16FillValue module-attribute

Uint16FillValue = int

Permitted JSON shape of the fill_value field for uint16: a JSON integer in [0, 65535].

__all__ module-attribute

__all__ = [
    "UINT16_DATA_TYPE_NAME",
    "Uint16DataType",
    "Uint16DataTypeName",
    "Uint16FillValue",
]

Uint16DataType dataclass

Bases: IntegerDataType

The uint16 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/uint16.py
@dataclass(frozen=True)
class Uint16DataType(IntegerDataType):
    """The `uint16` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    bounds: ClassVar[tuple[int, int]] = (0, 65535)
    identifier: ClassVar[str] = UINT16_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (0, 65535)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = UINT16_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.uint32

Zarr v3 uint32 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

UINT32_DATA_TYPE_NAME module-attribute

UINT32_DATA_TYPE_NAME: Final = 'uint32'

The data_type value for the uint32 type.

Uint32DataTypeName module-attribute

Uint32DataTypeName = Literal['uint32']

Literal type of the data_type field for uint32.

Uint32FillValue module-attribute

Uint32FillValue = int

Permitted JSON shape of the fill_value field for uint32: a JSON integer in [0, 2**32 - 1].

__all__ module-attribute

__all__ = [
    "UINT32_DATA_TYPE_NAME",
    "Uint32DataType",
    "Uint32DataTypeName",
    "Uint32FillValue",
]

Uint32DataType dataclass

Bases: IntegerDataType

The uint32 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/uint32.py
@dataclass(frozen=True)
class Uint32DataType(IntegerDataType):
    """The `uint32` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    bounds: ClassVar[tuple[int, int]] = (0, 4294967295)
    identifier: ClassVar[str] = UINT32_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (0, 4294967295)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = UINT32_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.uint64

Zarr v3 uint64 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

UINT64_DATA_TYPE_NAME module-attribute

UINT64_DATA_TYPE_NAME: Final = 'uint64'

The data_type value for the uint64 type.

Uint64DataTypeName module-attribute

Uint64DataTypeName = Literal['uint64']

Literal type of the data_type field for uint64.

Uint64FillValue module-attribute

Uint64FillValue = int

Permitted JSON shape of the fill_value field for uint64: a JSON integer in [0, 2**64 - 1].

__all__ module-attribute

__all__ = [
    "UINT64_DATA_TYPE_NAME",
    "Uint64DataType",
    "Uint64DataTypeName",
    "Uint64FillValue",
]

Uint64DataType dataclass

Bases: IntegerDataType

The uint64 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/uint64.py
@dataclass(frozen=True)
class Uint64DataType(IntegerDataType):
    """The `uint64` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    bounds: ClassVar[tuple[int, int]] = (0, 18446744073709551615)
    identifier: ClassVar[str] = UINT64_DATA_TYPE_NAME

bounds class-attribute

bounds: tuple[int, int] = (0, 18446744073709551615)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = UINT64_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = True

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    low, high = type(self).bounds
    if not is_integer(value):
        return problem(loc, f"expected an integer, got {value!r}", "invalid_value")
    if not low <= value <= high:
        return problem(
            loc, f"expected an integer in [{low}, {high}], got {value!r}", "invalid_value"
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.float16

Zarr v3 float16 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

CANONICAL_NAN_HEX_FLOAT16 module-attribute

CANONICAL_NAN_HEX_FLOAT16: Final = '0x7e00'

Canonical hex form of the float16 NaN sentinel "NaN".

Per spec (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L72-L74) the named "NaN" sentinel denotes the float with sign=0, the most significant mantissa bit set, and all other mantissa bits zero (the IEEE 754 default quiet NaN). Other NaN bit patterns must be encoded with the explicit hex-string form.

CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT16 module-attribute

CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT16: Final = '0xfc00'

Canonical hex form of the float16 "-Infinity" sentinel.

CANONICAL_POSITIVE_INFINITY_HEX_FLOAT16 module-attribute

CANONICAL_POSITIVE_INFINITY_HEX_FLOAT16: Final = '0x7c00'

Canonical hex form of the float16 "Infinity" sentinel.

FLOAT16_DATA_TYPE_NAME module-attribute

FLOAT16_DATA_TYPE_NAME: Final = 'float16'

The data_type value for the float16 type.

Float16DataTypeName module-attribute

Float16DataTypeName = Literal['float16']

Literal type of the data_type field for float16.

Float16FillValue module-attribute

Float16FillValue = (
    float | int | Float16SpecialFillValue | HexFloat16
)

Permitted JSON shape of the fill_value field for float16.

Either a JSON number, one of the named non-finite sentinels ("NaN", "Infinity", "-Infinity"), or a HexFloat16 (0xYYYY string encoding the unsigned-integer representation of the IEEE 754 value).

Float16SpecialFillValue module-attribute

Float16SpecialFillValue = Literal[
    "NaN", "Infinity", "-Infinity"
]

Named non-finite fill values permitted by the spec for IEEE 754 floats.

https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L63-L79

HexFloat16 module-attribute

HexFloat16 = NewType('HexFloat16', str)

A 6-character hex string (0x + 4 hex digits) encoding the unsigned-integer representation of a float16.

__all__ module-attribute

__all__ = [
    "CANONICAL_NAN_HEX_FLOAT16",
    "CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT16",
    "CANONICAL_POSITIVE_INFINITY_HEX_FLOAT16",
    "FLOAT16_DATA_TYPE_NAME",
    "Float16DataType",
    "Float16DataTypeName",
    "Float16FillValue",
    "Float16SpecialFillValue",
    "HexFloat16",
    "hex_float16",
]

Float16DataType dataclass

Bases: FloatDataType

The float16 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/float16.py
@dataclass(frozen=True)
class Float16DataType(FloatDataType):
    """The `float16` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    hex_parser: ClassVar[Callable[[str], object]] = staticmethod(hex_float16)
    largest: ClassVar[float | None] = 65504.0
    identifier: ClassVar[str] = FLOAT16_DATA_TYPE_NAME

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

hex_parser class-attribute

identifier class-attribute

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

largest class-attribute

largest: float | None = 65504.0

The largest finite magnitude this width holds, or None for float64.

None because a Python float is a float64, so no literal that reaches here can exceed it.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    if is_integer(value) or isinstance(value, float):
        largest = type(self).largest
        if largest is not None and abs(value) > largest:
            return problem(
                loc,
                f"expected a {type(self).identifier} value, got {value!r}",
                "invalid_value",
            )
        return ()
    if not isinstance(value, str):
        return problem(loc, f"expected a number or string, got {value!r}", "invalid_value")
    if value in FLOAT_SPECIALS:
        return ()
    try:
        type(self).hex_parser(value)
    except ValueError:
        return problem(
            loc,
            f"expected a number, one of 'NaN'/'Infinity'/'-Infinity', or a "
            f"{type(self).identifier} hex string, got {value!r}",
            "invalid_value",
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

hex_float16

hex_float16(value: str) -> HexFloat16

Validate value as a HexFloat16 and brand it.

Raises ValueError if value is not exactly 0x followed by 4 hex digits.

Source code in src/zarr_metadata/v3/data_type/float16.py
def hex_float16(value: str) -> HexFloat16:
    """Validate `value` as a HexFloat16 and brand it.

    Raises ValueError if `value` is not exactly `0x` followed by 4 hex
    digits.
    """
    if not _HEX_FLOAT16_RE.fullmatch(value):
        raise ValueError(f"Expected '0x' followed by 4 hex digits, got {value!r}")
    return HexFloat16(value)

zarr_metadata.v3.data_type.float32

Zarr v3 float32 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

CANONICAL_NAN_HEX_FLOAT32 module-attribute

CANONICAL_NAN_HEX_FLOAT32: Final = '0x7fc00000'

Canonical hex form of the float32 NaN sentinel "NaN".

Per spec (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L72-L74) the named "NaN" sentinel denotes the float with sign=0, the most significant mantissa bit set, and all other mantissa bits zero (the IEEE 754 default quiet NaN). Other NaN bit patterns must be encoded with the explicit hex-string form.

CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT32 module-attribute

CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT32: Final = (
    "0xff800000"
)

Canonical hex form of the float32 "-Infinity" sentinel.

CANONICAL_POSITIVE_INFINITY_HEX_FLOAT32 module-attribute

CANONICAL_POSITIVE_INFINITY_HEX_FLOAT32: Final = (
    "0x7f800000"
)

Canonical hex form of the float32 "Infinity" sentinel.

FLOAT32_DATA_TYPE_NAME module-attribute

FLOAT32_DATA_TYPE_NAME: Final = 'float32'

The data_type value for the float32 type.

Float32DataTypeName module-attribute

Float32DataTypeName = Literal['float32']

Literal type of the data_type field for float32.

Float32FillValue module-attribute

Float32FillValue = (
    float | int | Float32SpecialFillValue | HexFloat32
)

Permitted JSON shape of the fill_value field for float32.

Either a JSON number, one of the named non-finite sentinels ("NaN", "Infinity", "-Infinity"), or a HexFloat32 (0xYYYYYYYY string encoding the unsigned-integer representation of the IEEE 754 value).

Float32SpecialFillValue module-attribute

Float32SpecialFillValue = Literal[
    "NaN", "Infinity", "-Infinity"
]

Named non-finite fill values permitted by the spec for IEEE 754 floats.

https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L63-L79

HexFloat32 module-attribute

HexFloat32 = NewType('HexFloat32', str)

A 10-character hex string (0x + 8 hex digits) encoding the unsigned-integer representation of a float32.

__all__ module-attribute

__all__ = [
    "CANONICAL_NAN_HEX_FLOAT32",
    "CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT32",
    "CANONICAL_POSITIVE_INFINITY_HEX_FLOAT32",
    "FLOAT32_DATA_TYPE_NAME",
    "Float32DataType",
    "Float32DataTypeName",
    "Float32FillValue",
    "Float32SpecialFillValue",
    "HexFloat32",
    "hex_float32",
]

Float32DataType dataclass

Bases: FloatDataType

The float32 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/float32.py
@dataclass(frozen=True)
class Float32DataType(FloatDataType):
    """The `float32` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    hex_parser: ClassVar[Callable[[str], object]] = staticmethod(hex_float32)
    largest: ClassVar[float | None] = 3.4028235e38
    identifier: ClassVar[str] = FLOAT32_DATA_TYPE_NAME

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

hex_parser class-attribute

identifier class-attribute

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

largest class-attribute

largest: float | None = 3.4028235e+38

The largest finite magnitude this width holds, or None for float64.

None because a Python float is a float64, so no literal that reaches here can exceed it.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    if is_integer(value) or isinstance(value, float):
        largest = type(self).largest
        if largest is not None and abs(value) > largest:
            return problem(
                loc,
                f"expected a {type(self).identifier} value, got {value!r}",
                "invalid_value",
            )
        return ()
    if not isinstance(value, str):
        return problem(loc, f"expected a number or string, got {value!r}", "invalid_value")
    if value in FLOAT_SPECIALS:
        return ()
    try:
        type(self).hex_parser(value)
    except ValueError:
        return problem(
            loc,
            f"expected a number, one of 'NaN'/'Infinity'/'-Infinity', or a "
            f"{type(self).identifier} hex string, got {value!r}",
            "invalid_value",
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

hex_float32

hex_float32(value: str) -> HexFloat32

Validate value as a HexFloat32 and brand it.

Raises ValueError if value is not exactly 0x followed by 8 hex digits.

Source code in src/zarr_metadata/v3/data_type/float32.py
def hex_float32(value: str) -> HexFloat32:
    """Validate `value` as a HexFloat32 and brand it.

    Raises ValueError if `value` is not exactly `0x` followed by 8 hex
    digits.
    """
    if not _HEX_FLOAT32_RE.fullmatch(value):
        raise ValueError(f"Expected '0x' followed by 8 hex digits, got {value!r}")
    return HexFloat32(value)

zarr_metadata.v3.data_type.float64

Zarr v3 float64 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

CANONICAL_NAN_HEX_FLOAT64 module-attribute

CANONICAL_NAN_HEX_FLOAT64: Final = '0x7ff8000000000000'

Canonical hex form of the float64 NaN sentinel "NaN".

Per spec (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L72-L74) the named "NaN" sentinel denotes the float with sign=0, the most significant mantissa bit set, and all other mantissa bits zero (the IEEE 754 default quiet NaN). Other NaN bit patterns must be encoded with the explicit hex-string form.

CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT64 module-attribute

CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT64: Final = (
    "0xfff0000000000000"
)

Canonical hex form of the float64 "-Infinity" sentinel.

CANONICAL_POSITIVE_INFINITY_HEX_FLOAT64 module-attribute

CANONICAL_POSITIVE_INFINITY_HEX_FLOAT64: Final = (
    "0x7ff0000000000000"
)

Canonical hex form of the float64 "Infinity" sentinel.

FLOAT64_DATA_TYPE_NAME module-attribute

FLOAT64_DATA_TYPE_NAME: Final = 'float64'

The data_type value for the float64 type.

Float64DataTypeName module-attribute

Float64DataTypeName = Literal['float64']

Literal type of the data_type field for float64.

Float64FillValue module-attribute

Float64FillValue = (
    float | int | Float64SpecialFillValue | HexFloat64
)

Permitted JSON shape of the fill_value field for float64.

Either a JSON number, one of the named non-finite sentinels ("NaN", "Infinity", "-Infinity"), or a HexFloat64 (0xYYYYYYYYYYYYYYYY string encoding the unsigned-integer representation of the IEEE 754 value).

Float64SpecialFillValue module-attribute

Float64SpecialFillValue = Literal[
    "NaN", "Infinity", "-Infinity"
]

Named non-finite fill values permitted by the spec for IEEE 754 floats.

https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L63-L79

HexFloat64 module-attribute

HexFloat64 = NewType('HexFloat64', str)

An 18-character hex string (0x + 16 hex digits) encoding the unsigned-integer representation of a float64.

__all__ module-attribute

__all__ = [
    "CANONICAL_NAN_HEX_FLOAT64",
    "CANONICAL_NEGATIVE_INFINITY_HEX_FLOAT64",
    "CANONICAL_POSITIVE_INFINITY_HEX_FLOAT64",
    "FLOAT64_DATA_TYPE_NAME",
    "Float64DataType",
    "Float64DataTypeName",
    "Float64FillValue",
    "Float64SpecialFillValue",
    "HexFloat64",
    "hex_float64",
]

Float64DataType dataclass

Bases: FloatDataType

The float64 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/float64.py
@dataclass(frozen=True)
class Float64DataType(FloatDataType):
    """The `float64` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    hex_parser: ClassVar[Callable[[str], object]] = staticmethod(hex_float64)
    largest: ClassVar[float | None] = None
    identifier: ClassVar[str] = FLOAT64_DATA_TYPE_NAME

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

hex_parser class-attribute

identifier class-attribute

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

largest class-attribute

largest: float | None = None

The largest finite magnitude this width holds, or None for float64.

None because a Python float is a float64, so no literal that reaches here can exceed it.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    if is_integer(value) or isinstance(value, float):
        largest = type(self).largest
        if largest is not None and abs(value) > largest:
            return problem(
                loc,
                f"expected a {type(self).identifier} value, got {value!r}",
                "invalid_value",
            )
        return ()
    if not isinstance(value, str):
        return problem(loc, f"expected a number or string, got {value!r}", "invalid_value")
    if value in FLOAT_SPECIALS:
        return ()
    try:
        type(self).hex_parser(value)
    except ValueError:
        return problem(
            loc,
            f"expected a number, one of 'NaN'/'Infinity'/'-Infinity', or a "
            f"{type(self).identifier} hex string, got {value!r}",
            "invalid_value",
        )
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

hex_float64

hex_float64(value: str) -> HexFloat64

Validate value as a HexFloat64 and brand it.

Raises ValueError if value is not exactly 0x followed by 16 hex digits.

Source code in src/zarr_metadata/v3/data_type/float64.py
def hex_float64(value: str) -> HexFloat64:
    """Validate `value` as a HexFloat64 and brand it.

    Raises ValueError if `value` is not exactly `0x` followed by 16 hex
    digits.
    """
    if not _HEX_FLOAT64_RE.fullmatch(value):
        raise ValueError(f"Expected '0x' followed by 16 hex digits, got {value!r}")
    return HexFloat64(value)

zarr_metadata.v3.data_type.complex64

Zarr v3 complex64 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

COMPLEX64_DATA_TYPE_NAME module-attribute

COMPLEX64_DATA_TYPE_NAME: Final = 'complex64'

The data_type value for the complex64 type.

Complex64Component module-attribute

Complex64Component = Float32FillValue

One real or imaginary component of a complex64 fill value.

Same shape as a float32 fill value: a JSON number, a named sentinel, or a HexFloat32 string.

Complex64DataTypeName module-attribute

Complex64DataTypeName = Literal['complex64']

Literal type of the data_type field for complex64.

Complex64FillValue module-attribute

Complex64FillValue = tuple[
    Complex64Component, Complex64Component
]

Permitted JSON shape of the fill_value field for complex64.

A two-element JSON array [real, imag] where each component is a Complex64Component.

__all__ module-attribute

__all__ = [
    "COMPLEX64_DATA_TYPE_NAME",
    "Complex64Component",
    "Complex64DataType",
    "Complex64DataTypeName",
    "Complex64FillValue",
]

Complex64DataType dataclass

Bases: ComplexDataType

The complex64 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/complex64.py
@dataclass(frozen=True)
class Complex64DataType(ComplexDataType):
    """The `complex64` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    component: ClassVar[type[FloatDataType]] = Float32DataType
    identifier: ClassVar[str] = COMPLEX64_DATA_TYPE_NAME

component class-attribute

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    pair = as_sequence(value)
    if pair is None or len(pair) != 2:
        return problem(loc, f"expected a [real, imag] pair, got {value!r}", "invalid_value")
    component = type(self).component()
    return tuple(
        ValidationProblem(found.loc, f"invalid component: {found.message}", found.kind)
        for index, part in enumerate(pair)
        for found in component.fill_value_problems(part, (*loc, index))
    )

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.complex128

Zarr v3 complex128 data type.

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html

COMPLEX128_DATA_TYPE_NAME module-attribute

COMPLEX128_DATA_TYPE_NAME: Final = 'complex128'

The data_type value for the complex128 type.

Complex128Component module-attribute

Complex128Component = Float64FillValue

One real or imaginary component of a complex128 fill value.

Same shape as a float64 fill value: a JSON number, a named sentinel, or a HexFloat64 string.

Complex128DataTypeName module-attribute

Complex128DataTypeName = Literal['complex128']

Literal type of the data_type field for complex128.

Complex128FillValue module-attribute

Complex128FillValue = tuple[
    Complex128Component, Complex128Component
]

Permitted JSON shape of the fill_value field for complex128.

A two-element JSON array [real, imag] where each component is a Complex128Component.

__all__ module-attribute

__all__ = [
    "COMPLEX128_DATA_TYPE_NAME",
    "Complex128Component",
    "Complex128DataType",
    "Complex128DataTypeName",
    "Complex128FillValue",
]

Complex128DataType dataclass

Bases: ComplexDataType

The complex128 data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/complex128.py
@dataclass(frozen=True)
class Complex128DataType(ComplexDataType):
    """The `complex128` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    component: ClassVar[type[FloatDataType]] = Float64DataType
    identifier: ClassVar[str] = COMPLEX128_DATA_TYPE_NAME

component class-attribute

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    pair = as_sequence(value)
    if pair is None or len(pair) != 2:
        return problem(loc, f"expected a [real, imag] pair, got {value!r}", "invalid_value")
    component = type(self).component()
    return tuple(
        ValidationProblem(found.loc, f"invalid component: {found.message}", found.kind)
        for index, part in enumerate(pair)
        for found in component.fill_value_problems(part, (*loc, index))
    )

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.raw

Zarr v3 r<N> raw-bytes data type (parameterised by bit count).

The data_type value is a string of the form r<N> where N is a positive multiple of 8 (e.g. r8, r16, r24).

See https://zarr-specs.readthedocs.io/en/latest/v3/data-types/index.html (https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L46-L47; fill value: https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L97-L99)

RAW_BYTES_FAMILY module-attribute

RAW_BYTES_FAMILY: Final = 'r<N>'

Canonical key for the parameterized raw-bytes data type family.

Spelled as the spec writes the family; the angle brackets keep it unforgeable by a real name.

RAW_BYTES_NAME_PATTERN module-attribute

RAW_BYTES_NAME_PATTERN: Final = re.compile('^r([0-9]+)$')

The shape of a raw-bytes data type name, not its validity.

ASCII digits only: \d would also match every other Unicode decimal, so r16 would be read as sixteen bits and a genuine third-party name spelled that way would be folded into this family.

Matches every r<N> spelling including malformed ones (r0, r12), so that a misspelled member of this family is recognized as belonging to it and reported as a misspelling, rather than passing as an unknown third-party extension. raw_bytes_dtype_name applies the validity rule on top. Sole owner of this grammar: other modules match through it.

RawBytesDataTypeName module-attribute

RawBytesDataTypeName = NewType('RawBytesDataTypeName', str)

A spec-conformant r<N> raw-bytes name (e.g. "r8", "r16").

"raw bits, variable size given by *, limited to be a multiple of 8": https://github.com/zarr-developers/zarr-specs/blob/fc7dd9c9beb5a50b87f9b08b00bf50fc0048482f/docs/v3/data-types/index.rst#L46-L47

RawBytesFillValue module-attribute

RawBytesFillValue = tuple[int, ...]

Permitted JSON shape of the fill_value field for r<N>.

A JSON array of N/8 integers in [0, 255] (one per byte).

__all__ module-attribute

__all__ = [
    "RAW_BYTES_FAMILY",
    "RAW_BYTES_NAME_PATTERN",
    "RawBytesDataType",
    "RawBytesDataTypeName",
    "RawBytesFillValue",
    "raw_bytes_dtype_name",
]

RawBytesDataType dataclass

Bases: DataTypeEntity

An r<N> raw-bytes data type, coerced from its metadata.

One class for the whole family, because r8 and r4096 differ only in a number. That is why this is the one entity whose identifier is not a name any document carries: r<N> is a shape, not a spelling, and no real name can collide with it.

The spelling is kept rather than the bit count, so a document comes back out as it went in. r008 is a valid and distinct way of writing r8, and canonicalizing it away is not this package's call.

Source code in src/zarr_metadata/v3/data_type/raw.py
@dataclass(frozen=True)
class RawBytesDataType(DataTypeEntity):
    """An `r<N>` raw-bytes data type, coerced from its metadata.

    One class for the whole family, because `r8` and `r4096` differ only
    in a number. That is why this is the one entity whose `identifier` is
    not a name any document carries: `r<N>` is a shape, not a spelling,
    and no real name can collide with it.

    The spelling is kept rather than the bit count, so a document comes
    back out as it went in. `r008` is a valid and distinct way of writing
    `r8`, and canonicalizing it away is not this package's call.
    """

    data_type_name: str = "r8"

    scalar_storage: ClassVar[StorageClass] = "single_byte"
    twos_complement: ClassVar[bool] = False
    identifier: ClassVar[str] = RAW_BYTES_FAMILY

    @property
    def name(self) -> str:
        """The `r<N>` spelling this instance was read from."""
        return self.data_type_name

    @classmethod
    def accepts(cls, name: str) -> bool:
        """Every `r<N>` spelling, valid or not.

        A malformed member of the family is recognized as belonging to it
        and reported as malformed, rather than passing unjudged as some
        third party's extension.
        """
        return RAW_BYTES_NAME_PATTERN.fullmatch(name) is not None

    @classmethod
    def coerce(cls, value: object, context: object) -> Coerced[Self]:
        name, configuration, must_understand = named_configuration(value)
        if name is None or not cls.accepts(name):
            return None, problem((), "expected an 'r<N>' raw-bytes data type")
        found: tuple[ValidationProblem, ...] = ()
        if configuration is not None and len(configuration) != 0:
            # Survivable, as an unknown key is everywhere else: the name
            # still says everything this type is, so it is still read and
            # its fill values are still judged. Returning nothing here let
            # a stray key hide every other problem in the document.
            found = problem(("configuration",), "'r<N>' takes no configuration", "unknown_key")
        return cls(must_understand=must_understand, data_type_name=name), found

    def problems(self) -> tuple[ValidationProblem, ...]:
        """N must be a positive multiple of 8.

        "raw bits, variable size given by *, limited to be a multiple of
        8" -- and zero bits is not a data type.
        """
        try:
            raw_bytes_dtype_name(self.data_type_name)
        except ValueError as error:
            return problem((), str(error), "invalid_value")
        return ()

    def to_json(self) -> ZarrV3MetadataFieldJSON:
        if self.must_understand:
            return cast("ZarrV3MetadataFieldJSON", self.data_type_name)
        return cast(
            "ZarrV3MetadataFieldJSON",
            {"name": self.data_type_name, "must_understand": False},
        )

    def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
        """One byte value per byte of the scalar.

        A malformed name says nothing about how wide the scalar is, so
        there is no length to check against; `problems` reports the name.
        """
        try:
            raw_bytes_dtype_name(self.data_type_name)
        except ValueError:
            return ()
        return byte_values(value, int(self.data_type_name[1:]) // 8, loc)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

data_type_name class-attribute instance-attribute

data_type_name: str = 'r8'

identifier class-attribute

identifier: str = RAW_BYTES_FAMILY

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The r<N> spelling this instance was read from.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'single_byte'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(
    data_type_name: str = "r8",
    *,
    must_understand: bool = True,
) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Every r<N> spelling, valid or not.

A malformed member of the family is recognized as belonging to it and reported as malformed, rather than passing unjudged as some third party's extension.

Source code in src/zarr_metadata/v3/data_type/raw.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Every `r<N>` spelling, valid or not.

    A malformed member of the family is recognized as belonging to it
    and reported as malformed, rather than passing unjudged as some
    third party's extension.
    """
    return RAW_BYTES_NAME_PATTERN.fullmatch(name) is not None

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: object) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/data_type/raw.py
@classmethod
def coerce(cls, value: object, context: object) -> Coerced[Self]:
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), "expected an 'r<N>' raw-bytes data type")
    found: tuple[ValidationProblem, ...] = ()
    if configuration is not None and len(configuration) != 0:
        # Survivable, as an unknown key is everywhere else: the name
        # still says everything this type is, so it is still read and
        # its fill values are still judged. Returning nothing here let
        # a stray key hide every other problem in the document.
        found = problem(("configuration",), "'r<N>' takes no configuration", "unknown_key")
    return cls(must_understand=must_understand, data_type_name=name), found

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

One byte value per byte of the scalar.

A malformed name says nothing about how wide the scalar is, so there is no length to check against; problems reports the name.

Source code in src/zarr_metadata/v3/data_type/raw.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    """One byte value per byte of the scalar.

    A malformed name says nothing about how wide the scalar is, so
    there is no length to check against; `problems` reports the name.
    """
    try:
        raw_bytes_dtype_name(self.data_type_name)
    except ValueError:
        return ()
    return byte_values(value, int(self.data_type_name[1:]) // 8, loc)

problems

problems() -> tuple[ValidationProblem, ...]

N must be a positive multiple of 8.

"raw bits, variable size given by *, limited to be a multiple of 8" -- and zero bits is not a data type.

Source code in src/zarr_metadata/v3/data_type/raw.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """N must be a positive multiple of 8.

    "raw bits, variable size given by *, limited to be a multiple of
    8" -- and zero bits is not a data type.
    """
    try:
        raw_bytes_dtype_name(self.data_type_name)
    except ValueError as error:
        return problem((), str(error), "invalid_value")
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/data_type/raw.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    if self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", self.data_type_name)
    return cast(
        "ZarrV3MetadataFieldJSON",
        {"name": self.data_type_name, "must_understand": False},
    )

raw_bytes_dtype_name

raw_bytes_dtype_name(value: str) -> RawBytesDataTypeName

Validate value as a r<N> raw-bytes name and brand it.

Raises ValueError if value is not r followed by a positive multiple of 8.

Source code in src/zarr_metadata/v3/data_type/raw.py
def raw_bytes_dtype_name(value: str) -> RawBytesDataTypeName:
    """Validate `value` as a `r<N>` raw-bytes name and brand it.

    Raises ValueError if `value` is not `r` followed by a positive
    multiple of 8.
    """
    match = RAW_BYTES_NAME_PATTERN.fullmatch(value)
    if match is None:
        raise ValueError(f"Expected 'r' followed by a positive integer, got {value!r}")
    bits = int(match.group(1))
    if bits == 0 or bits % 8 != 0:
        raise ValueError(f"Expected 'r<N>' where N is a positive multiple of 8, got {value!r}")
    return RawBytesDataTypeName(value)

zarr_metadata.v3.data_type.bytes

Zarr bytes data type (variable-length raw bytes, zarr-extensions).

See https://github.com/zarr-developers/zarr-extensions/blob/4da7b37a84f76e660902f6d3de3eaef0e0febae6/data-types/bytes/README.md

BYTES_DATA_TYPE_NAME module-attribute

BYTES_DATA_TYPE_NAME: Final = 'bytes'

The data_type value for the variable-length bytes type.

Base64Bytes module-attribute

Base64Bytes = NewType('Base64Bytes', str)

A standard-alphabet base64-encoded byte sequence.

BytesDataTypeName module-attribute

BytesDataTypeName = Literal['bytes']

Literal type of the data_type field for bytes.

BytesFillValue module-attribute

BytesFillValue = tuple[int, ...] | Base64Bytes

Permitted JSON shape of the fill_value field for bytes.

Either a JSON array of integers in [0, 255] (one per byte), or a Base64Bytes string encoding the byte sequence.

__all__ module-attribute

__all__ = [
    "BYTES_DATA_TYPE_NAME",
    "Base64Bytes",
    "BytesDataType",
    "BytesDataTypeName",
    "BytesFillValue",
    "base64_bytes",
]

BytesDataType dataclass

Bases: DataTypeEntity

The bytes data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/bytes.py
@dataclass(frozen=True)
class BytesDataType(DataTypeEntity):
    """The `bytes` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "variable_length"
    twos_complement: ClassVar[bool] = False
    identifier: ClassVar[str] = BYTES_DATA_TYPE_NAME

    def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
        """Base64, or an array of byte values of any length."""
        if isinstance(value, str):
            try:
                base64_bytes(value)
            except ValueError:
                return problem(
                    loc, f"expected standard-alphabet base64, got {value!r}", "invalid_value"
                )
            return ()
        return byte_values(value, None, loc)

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = BYTES_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'variable_length'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Base64, or an array of byte values of any length.

Source code in src/zarr_metadata/v3/data_type/bytes.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    """Base64, or an array of byte values of any length."""
    if isinstance(value, str):
        try:
            base64_bytes(value)
        except ValueError:
            return problem(
                loc, f"expected standard-alphabet base64, got {value!r}", "invalid_value"
            )
        return ()
    return byte_values(value, None, loc)

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

base64_bytes

base64_bytes(value: str) -> Base64Bytes

Validate value as a Base64Bytes and brand it.

Raises ValueError if value is not standard-alphabet base64 (length must be a multiple of 4 once padded; only A-Z, a-z, 0-9, +, /, and trailing = padding are permitted).

Source code in src/zarr_metadata/v3/data_type/bytes.py
def base64_bytes(value: str) -> Base64Bytes:
    """Validate `value` as a Base64Bytes and brand it.

    Raises ValueError if `value` is not standard-alphabet base64
    (length must be a multiple of 4 once padded; only `A-Z`, `a-z`,
    `0-9`, `+`, `/`, and trailing `=` padding are permitted).
    """
    if len(value) % 4 != 0 or not _BASE64_RE.fullmatch(value):
        raise ValueError(f"Expected standard-alphabet base64, got {value!r}")
    return Base64Bytes(value)

zarr_metadata.v3.data_type.string

Zarr string data type (variable-length utf-8, zarr-extensions).

See https://github.com/zarr-developers/zarr-extensions/blob/4da7b37a84f76e660902f6d3de3eaef0e0febae6/data-types/string/README.md

STRING_DATA_TYPE_NAME module-attribute

STRING_DATA_TYPE_NAME: Final = 'string'

The data_type value for the string type.

StringDataTypeName module-attribute

StringDataTypeName = Literal['string']

Literal type of the data_type field for string.

StringFillValue module-attribute

StringFillValue = str

Permitted JSON shape of the fill_value field for string: a JSON unicode string.

__all__ module-attribute

__all__ = [
    "STRING_DATA_TYPE_NAME",
    "StringDataType",
    "StringDataTypeName",
    "StringFillValue",
]

StringDataType dataclass

Bases: DataTypeEntity

The string data type. The name says everything.

Source code in src/zarr_metadata/v3/data_type/string.py
@dataclass(frozen=True)
class StringDataType(DataTypeEntity):
    """The `string` data type. The name says everything."""

    scalar_storage: ClassVar[StorageClass] = "variable_length"
    twos_complement: ClassVar[bool] = False
    identifier: ClassVar[str] = STRING_DATA_TYPE_NAME

    def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
        if not isinstance(value, str):
            return problem(loc, f"expected a string, got {value!r}", "invalid_value")
        return ()

configuration_required class-attribute

configuration_required: bool = False

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

identifier: str = STRING_DATA_TYPE_NAME

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = MappingProxyType({})

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'variable_length'

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

__init__

__init__(*, must_understand: bool = True) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/string.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    if not isinstance(value, str):
        return problem(loc, f"expected a string, got {value!r}", "invalid_value")
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

Every value of this entity the spec disallows.

Locations are relative to the entity's configuration. Default: an entity whose type admits only valid values has nothing to add.

Source code in src/zarr_metadata/v3/_entity.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """Every value of this entity the spec disallows.

    Locations are relative to the entity's `configuration`. Default:
    an entity whose type admits only valid values has nothing to add.
    """
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/_entity.py
def to_json(self) -> ZarrV3MetadataFieldJSON:
    """This entity as a document would write it.

    Faithful to every member: read a document, write it back, and the
    members come out as they went in. Ask `canonical` first if you
    want the simplest equivalent spelling.

    What is *not* preserved is the envelope's spelling, because the
    entity does not model it: a bare name, `{"name": x}`, and
    `{"name": x, "configuration": {}}` all mean the same and all read
    to the same entity, so all three write back as the bare name.
    `must_understand` is omitted when true, which is its default; an
    explicit false is kept, because that one says something.

    Subclasses narrow the return type to their own object TypedDict,
    which is the JSON form this dataclass models.
    """
    configuration = self.configuration()
    if len(configuration) == 0 and self.must_understand:
        return cast("ZarrV3MetadataFieldJSON", type(self).identifier)
    entry: dict[str, object] = {"name": type(self).identifier}
    if len(configuration) != 0:
        entry["configuration"] = configuration
    if not self.must_understand:
        entry["must_understand"] = False
    return cast("ZarrV3MetadataFieldJSON", entry)

zarr_metadata.v3.data_type.numpy_datetime64

Zarr numpy.datetime64 data type (zarr-extensions).

See https://github.com/zarr-developers/zarr-extensions/blob/4da7b37a84f76e660902f6d3de3eaef0e0febae6/data-types/numpy.datetime64/README.md

NUMPY_DATETIME64_DATA_TYPE_NAME module-attribute

NUMPY_DATETIME64_DATA_TYPE_NAME: Final = 'numpy.datetime64'

The name field value of the numpy.datetime64 data type.

NumpyDatetime64DataTypeName module-attribute

NumpyDatetime64DataTypeName = Literal['numpy.datetime64']

Literal type of the name field of the numpy.datetime64 data type.

NumpyDatetime64FillValue module-attribute

NumpyDatetime64FillValue = int | Literal['NaT']

Permitted JSON shape of the fill_value field for numpy.datetime64.

Either a JSON integer (count of unit * scale_factor since the epoch), or the string "NaT" (equivalent to the integer -2**63).

NumpyTimeUnit module-attribute

NumpyTimeUnit = Literal[
    "Y",
    "M",
    "W",
    "D",
    "h",
    "m",
    "s",
    "ms",
    "us",
    "μs",
    "ns",
    "ps",
    "fs",
    "as",
    "generic",
]

Time unit codes used by numpy.datetime64.

__all__ module-attribute

__all__ = [
    "NUMPY_DATETIME64_DATA_TYPE_NAME",
    "NumpyDatetime64",
    "NumpyDatetime64Configuration",
    "NumpyDatetime64DataType",
    "NumpyDatetime64DataTypeName",
    "NumpyDatetime64FillValue",
    "NumpyTimeUnit",
]

NumpyDatetime64

Bases: TypedDict

numpy.datetime64 data type metadata.

Source code in src/zarr_metadata/v3/data_type/numpy_datetime64.py
class NumpyDatetime64(TypedDict, closed=True):
    """`numpy.datetime64` data type metadata."""

    name: NumpyDatetime64DataTypeName
    configuration: NumpyDatetime64Configuration
    must_understand: NotRequired[bool]

configuration instance-attribute

must_understand instance-attribute

must_understand: NotRequired[bool]

name instance-attribute

NumpyDatetime64Configuration

Bases: TypedDict

Configuration for the numpy.datetime64 data type.

Attributes:

Source code in src/zarr_metadata/v3/data_type/numpy_datetime64.py
class NumpyDatetime64Configuration(TypedDict, closed=True):
    """
    Configuration for the `numpy.datetime64` data type.

    Attributes
    ----------
    unit
        A string encoding a unit of time.
    scale_factor
        The multiplier relative to the unit.
    """

    unit: ReadOnly[NumpyTimeUnit]
    scale_factor: ReadOnly[int]

scale_factor instance-attribute

scale_factor: ReadOnly[int]

unit instance-attribute

unit: ReadOnly[NumpyTimeUnit]

NumpyDatetime64DataType dataclass

Bases: NumpyTimeDataType

The numpy.datetime64 data type, coerced from its metadata.

Source code in src/zarr_metadata/v3/data_type/numpy_datetime64.py
@dataclass(frozen=True)
class NumpyDatetime64DataType(NumpyTimeDataType):
    """The `numpy.datetime64` data type, coerced from its metadata."""

    unit: NumpyTimeUnit = "generic"
    scale_factor: int = 1

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    identifier: ClassVar[str] = NUMPY_DATETIME64_DATA_TYPE_NAME

    configuration_required: ClassVar[bool] = True
    member_types: ClassVar[MemberTypes] = {
        "unit": (True, one_of(NUMPY_TIME_UNIT)),
        "scale_factor": (True, is_int),
    }

    def problems(self) -> tuple[ValidationProblem, ...]:
        """`scale_factor` counts units per step, so it is positive.

        The upper bound is numpy's: the field is a signed 32-bit integer.
        """
        if not 1 <= self.scale_factor <= NUMPY_TIME_MAX_SCALE_FACTOR:
            return problem(
                ("scale_factor",),
                f"expected an integer in [1, {NUMPY_TIME_MAX_SCALE_FACTOR}], "
                f"got {self.scale_factor}",
                "invalid_value",
            )
        return ()

    def to_json(self) -> NumpyDatetime64:
        return cast("NumpyDatetime64", super().to_json())

configuration_required class-attribute

configuration_required: bool = True

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = {
    "unit": (True, one_of(NUMPY_TIME_UNIT)),
    "scale_factor": (True, is_int),
}

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

scale_factor class-attribute instance-attribute

scale_factor: int = 1

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

unit class-attribute instance-attribute

unit: NumpyTimeUnit = 'generic'

__init__

__init__(
    unit: NumpyTimeUnit = "generic",
    scale_factor: int = 1,
    *,
    must_understand: bool = True,
) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    if value == "NaT":
        return ()
    if not is_integer(value):
        return problem(
            loc, f"expected a signed 64-bit integer or 'NaT', got {value!r}", "invalid_value"
        )
    if not -(2**63) <= value <= 2**63 - 1:
        return problem(loc, f"expected a signed 64-bit integer, got {value!r}", "invalid_value")
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

scale_factor counts units per step, so it is positive.

The upper bound is numpy's: the field is a signed 32-bit integer.

Source code in src/zarr_metadata/v3/data_type/numpy_datetime64.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """`scale_factor` counts units per step, so it is positive.

    The upper bound is numpy's: the field is a signed 32-bit integer.
    """
    if not 1 <= self.scale_factor <= NUMPY_TIME_MAX_SCALE_FACTOR:
        return problem(
            ("scale_factor",),
            f"expected an integer in [1, {NUMPY_TIME_MAX_SCALE_FACTOR}], "
            f"got {self.scale_factor}",
            "invalid_value",
        )
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

to_json() -> NumpyDatetime64

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/data_type/numpy_datetime64.py
def to_json(self) -> NumpyDatetime64:
    return cast("NumpyDatetime64", super().to_json())

zarr_metadata.v3.data_type.numpy_timedelta64

Zarr numpy.timedelta64 data type (zarr-extensions).

See https://github.com/zarr-developers/zarr-extensions/blob/4da7b37a84f76e660902f6d3de3eaef0e0febae6/data-types/numpy.timedelta64/README.md

NUMPY_TIMEDELTA64_DATA_TYPE_NAME module-attribute

NUMPY_TIMEDELTA64_DATA_TYPE_NAME: Final = (
    "numpy.timedelta64"
)

The name field value of the numpy.timedelta64 data type.

NUMPY_TIME_MAX_SCALE_FACTOR module-attribute

NUMPY_TIME_MAX_SCALE_FACTOR: Final = 2 ** 31 - 1

The largest scale_factor numpy stores: the field is a signed int32.

NUMPY_TIME_UNIT module-attribute

NUMPY_TIME_UNIT: Final = (
    "Y",
    "M",
    "W",
    "D",
    "h",
    "m",
    "s",
    "ms",
    "us",
    "μs",
    "ns",
    "ps",
    "fs",
    "as",
    "generic",
)

Runtime tuple of the permitted numpy.timedelta64/numpy.datetime64 unit strings.

NumpyTimeUnit module-attribute

NumpyTimeUnit = Literal[
    "Y",
    "M",
    "W",
    "D",
    "h",
    "m",
    "s",
    "ms",
    "us",
    "μs",
    "ns",
    "ps",
    "fs",
    "as",
    "generic",
]

Time unit codes used by numpy.timedelta64.

NumpyTimedelta64DataTypeName module-attribute

NumpyTimedelta64DataTypeName = Literal['numpy.timedelta64']

Literal type of the name field of the numpy.timedelta64 data type.

NumpyTimedelta64FillValue module-attribute

NumpyTimedelta64FillValue = int | Literal['NaT']

Permitted JSON shape of the fill_value field for numpy.timedelta64.

Either a JSON integer (a count of unit * scale_factor), or the string "NaT" (equivalent to the integer -2**63).

__all__ module-attribute

__all__ = [
    "NUMPY_TIMEDELTA64_DATA_TYPE_NAME",
    "NUMPY_TIME_MAX_SCALE_FACTOR",
    "NUMPY_TIME_UNIT",
    "NumpyTimeUnit",
    "NumpyTimedelta64",
    "NumpyTimedelta64Configuration",
    "NumpyTimedelta64DataType",
    "NumpyTimedelta64DataTypeName",
    "NumpyTimedelta64FillValue",
]

NumpyTimedelta64

Bases: TypedDict

numpy.timedelta64 data type metadata.

Source code in src/zarr_metadata/v3/data_type/numpy_timedelta64.py
class NumpyTimedelta64(TypedDict, closed=True):
    """`numpy.timedelta64` data type metadata."""

    name: NumpyTimedelta64DataTypeName
    configuration: NumpyTimedelta64Configuration
    must_understand: NotRequired[bool]

configuration instance-attribute

must_understand instance-attribute

must_understand: NotRequired[bool]

name instance-attribute

NumpyTimedelta64Configuration

Bases: TypedDict

Configuration for the numpy.timedelta64 data type.

Attributes:

Source code in src/zarr_metadata/v3/data_type/numpy_timedelta64.py
class NumpyTimedelta64Configuration(TypedDict, closed=True):
    """
    Configuration for the `numpy.timedelta64` data type.

    Attributes
    ----------
    unit
        A string encoding a unit of time.
    scale_factor
        The multiplier relative to the unit.
    """

    unit: ReadOnly[NumpyTimeUnit]
    scale_factor: ReadOnly[int]

scale_factor instance-attribute

scale_factor: ReadOnly[int]

unit instance-attribute

unit: ReadOnly[NumpyTimeUnit]

NumpyTimedelta64DataType dataclass

Bases: NumpyTimeDataType

The numpy.timedelta64 data type, coerced from its metadata.

Source code in src/zarr_metadata/v3/data_type/numpy_timedelta64.py
@dataclass(frozen=True)
class NumpyTimedelta64DataType(NumpyTimeDataType):
    """The `numpy.timedelta64` data type, coerced from its metadata."""

    unit: NumpyTimeUnit = "generic"
    scale_factor: int = 1

    scalar_storage: ClassVar[StorageClass] = "multi_byte"
    identifier: ClassVar[str] = NUMPY_TIMEDELTA64_DATA_TYPE_NAME

    configuration_required: ClassVar[bool] = True
    member_types: ClassVar[MemberTypes] = {
        "unit": (True, one_of(NUMPY_TIME_UNIT)),
        "scale_factor": (True, is_int),
    }

    def problems(self) -> tuple[ValidationProblem, ...]:
        """`scale_factor` counts units per step, so it is positive.

        The upper bound is numpy's: the field is a signed 32-bit integer.
        """
        if not 1 <= self.scale_factor <= NUMPY_TIME_MAX_SCALE_FACTOR:
            return problem(
                ("scale_factor",),
                f"expected an integer in [1, {NUMPY_TIME_MAX_SCALE_FACTOR}], "
                f"got {self.scale_factor}",
                "invalid_value",
            )
        return ()

    def to_json(self) -> NumpyTimedelta64:
        return cast("NumpyTimedelta64", super().to_json())

configuration_required class-attribute

configuration_required: bool = True

Whether the bare-name spelling says too little for this entity.

The spec permits a bare name "if no configuration metadata is required", so this is true exactly when some member is required.

identifier class-attribute

The name this entity is registered under.

Usually the name the metadata carries. The raw-bytes data types are the exception: every r<N> spelling is one family, so the family gets an invented identifier that no real name can collide with.

member_types class-attribute

member_types: MemberTypes = {
    "unit": (True, one_of(NUMPY_TIME_UNIT)),
    "scale_factor": (True, is_int),
}

The configuration members, and the type each one takes.

The same keys as the configuration TypedDict, which is the same as the constructor signature; tests/v3/test_entities.py holds the three together.

must_understand class-attribute instance-attribute

must_understand: bool = field(default=True, kw_only=True)

name property

name: str

The name this entity carries, as a document would write it.

Usually the identifier. They differ for the raw-bytes family, whose identifier is invented and belongs in no message a reader sees -- so anything user-facing wants this, and anything looking something up wants identifier.

required_class_vars class-attribute

required_class_vars: tuple[str, ...] = (
    "identifier",
    "scalar_storage",
    "twos_complement",
)

Every class variable a concrete entity of this kind must declare.

scalar_storage class-attribute

scalar_storage: StorageClass = 'multi_byte'

scale_factor class-attribute instance-attribute

scale_factor: int = 1

twos_complement class-attribute

twos_complement: bool = False

Whether this type's scalars are two's complement integers.

Asked by cast_value, whose out_of_range: "wrap" is defined only for such a target. Required rather than defaulted: a data type added later must decide, because either default would answer for it silently -- and getting it wrong in one direction accepts a cast the spec does not define.

unit class-attribute instance-attribute

unit: NumpyTimeUnit = 'generic'

__init__

__init__(
    unit: NumpyTimeUnit = "generic",
    scale_factor: int = 1,
    *,
    must_understand: bool = True,
) -> None

__init_subclass__

__init_subclass__(
    *, base: bool = False, **kwargs: object
) -> None

Refuse a subclass that forgot to say what it is.

identifier and the per-kind class variables carry no default, so a subclass omitting one type-checks cleanly and then raises AttributeError from whichever method is reached first. Saying so here makes it an import-time error in the extension's own module.

base=True for a class that exists to add a class variable rather than to be an entity -- CodecEntity, IntegerDataType.

Source code in src/zarr_metadata/v3/_entity.py
def __init_subclass__(cls, *, base: bool = False, **kwargs: object) -> None:
    """Refuse a subclass that forgot to say what it is.

    `identifier` and the per-kind class variables carry no default,
    so a subclass omitting one type-checks cleanly and then raises
    `AttributeError` from whichever method is reached first. Saying so
    here makes it an import-time error in the extension's own module.

    `base=True` for a class that exists to add a class variable
    rather than to be an entity -- `CodecEntity`, `IntegerDataType`.
    """
    super().__init_subclass__(**kwargs)
    if base:
        return
    missing = [name for name in cls.required_class_vars if not hasattr(cls, name)]
    if len(missing) != 0:
        msg = f"{cls.__name__} does not declare {', '.join(missing)}"
        raise TypeError(msg)
    # An optional member defaults to UNSET or `configuration` emits it
    # for every instance, so the bare-name spelling becomes
    # unreachable and a document gains a member it never wrote.
    invented = [
        key
        for key, (required, _) in cls.member_types.items()
        if not required and getattr(cls, key, UNSET) is not UNSET
    ]
    if len(invented) != 0:
        msg = (
            f"{cls.__name__} gives the optional member(s) "
            f"{', '.join(invented)} a default other than UNSET"
        )
        raise TypeError(msg)

accepts classmethod

accepts(name: str) -> bool

Whether name denotes this entity.

Constant for all but the raw-bytes family, where one class covers every r<N>.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def accepts(cls, name: str) -> bool:
    """Whether `name` denotes this entity.

    Constant for all but the raw-bytes family, where one class covers
    every `r<N>`.
    """
    return name == cls.identifier

canonical

canonical() -> Self

This entity in the simplest form that means the same thing.

A transformation, asked for by canonicalize_array_metadata_v3 and by nothing else. to_json does not apply it, because writing a document back is not the same as asking for it to be rewritten: a reader that reads and writes should not change bytes it was not asked to change.

Default: entities are already canonical. Override where two spellings of a member mean the same -- a rectilinear dimension's run-length encoding, a typesize that noshuffle ignores -- and where a contained entity has its own canonical form.

Source code in src/zarr_metadata/v3/_entity.py
def canonical(self) -> Self:
    """This entity in the simplest form that means the same thing.

    A *transformation*, asked for by `canonicalize_array_metadata_v3`
    and by nothing else. `to_json` does not apply it, because writing
    a document back is not the same as asking for it to be rewritten:
    a reader that reads and writes should not change bytes it was not
    asked to change.

    Default: entities are already canonical. Override where two
    spellings of a member mean the same -- a rectilinear dimension's
    run-length encoding, a `typesize` that `noshuffle` ignores -- and
    where a contained entity has its own canonical form.
    """
    return self

coerce classmethod

coerce(value: object, context: Context) -> Coerced[Self]

value as this entity, or the reasons it is not one.

context is the scope this reading is happening in; most entities have no use for it and ignore it.

Source code in src/zarr_metadata/v3/_entity.py
@classmethod
def coerce(cls, value: object, context: Context) -> Coerced[Self]:
    """`value` as this entity, or the reasons it is not one.

    `context` is the scope this reading is happening in; most entities
    have no use for it and ignore it.
    """
    name, configuration, must_understand = named_configuration(value)
    if name is None or not cls.accepts(name):
        return None, problem((), f"expected the {cls.identifier!r} entity")
    if configuration is None:
        if cls.configuration_required:
            return None, problem(
                ("configuration",),
                f"{cls.identifier!r} requires a configuration",
                "missing_key",
            )
        configuration = cast("Mapping[str, object]", {})
    members, found, unreadable = coerce_members(configuration, cls.member_types)
    if len(unreadable) != 0:
        # The entity cannot be built, but the members that *did* read
        # can still be judged -- one bad member should not hide the
        # value problems of the ones beside it. Anything the partial
        # reading says about an unreadable member is its default
        # talking, so those are dropped.
        partial = cls(must_understand=must_understand, **members)  # type: ignore[arg-type]
        found = (
            *found,
            # `within`, because a partial reading reports relative to
            # the configuration and `coerce`'s caller does not insert
            # that segment -- `coerce_members` problems already carry it.
            *within(
                (),
                [
                    entry
                    for entry in partial.problems()
                    if entry.loc[:1] not in {(key,) for key in unreadable}
                ],
            ),
        )
        return None, found
    return cls(must_understand=must_understand, **members), found  # type: ignore[arg-type]

configuration

configuration() -> dict[str, object]

This entity's configuration, as the document would write it.

Faithful to every member the entity holds: to_json is serialization, not canonicalization, so nothing is simplified here. Override only to render a member that is not already JSON, such as a contained entity.

Absent optional members are left out, which is what makes the bare-name spelling reachable. Absence is UNSET, never None: this package holds None to mean a JSON null the document actually wrote, and scale_offset is a real case where null and absent are different documents.

Source code in src/zarr_metadata/v3/_entity.py
def configuration(self) -> dict[str, object]:
    """This entity's configuration, as the document would write it.

    Faithful to every member the entity holds: `to_json` is
    serialization, not canonicalization, so nothing is simplified
    here. Override only to render a member that is not already JSON,
    such as a contained entity.

    Absent optional members are left out, which is what makes the
    bare-name spelling reachable. Absence is `UNSET`, never `None`:
    this package holds `None` to mean a JSON `null` the document
    actually wrote, and `scale_offset` is a real case where `null`
    and absent are different documents.
    """
    return {
        key: value
        for key in type(self).member_types
        if (value := getattr(self, key)) is not UNSET
    }

fill_value_problems

fill_value_problems(
    value: object, loc: Loc = ()
) -> tuple[ValidationProblem, ...]

Why value is not a fill value of this type, if it is not.

Default: nothing. A data type this package does not model accepts whatever its extension says it does, and guessing would reject valid documents.

Source code in src/zarr_metadata/v3/data_type/_families.py
def fill_value_problems(self, value: object, loc: Loc = ()) -> tuple[ValidationProblem, ...]:
    if value == "NaT":
        return ()
    if not is_integer(value):
        return problem(
            loc, f"expected a signed 64-bit integer or 'NaT', got {value!r}", "invalid_value"
        )
    if not -(2**63) <= value <= 2**63 - 1:
        return problem(loc, f"expected a signed 64-bit integer, got {value!r}", "invalid_value")
    return ()

problems

problems() -> tuple[ValidationProblem, ...]

scale_factor counts units per step, so it is positive.

The upper bound is numpy's: the field is a signed 32-bit integer.

Source code in src/zarr_metadata/v3/data_type/numpy_timedelta64.py
def problems(self) -> tuple[ValidationProblem, ...]:
    """`scale_factor` counts units per step, so it is positive.

    The upper bound is numpy's: the field is a signed 32-bit integer.
    """
    if not 1 <= self.scale_factor <= NUMPY_TIME_MAX_SCALE_FACTOR:
        return problem(
            ("scale_factor",),
            f"expected an integer in [1, {NUMPY_TIME_MAX_SCALE_FACTOR}], "
            f"got {self.scale_factor}",
            "invalid_value",
        )
    return ()

storage_class

storage_class() -> StorageClass | None

How one scalar occupies bytes, or None if undetermined.

None only for a composite whose parts are not all in scope: an answer would be a guess, and the rules that ask decline instead.

Source code in src/zarr_metadata/v3/_entity.py
def storage_class(self) -> StorageClass | None:
    """How one scalar occupies bytes, or None if undetermined.

    None only for a composite whose parts are not all in scope: an
    answer would be a guess, and the rules that ask decline instead.
    """
    return type(self).scalar_storage

to_json

to_json() -> NumpyTimedelta64

This entity as a document would write it.

Faithful to every member: read a document, write it back, and the members come out as they went in. Ask canonical first if you want the simplest equivalent spelling.

What is not preserved is the envelope's spelling, because the entity does not model it: a bare name, {"name": x}, and {"name": x, "configuration": {}} all mean the same and all read to the same entity, so all three write back as the bare name. must_understand is omitted when true, which is its default; an explicit false is kept, because that one says something.

Subclasses narrow the return type to their own object TypedDict, which is the JSON form this dataclass models.

Source code in src/zarr_metadata/v3/data_type/numpy_timedelta64.py
def to_json(self) -> NumpyTimedelta64:
    return cast("NumpyTimedelta64", super().to_json())