Skip to content

zarr_metadata.testing.strategies

zarr_metadata.testing.strategies

Hypothesis strategies for valid Zarr v3 metadata fields.

Every strategy here draws metadata its definition accepts, and that the spec allows: a reader, writer or converter can test itself against the whole space of a codec's, data type's, chunk grid's or chunk key encoding's configurations instead of the handful its authors thought to write down.

from hypothesis import given
from zarr_metadata.testing.strategies import fields
from zarr_metadata.v3.codec.blosc import BLOSC_CODEC

@given(fields(BLOSC_CODEC))
def test_my_reader_reads_every_blosc_codec(field):
    my_reader.read_codec(field)

What a configuration depends on, each kind's function takes: fill_values a data type's configuration, chunk_grid_configurations the shape of the array, codec_configurations the Chunk the codec is handed. Left out, nothing is known of it, and what is drawn is what the definition's rules accept. fields wraps configurations in the envelopes a document writes them in.

How each definition's configurations are drawn is a value, Strategies, keyed by the definition: DEFAULT_STRATEGIES holds one for each definition this package defines that holds no other field. An extension author adds their own with DEFAULT_STRATEGIES | {MY_CODEC: CodecStrategy(...)}.

Configurations are drawn at the data level the TypedDicts declare, JSON arrays as tuples; json.dumps takes them as they are.

Requires the testing extra (pip install zarr-metadata[testing]).

DEFAULT_STRATEGIES module-attribute

DEFAULT_STRATEGIES: Final[
    MappingProxyType[Definition[Any], DefinitionStrategy]
] = MappingProxyType(
    {
        **DATA_TYPE_STRATEGIES,
        **CHUNK_GRID_STRATEGIES,
        **CHUNK_KEY_ENCODING_STRATEGIES,
        **CODEC_STRATEGIES,
    }
)

A strategy for each definition this package defines that holds no other field.

Spellings module-attribute

Spellings = Literal['all', 'canonical']

How fields are written: every spelling the spec allows, or only the simplest.

"all" draws each envelope the spec gives a field -- a bare name, an object without a configuration, an empty configuration, a must_understand of true -- and configurations that say what a simpler one says, as a blosc typesize under noshuffle does. "canonical" draws each in the spelling canonicalize gives it, which is what a writer that writes the fewest words writes.

Strategies module-attribute

How to draw each definition's configurations, by the definition.

ChunkGridStrategy dataclass

Bases: DefinitionStrategy

How to draw a chunk grid's configurations over an array of a shape.

Source code in src/zarr_metadata/testing/_strategy.py
@dataclass(frozen=True, kw_only=True, slots=True)
class ChunkGridStrategy(DefinitionStrategy):
    """How to draw a chunk grid's configurations over an array of a shape."""

    kind: ClassVar[type[Definition[Any]]] = ChunkGridDefinition

    configurations: Callable[[tuple[int, ...] | None], Configurations]
    """Configurations of grids that fit an array of the shape.

    Handed None, no shape is known, and what is drawn is what the grid's
    rules accept, whatever shape it is put over.
    """

    def unconstrained(self) -> Configurations:
        return self.configurations(None)

configurations instance-attribute

configurations: Callable[
    [tuple[int, ...] | None], Configurations
]

Configurations of grids that fit an array of the shape.

Handed None, no shape is known, and what is drawn is what the grid's rules accept, whatever shape it is put over.

kind class-attribute

The kind of definition whose configurations it draws.

unconstrained

unconstrained() -> Configurations

Configurations when nothing is known of what they depend on: what the definition's rules accept.

Source code in src/zarr_metadata/testing/_strategy.py
def unconstrained(self) -> Configurations:
    return self.configurations(None)

ChunkKeyEncodingStrategy dataclass

Bases: DefinitionStrategy

How to draw a chunk key encoding's configurations.

Source code in src/zarr_metadata/testing/_strategy.py
@dataclass(frozen=True, kw_only=True, slots=True)
class ChunkKeyEncodingStrategy(DefinitionStrategy):
    """How to draw a chunk key encoding's configurations."""

    kind: ClassVar[type[Definition[Any]]] = ChunkKeyEncodingDefinition

    configurations: Configurations
    """Configurations its definition accepts."""

    def unconstrained(self) -> Configurations:
        return self.configurations

configurations instance-attribute

configurations: Configurations

Configurations its definition accepts.

kind class-attribute

The kind of definition whose configurations it draws.

unconstrained

unconstrained() -> Configurations

Configurations when nothing is known of what they depend on: what the definition's rules accept.

Source code in src/zarr_metadata/testing/_strategy.py
def unconstrained(self) -> Configurations:
    return self.configurations

CodecStrategy dataclass

Bases: DefinitionStrategy

How to draw a codec's configurations, handed a chunk.

Every codec takes Chunk(), a chunk nothing is known of, so a strategy that draws nothing for it is refused when it is built.

Source code in src/zarr_metadata/testing/_strategy.py
@dataclass(frozen=True, kw_only=True, slots=True)
class CodecStrategy(DefinitionStrategy):
    """How to draw a codec's configurations, handed a chunk.

    Every codec takes `Chunk()`, a chunk nothing is known of, so a
    strategy that draws nothing for it is refused when it is built.
    """

    kind: ClassVar[type[Definition[Any]]] = CodecDefinition

    configurations: Callable[[Chunk], Configurations | None]
    """Configurations of the codec handed the chunk; None when no configuration takes it.

    A `bytes` codec handed a `string` chunk is None, since the spec gives
    that data type a codec of its own.
    """

    def __post_init__(self) -> None:
        if self.configurations(NOTHING_KNOWN) is None:
            msg = "a codec strategy draws configurations for Chunk(), a chunk nothing is known of; got None"
            raise ValueError(msg)

    def unconstrained(self) -> Configurations:
        return cast("Configurations", self.configurations(NOTHING_KNOWN))

configurations instance-attribute

configurations: Callable[[Chunk], Configurations | None]

Configurations of the codec handed the chunk; None when no configuration takes it.

A bytes codec handed a string chunk is None, since the spec gives that data type a codec of its own.

kind class-attribute

The kind of definition whose configurations it draws.

unconstrained

unconstrained() -> Configurations

Configurations when nothing is known of what they depend on: what the definition's rules accept.

Source code in src/zarr_metadata/testing/_strategy.py
def unconstrained(self) -> Configurations:
    return cast("Configurations", self.configurations(NOTHING_KNOWN))

DataTypeStrategy dataclass

Bases: DefinitionStrategy

How to draw a data type's configurations, and the fill values an array of one takes.

Source code in src/zarr_metadata/testing/_strategy.py
@dataclass(frozen=True, kw_only=True, slots=True)
class DataTypeStrategy(DefinitionStrategy):
    """How to draw a data type's configurations, and the fill values an array of one takes."""

    kind: ClassVar[type[Definition[Any]]] = DataTypeDefinition

    configurations: Configurations
    """Configurations its definition accepts."""
    fill_values: Callable[[Mapping[str, JSONValue]], SearchStrategy[JSONValue]]
    """Fill values of the data type a configuration describes."""

    def unconstrained(self) -> Configurations:
        return self.configurations

configurations instance-attribute

configurations: Configurations

Configurations its definition accepts.

fill_values instance-attribute

fill_values: Callable[
    [Mapping[str, JSONValue]], SearchStrategy[JSONValue]
]

Fill values of the data type a configuration describes.

kind class-attribute

The kind of definition whose configurations it draws.

unconstrained

unconstrained() -> Configurations

Configurations when nothing is known of what they depend on: what the definition's rules accept.

Source code in src/zarr_metadata/testing/_strategy.py
def unconstrained(self) -> Configurations:
    return self.configurations

DefinitionStrategy

Bases: ABC

How to draw the configurations of one kind of definition.

Source code in src/zarr_metadata/testing/_strategy.py
class DefinitionStrategy(ABC):
    """How to draw the configurations of one kind of definition."""

    __slots__ = ()

    kind: ClassVar[type[Definition[Any]]]
    """The kind of definition whose configurations it draws."""

    @abstractmethod
    def unconstrained(self) -> Configurations:
        """Configurations when nothing is known of what they depend on: what the definition's rules accept."""

kind class-attribute

The kind of definition whose configurations it draws.

unconstrained abstractmethod

unconstrained() -> Configurations

Configurations when nothing is known of what they depend on: what the definition's rules accept.

Source code in src/zarr_metadata/testing/_strategy.py
@abstractmethod
def unconstrained(self) -> Configurations:
    """Configurations when nothing is known of what they depend on: what the definition's rules accept."""

chunk_grid_configurations

chunk_grid_configurations(
    definition: ChunkGridDefinition[C],
    shape: tuple[int, ...] | None = None,
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[C]

Configurations of the chunk grid definition defines, that fit an array of shape.

With no shape, what is drawn is what the grid's rules accept.

Source code in src/zarr_metadata/testing/strategies.py
def chunk_grid_configurations(
    definition: ChunkGridDefinition[C],
    shape: tuple[int, ...] | None = None,
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[C]:
    """Configurations of the chunk grid `definition` defines, that fit an array of `shape`.

    With no shape, what is drawn is what the grid's rules accept.
    """
    found = cast("ChunkGridStrategy", _strategy_for(definition, strategies))
    return cast("SearchStrategy[C]", found.configurations(shape))

chunk_key_encoding_configurations

chunk_key_encoding_configurations(
    definition: ChunkKeyEncodingDefinition[C],
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[C]

Configurations of the chunk key encoding definition defines.

Source code in src/zarr_metadata/testing/strategies.py
def chunk_key_encoding_configurations(
    definition: ChunkKeyEncodingDefinition[C], *, strategies: Strategies = DEFAULT_STRATEGIES
) -> SearchStrategy[C]:
    """Configurations of the chunk key encoding `definition` defines."""
    found = cast("ChunkKeyEncodingStrategy", _strategy_for(definition, strategies))
    return cast("SearchStrategy[C]", found.configurations)

codec_configurations

codec_configurations(
    definition: CodecDefinition[C],
    chunk: Chunk = NOTHING_KNOWN,
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[C] | None

Configurations of the codec definition defines, handed chunk; None when none takes it.

With no chunk, nothing is known of it, and what is drawn is what the codec's rules accept.

Source code in src/zarr_metadata/testing/strategies.py
def codec_configurations(
    definition: CodecDefinition[C],
    chunk: Chunk = NOTHING_KNOWN,
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[C] | None:
    """Configurations of the codec `definition` defines, handed `chunk`; None when none takes it.

    With no chunk, nothing is known of it, and what is drawn is what the
    codec's rules accept.
    """
    found = cast("CodecStrategy", _strategy_for(definition, strategies))
    return cast("SearchStrategy[C] | None", found.configurations(chunk))

data_type_configurations

data_type_configurations(
    definition: DataTypeDefinition[C],
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[C]

Configurations of the data type definition defines.

Source code in src/zarr_metadata/testing/strategies.py
def data_type_configurations(
    definition: DataTypeDefinition[C], *, strategies: Strategies = DEFAULT_STRATEGIES
) -> SearchStrategy[C]:
    """Configurations of the data type `definition` defines."""
    found = cast("DataTypeStrategy", _strategy_for(definition, strategies))
    return cast("SearchStrategy[C]", found.configurations)

fields

fields(
    definition: Definition[C],
    configurations: SearchStrategy[C] | UNSET = UNSET,
    *,
    spellings: Spellings = "all",
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[ZarrV3MetadataFieldJSON]

Metadata fields of definition: its name and a configuration, in an envelope a document writes.

configurations is what to draw the configuration from, by default the definition's own with nothing known of what they depend on: pass codec_configurations(definition, chunk) for a codec handed a chunk, once it is known not to be None. spellings says whether every spelling the spec allows is drawn, or only the simplest.

Source code in src/zarr_metadata/testing/strategies.py
def fields(
    definition: Definition[C],
    configurations: SearchStrategy[C] | UNSET = UNSET,
    *,
    spellings: Spellings = "all",
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[ZarrV3MetadataFieldJSON]:
    """Metadata fields of `definition`: its name and a configuration, in an envelope a document writes.

    `configurations` is what to draw the configuration from, by default
    the definition's own with nothing known of what they depend on: pass
    `codec_configurations(definition, chunk)` for a codec handed a chunk,
    once it is known not to be None. `spellings` says whether every
    spelling the spec allows is drawn, or only the simplest.
    """
    drawn: Configurations | SearchStrategy[C] = (
        _strategy_for(definition, strategies).unconstrained()
        if configurations is UNSET
        else configurations
    )
    spelled = _any_spelling(definition, drawn)
    if spellings == "canonical":
        return spelled.map(lambda field: _canonical(definition, field))
    return spelled

fill_values

fill_values(
    definition: DataTypeDefinition[C],
    configuration: C,
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[JSONValue]

Fill values of the data type definition and configuration describe, in every spelling the spec gives them.

Source code in src/zarr_metadata/testing/strategies.py
def fill_values(
    definition: DataTypeDefinition[C],
    configuration: C,
    *,
    strategies: Strategies = DEFAULT_STRATEGIES,
) -> SearchStrategy[JSONValue]:
    """Fill values of the data type `definition` and `configuration` describe, in every spelling the spec gives them."""
    found = cast("DataTypeStrategy", _strategy_for(definition, strategies))
    return found.fill_values(cast("Mapping[str, JSONValue]", configuration))