xarray_annotated¶
xarray_annotated
¶
xarray-annotated: validate DataArray properties declared via typing.Annotated.
Annotation is the unifying technology: a declared property is read off an
Annotated[DataArray, ...] hint and validated at runtime. Each kind of
property is a domain subpackage, imported explicitly:
xarray_annotated.units— physical units (pint / CF), the mature domain.xarray_annotated.schema— structural properties (dims, coords, dtype); validate-only (never mutates).xarray_annotated.temporal— the frequency (and phase) of a time axis; validate-only.
The top level is deliberately thin — nothing domain-specific is re-exported here, so the domains never collide in a shared namespace::
from xarray_annotated import schema, temporal, units
from xarray_annotated.units import declare_units, check_units
from xarray_annotated.schema import declare_schema, Dims, Dtype
from xarray_annotated.temporal import declare_freq, Freq
The only names surfaced at the top level are domain-agnostic helpers that
belong to no single domain: the shared declaration writer annotate and its
inverse reader declarations_from_signature (which reads every facet declared
on a signature into one uniform Declared value), plus the Annotated
introspection kernel (unwrap_annotated, walk_signature).
walk_signature is the shared driver behind every domain’s reader, so a
third-party facet author can use it to build their own *_from_signature
reader. No domain-specific name is re-exported here.
Declared
dataclass
¶
Declared(
unit: Unit | None = None,
dims: Dims | None = None,
dtype: Dtype | None = None,
coords: Coords | None = None,
freq: Freq | None = None,
)
Every facet declared on one DataArray hint, one marker (or None) per slot.
The uniform value produced by declarations_from_signature: a homogeneous
bag of markers, so facet-generic code can treat each slot identically. Each
slot holds the corresponding marker or None when that facet was not
declared; the unit is always a Unit (its bare-string shorthand is
normalised on read), so .unit.unit recovers the string.
The field order (unit, dims, dtype, coords, freq) matches annotate’s
keyword arguments, so annotate(unit=d.unit, dims=d.dims, dtype=d.dtype,
coords=d.coords, freq=d.freq) rebuilds the hint this was read from.
annotate
¶
annotate(
base: Any = DataArray,
*,
unit: str | Unit | None = None,
dims: Iterable[str] | Dims | None = None,
dtype: str | Dtype | None = None,
coords: Iterable[str] | Coords | None = None,
freq: str | Freq | None = None,
) -> Any
Build an Annotated[base, <markers>] hint from declared facet values.
The inverse of the *_from_signature readers: given facet values it returns a
real Annotated object carrying the corresponding markers, in a fixed order
(unit, dims, dtype, coords, freq). Assign it to a function’s return/parameter
annotation and the declare_units / declare_schema / declare_freq decorators
read it back exactly as they would a hand-written one.
Each facet accepts either a raw value or an already-built marker, so a caller
holding a mix (e.g. a Unit object but bare dim-name tuples) can pass both
without unwrapping:
* `unit` — a unit string (`"Pa"`) or a `Unit`.
* `dims` — an iterable of dim names (`("time", "x")`) or a `Dims`.
* `dtype` — a dtype string (`"float64"`) or a `Dtype`.
* `coords` — an iterable of coord names or a `Coords`.
* `freq` — an offset string (`"7D"`) or a `Freq`.
A facet left as None contributes no marker. When no facet is given, base
is returned unchanged (no Annotated wrapper), so annotate() is a safe
no-op default.
Note the freq string is a writer convenience only: there is no bare-string
shorthand on the read side, so a frequency must be spelled as a Freq marker in
a hand-written annotation.
Parameters:
-
base(Any, default:DataArray) –The base type to annotate (default
xarray.DataArray). -
unit(str | Unit | None, default:None) –Declared unit, or
None. -
dims(Iterable[str] | Dims | None, default:None) –Declared dimensions, or
None. -
dtype(str | Dtype | None, default:None) –Declared dtype, or
None. -
coords(Iterable[str] | Coords | None, default:None) –Declared coordinates, or
None. -
freq(str | Freq | None, default:None) –Declared temporal frequency, or
None.
Returns:
-
Any–Annotated[base, <markers>]if any facet was given; otherwisebase.
Examples:
>>> from typing import Annotated, get_args, get_origin
>>> import xarray as xr
>>> from xarray_annotated import annotate
>>> from xarray_annotated.units import Unit
>>> hint = annotate(unit="Pa", dims=("time", "x"), dtype="float64")
>>> get_origin(hint) is Annotated
True
>>> Unit("Pa") in get_args(hint)
True
>>> annotate() is xr.DataArray
True
declarations_from_signature
¶
declarations_from_signature(
func: object,
) -> tuple[dict[str, Declared], dict[str, Declared] | Declared | None]
Read every declared facet off a function’s signature in one uniform shape.
The all-facets counterpart to units_from_signature / schema_from_signature:
a single reader whose payload is a Declared carrying the unit, dims, dtype,
and coords declared on a DataArray hint at once. A consumer that would
otherwise call both per-domain readers and merge their differently-shaped
results can call this once and route a uniform value instead.
Parameters:
-
func(object) –A callable whose parameters/return may carry
Annotated[DataArray, ...]metadata.
Returns:
-
dict[str, Declared]–An
(inputs, output)pair, mirroringwalk_signaturewith aDeclared -
payload(dict[str, Declared] | Declared | None) – -
tuple[dict[str, Declared], dict[str, Declared] | Declared | None]–inputs—dict[str, Declared]mapping each declared parameter to its facets. Parameters carrying no declaration are omitted.
-
tuple[dict[str, Declared], dict[str, Declared] | Declared | None]–output— adict[str, Declared](per-field) for aTypedDictor dataclass return, a singleDeclaredfor one declared DataArray return, orNonewhen the return carries no declaration.
unwrap_annotated
¶
Return the underlying type of an Annotated hint, else the hint itself.
Annotated[DataArray, ...] → DataArray; a non-Annotated hint is returned
unchanged. Lets type comparisons (e.g. t is DataArray) see through the
metadata that declarations attach to parameters.
Parameters:
-
hint(Any) –A type hint, possibly
Annotated.
Returns:
-
Any–The base type if
hintisAnnotated; otherwisehintunchanged.
walk_signature
¶
walk_signature(
func: object, extract: Callable[[Any], T | None]
) -> tuple[dict[str, T], dict[str, T] | T | None]
Extract per-parameter and return declarations from a function’s hints.
Reads get_type_hints(func, include_extras=True) and maps each hint through
extract, which returns a domain’s declaration payload for that hint or
None when the hint carries no declaration. The is not None filter is the
single “no declaration” rule shared by every domain.
Parameters:
-
func(object) –A callable whose parameters/return may carry
Annotatedmetadata. -
extract(Callable[[Any], T | None]) –Turns one hint into a declaration payload, or
None. For units this isannotated_unit(payloadstr); for schemaannotated_schema(payloadlist[marker]).
Returns:
-
dict[str, T]–An
(inputs, output)pair: -
dict[str, T] | T | None–inputs—dict[str, T]mapping each declared parameter to its payload.
-
tuple[dict[str, T], dict[str, T] | T | None]–output— adict[str, T](per-field) if the return hint is aTypedDictor dataclass; a singleTif the return is one declaredAnnotated[DataArray, ...];Noneotherwise.