Coverage for snekql/validation.py: 100%
21 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 21:13 +0300
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 21:13 +0300
1"""Boundary validation helpers for constrained public API values."""
3from __future__ import annotations
5from collections.abc import Callable
6from functools import wraps
7from typing import Annotated
9from annotated_types import Ge, Gt
10from pydantic import ConfigDict, ValidationError, validate_call
12from snekql.errors import SnekqlError
14# Constrained numeric aliases used after public boundary validation.
15type NonNegativeFloat = Annotated[float, Ge(0)]
16type NonNegativeInt = Annotated[int, Ge(0)]
17type PositiveInt = Annotated[int, Gt(0)]
20def validate_boundary[ErrorT: SnekqlError, **P, R](
21 *,
22 error_type: type[ErrorT],
23) -> Callable[[Callable[P, R]], Callable[P, R]]:
24 """Validate public API calls and wrap validation errors as domain errors."""
26 def decorate(function: Callable[P, R]) -> Callable[P, R]:
27 validated = validate_call(
28 config=ConfigDict(arbitrary_types_allowed=True, strict=True),
29 )(function)
31 @wraps(function)
32 def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
33 try:
34 return validated(*args, **kwargs)
35 except ValidationError as error:
36 raise error_type(str(error)) from error
38 return wrapper
40 return decorate