A small, prototypical zarrita-like Python Zarr implementation on top of zarrs,
exposed to Python via pyo3.
- Type-driven design, "parse, don't validate." Encode invariants in types so that illegal states are unrepresentable, rather than accepting loose inputs and checking them afterward.
- The
FromPyObjectextractor is the validator. Parse each input at the pyo3 boundary into its final, already-valid typed form (use#[derive(FromPyObject)]enums / unions for inputs that can take several shapes). The rest of the code then handles only well-formed values, and the parsing logic lives once on the type and is reused by every entry point. - No manual validation in function bodies. Prefer a richly-typed single
argument over several nullable, mutually-dependent keywords (which force
cross-field checks). Example: sharding is an array→bytes codec, so it occupies
the single
serializerslot — there is no separateshardskeyword to cross-check against it. AvoidOption-everything-then-validate; reserveOptionfor genuinely optional settings with meaningful defaults.
- Prefix every
#[pyclass]type withPy, and set the macronameto the unprefixed form. e.g.#[pyclass(name = "Blosc")] pub struct PyBlosc(...). This keeps it clear in Rust what's a Python-facing wrapper vs. an upstream type, while Python still sees the clean name (Blosc). - Elide lifetimes whenever possible. Prefer
'_over named lifetime parameters when the names are not actually referenced. For example, implementFromPyObjectasimpl FromPyObject<'_, '_> for Twithfn extract(ob: Borrowed<'_, '_, PyAny>)rather than introducing<'a, 'py>. - Extract to
PyBackedStr, notString, when you don't need ownership. When aFromPyObjectimpl only inspects the string (e.g. matching against known values), extract aPyBackedStrinstead of an ownedStringto avoid a needless allocation.PyBackedStrderefs tostr. - Prefer turbofish on
extract. Writelet name = ob.extract::<PyBackedStr>()?;rather than annotating the binding (let name: PyBackedStr = ob.extract()?;).
- Write all documentation in the spirit of ASD-STE100 Simplified Technical
English. This applies to docstrings,
.pyistubs, Rust doc comments, Markdown pages, and the README. Use one topic per sentence. Keep sentences short: 20 words or fewer for instructions, 25 or fewer for descriptions. Use the active voice. Use one term for one concept, and do not use synonyms. Do not omit articles or relative pronouns (that,which) to save space. Keep the Zarr domain terms (codec,sharding,chunk grid) as they are. - Use the active voice for functions and methods, and a noun phrase for everything else. A function or method docstring starts with an imperative verb: "Construct a regular grid...", "Return the store key...". A type alias, class, attribute, or property docstring names what the thing is: "The chunk sizes along one dimension." Do not write "Give a single chunk size" on a type.
- Write all Python docstrings in Google style. Use
Args:,Returns:,Raises:, andExamples:sections. Do not use NumPy underlines or Sphinx:param:fields. Every documented parameter must exist in the signature, and types belong in the signature, not in the docstring. - Verify each
Raises:entry before you write it. Name the concrete exception type, and confirm it by calling the built extension. Do not infer the type from the Rust source, and do not guess. If you cannot confirm it, omit the section.
- Prefer absolute imports over relative imports. Write
from zarrista.codec._array_to_array import ArrayToArrayCodec, notfrom ._array_to_array import .... The package root iszarrista(maturin'spython-source = "python").
- The
.pyistubs are the single source of truth for user-facing docs. The docs site renders from them only (allow_inspection: falseinmkdocs.yml), so full prose, examples, and mkdocstrings cross-references ([`Array.store_metadata`][zarrista.Array.store_metadata]) belong in the.pyiand nowhere else. - A
///doc comment on a Python-facing item gets the one-line summary only. On#[pymethods],#[pyclass], and#[pyfunction]items the doc comment becomes Python's__doc__(whathelp()shows), which would otherwise duplicate the stub. Don't restate the stub's prose there; a single summary line is enough. Everything else insrc/— plainimplblocks,FromPyObjecttypes, private helpers — is Rust-only and keeps full doc comments. - Implementation notes go in
//comments, not///. Rationale aimed at Rust readers (why an upstream type was avoided, alignment caveats, safety reasoning) must not leak into a Python docstring.