xarray_annotated.temporal¶
xarray_annotated.temporal
¶
Temporal-frequency domain for xarray-annotated.
Declares and validates the frequency of a DataArray’s time axis — a property
derived from the values of its datetime coordinate rather than carried in its
metadata, which is why it is a domain of its own rather than a fourth structural
marker. Like xarray_annotated.schema (and unlike units) it only asserts:
it never converts, resamples, or stamps anything.
A declaration compares on two things: spacing (always) and phase (the
End/Begin convention, and the anchor where both sides spell one). So
Freq("7D") accepts a weekly axis on any weekday, while Freq("W-SUN") catches
the classic resample footgun of a series landing on Wednesdays::
from typing import Annotated
import xarray as xr
from xarray_annotated.temporal import declare_freq, Freq
@declare_freq
def weekly_mean(
x: Annotated[xr.DataArray, Freq("D")],
) -> Annotated[xr.DataArray, Freq("W-SUN")]:
return x.resample(time="W-SUN").mean()
freq_compatible is the same comparison with no array in hand, for a build-time
check of a producer/consumer edge.
Freq
¶
Freq(
freq: str,
*,
dim: str | None = None,
anchored: bool | None = None,
on_mismatch: OnMismatch | None = None,
)
Declare the expected temporal frequency of a DataArray’s time axis.
Freq("7D") declares an array whose datetime coordinate advances in
seven-day steps; Freq("W-SUN") declares the same spacing and pins the
phase to Sundays. Two independent comparisons follow from the declaration:
- spacing — always compared (
"7D"and"W-WED"have the same spacing); - phase — the
End/Beginconvention ("ME"vs"MS"), always compared; and the anchor ("-WED","-MAR"), compared only where the declaration spells it.Freq("W")therefore means “weekly, any weekday”, a deliberate divergence from pandas (which would default it toW-SUN).
Pass anchored=True to opt in to pandas’ default anchor anyway
(Freq("W", anchored=True) means W-SUN and means it), or anchored=False
to suppress the anchor comparison of a spelled-out anchor
(Freq("W-SUN", anchored=False) = “weekly, any weekday”).
Pass dim to name the time coordinate explicitly; by default the array’s sole
datetime-like coordinate is used, and an array carrying two is ambiguous.
FreqError
¶
Bases: Exception
Raised for a frequency mismatch under on_mismatch="error".
Deliberately not a ValueError: a mismatch (the data fails the
declaration) is a distinct event from a malformed declaration (an unparseable
offset string), which raises ValueError from assert_valid_freq. Keeping
them on separate hierarchies means catching one never silently swallows the other.
FreqWarning
¶
Bases: UserWarning
Warning issued for a frequency mismatch or an uninferable time axis.
Policy
dataclass
¶
Policy(
enabled: bool = DEFAULT_ENABLED,
on_mismatch: OnMismatch = DEFAULT_ON_MISMATCH,
on_uninferable: OnUninferable = DEFAULT_ON_UNINFERABLE,
)
The resolved temporal-validation policy — the three axes as a single value.
Returned by get_policy. Build overrides via set_policy or the policy
context manager rather than constructing this directly.
Attributes:
-
enabled(bool) –Master switch;
Falsemakes all validation a no-op. -
on_mismatch(OnMismatch) –Default behaviour when the inferred frequency contradicts the declaration (
"error","warn", or"ignore"); a marker may override it. -
on_uninferable(OnUninferable) –Behaviour when no frequency can be inferred from the time axis — too few points, or irregular spacing.
annotated_freq
¶
Return the Freq marker carried by an Annotated hint, or None.
The first Freq marker in the metadata, and only when the annotated base type
is a DataArray (incl. DataArray | None). Non-Annotated hints, hints on
non-DataArray types, and hints with no Freq marker return None (so
walk_signature’s “no declaration” filter drops them).
Parameters:
-
hint(Any) –A type hint, typically from
get_type_hints(..., include_extras=True).
Returns:
-
Freq | None–The declared
Freq, orNone.
assert_valid_freq
¶
Validate a Freq declaration itself, independent of any DataArray.
Used at decoration time to fail fast on a malformed declaration (an unparseable
offset string, an empty dim name) rather than only when the decorated function
first runs. pandas’ own message is kept — it is the one that explains that the
legacy aliases "M" and "H" are now spelled "ME" and "h".
Parameters:
-
marker(Freq) –The declared
Freq. -
context(str) –A label for the error message (e.g.
"f input 'x'").
Raises:
-
ValueError–If the declaration is malformed.
check_freq
¶
check_freq(
da: DataArray,
declared: Freq | list[Freq],
name: str,
on_mismatch: OnMismatch | None = None,
on_uninferable: OnUninferable | None = None,
qualname: str | None = None,
) -> DataArray
Validate da’s time axis against its declared frequency; return it unchanged.
Locates the declared time coordinate (the array’s sole datetime-like coordinate,
unless the marker names one), infers its frequency with xarray.infer_freq, and
compares that with the declaration via freq_compatible. Because pandas always
hands back a fully-anchored string ("W-WED", never "7D"), the declaration
alone decides how strict the comparison is — Freq("7D") accepts any weekday,
Freq("W-SUN") accepts only Sundays.
Two events, two axes. A frequency that contradicts the declaration is a mismatch, as is an array with no (or an ambiguous) time axis. An axis whose frequency cannot be inferred at all — fewer than three points, or irregular spacing — is uninferable: the declaration was not violated, it was never tested, and by default that warns rather than raises.
Severity resolves per marker as: the marker’s own on_mismatch override, else the
on_mismatch argument, else the policy default. When the master switch is off
(enabled=False) this is a total no-op.
Parameters:
-
da(DataArray) –The DataArray to validate.
-
declared(Freq | list[Freq]) –A single
Freqor a list of them declared forda. -
name(str) –The parameter/field name, for error messages.
-
on_mismatch(OnMismatch | None, default:None) –Per-call default severity for a mismatch, overriding the policy default but overridden by a marker’s own
on_mismatch. -
on_uninferable(OnUninferable | None, default:None) –Per-call severity for an uninferable axis, overriding the policy default.
-
qualname(str | None, default:None) –Optional qualified function name, prefixed to messages.
Returns:
-
DataArray–da, unchanged (validation never mutates).
Raises:
-
FreqError–On a mismatch when the effective severity is
"error".
declare_freq
¶
declare_freq(
func: Callable[..., Any] | None = None,
*,
on_mismatch: OnMismatch | None = None,
on_uninferable: OnUninferable | None = None,
) -> Callable[..., Any]
Apply a function’s signature-declared frequencies at runtime.
Reads the decorated function’s own type annotations once, via
freq_from_signature: parameters annotated Annotated[DataArray, Freq(...)]
declare an input’s temporal frequency, and a TypedDict/dataclass return (or a
bare Annotated[DataArray, Freq(...)] return) declares an output’s.
On each call, under the active Policy (get_policy), the wrapper validates every
declared DataArray input via check_freq, runs the wrapped function, then
validates every declared output the same way. Nothing is mutated; a mismatch
raises/warns/ignores per the effective on_mismatch (a marker’s own override wins
over this decorator’s, which wins over the policy default), and an axis whose
frequency cannot be inferred at all is reported under on_uninferable. When the
policy is disabled the wrapper is a total no-op.
Usable bare (@declare_freq) or called (@declare_freq(on_mismatch=...)). Every
declaration is validated at decoration time (assert_valid_freq), so a
malformed offset string fails fast at import.
Parameters:
-
func(Callable[..., Any] | None, default:None) –The function to decorate (when used bare);
Nonewhen parametrised. -
on_mismatch(OnMismatch | None, default:None) –Default mismatch severity for this function, overriding the policy default.
None(default) resolves per call fromget_policy. -
on_uninferable(OnUninferable | None, default:None) –Severity for an uninferable time axis, overriding the policy default.
None(default) resolves per call fromget_policy.
Returns:
freq_compatible
¶
Return whether two Freq declarations can describe the same time axis.
The marker-vs-marker counterpart to check_freq (no array in hand), for a static
/ build-time check of a producer/consumer edge. Two declarations are compatible
when their spacing is the same and their phase does not conflict — where
“conflict” needs both sides to determine the thing being compared, so a
declaration that does not spell an anchor never conflicts on one.
Parameters:
Returns:
-
bool–Trueunless the two are provably inconsistent.
Raises:
-
ValueError–If either frequency string is unparseable.
Examples:
>>> from xarray_annotated.temporal import Freq, freq_compatible
>>> freq_compatible(Freq("7D"), Freq("W-WED")) # same spacing, no anchor on 7D
True
>>> freq_compatible(Freq("W-SUN"), Freq("W-WED")) # the resample-phase footgun
False
>>> freq_compatible(Freq("W"), Freq("W-WED")) # "W" spells no anchor
True
>>> freq_compatible(Freq("QE"), Freq("3ME")) # same spacing and convention
True
>>> freq_compatible(Freq("ME"), Freq("MS")) # month-end is not month-start
False
freq_from_signature
¶
Extract declared frequencies from a function’s type annotations.
Parameters:
-
func(object) –A callable whose hints carry
Annotated[DataArray, Freq(...)].
Returns:
-
dict[str, Freq]–An
(inputs, output)pair, where each declaration is that parameter’s -
dict[str, Freq] | Freq | None–Freq;outputis a per-field dict for aTypedDictor dataclass return, -
tuple[dict[str, Freq], dict[str, Freq] | Freq | None]–a single
Freqfor one declaredDataArrayreturn, orNone.
get_policy
¶
get_policy() -> Policy
Resolve the active temporal policy (env → process → default, per axis).
Returns:
-
Policy–The resolved
Policy.
policy
¶
policy(
*,
enabled: bool | _Unset | None = _UNSET,
on_mismatch: OnMismatch | _Unset | None = _UNSET,
on_uninferable: OnUninferable | _Unset | None = _UNSET,
)
Temporarily override temporal-policy axes, restoring them on exit.
Sets every axis you pass in one go, and restores the previous process overrides afterwards even if an exception is raised.
Parameters:
-
enabled(bool | _Unset | None, default:_UNSET) –Override the (package-wide) master switch, or
Noneto clear. -
on_mismatch(OnMismatch | _Unset | None, default:_UNSET) –Override the on-mismatch axis, or
Noneto clear. -
on_uninferable(OnUninferable | _Unset | None, default:_UNSET) –Override the on-uninferable axis, or
Noneto clear.
set_policy
¶
set_policy(
*,
enabled: bool | _Unset | None = _UNSET,
on_mismatch: OnMismatch | _Unset | None = _UNSET,
on_uninferable: OnUninferable | _Unset | None = _UNSET,
) -> None
Set process-wide temporal-policy overrides for one or more axes.
Only the axes you pass are changed. Pass a value to set it, None to clear
that axis’s override (so its env var / default applies again), or omit it to
leave it untouched.
Parameters:
-
enabled(bool | _Unset | None, default:_UNSET) –Override the (package-wide) master switch, or
Noneto clear. -
on_mismatch(OnMismatch | _Unset | None, default:_UNSET) –Override the on-mismatch axis, or
Noneto clear. -
on_uninferable(OnUninferable | _Unset | None, default:_UNSET) –Override the on-uninferable axis, or
Noneto clear.