"""Shared strict scalar and object readers for configuration codecs.""" from __future__ import annotations import math from typing import Any def json_object(value: Any, context: str) -> dict[str, Any]: """Read an object; a missing or null section keeps its defaults.""" if value is None: return {} if not isinstance(value, dict): raise ValueError(f"{context} must be a JSON object") return value def _value(payload: dict[str, Any], key: str, default: Any) -> Any: value = payload.get(key, default) return default if value is None else value def read_int(payload: dict[str, Any], key: str, default: int) -> int: """Read a JSON integer without truncation or boolean coercion.""" value = _value(payload, key, default) if isinstance(value, bool) or not isinstance(value, int): raise ValueError(f"{key} must be a JSON integer") return value def read_str(payload: dict[str, Any], key: str, default: str) -> str: """Read a JSON string without converting other scalar types.""" value = _value(payload, key, default) if not isinstance(value, str): raise ValueError(f"{key} must be a JSON string") return value def read_bool(payload: dict[str, Any], key: str, default: bool) -> bool: """Read a JSON boolean.""" value = _value(payload, key, default) if not isinstance(value, bool): raise ValueError(f"{key} must be a JSON boolean") return value def read_float(payload: dict[str, Any], key: str, default: float) -> float: """Read a finite JSON number, including overflow-safe integer conversion.""" return finite_number(_value(payload, key, default), key) def finite_number(value: Any, context: str) -> float: """Validate a number without accepting booleans, strings or nonfinite values.""" if isinstance(value, bool) or not isinstance(value, (int, float)): raise ValueError(f"{context} must be a JSON number") try: result = float(value) except OverflowError as exc: raise ValueError(f"{context} must be a finite number") from exc if not math.isfinite(result): raise ValueError(f"{context} must be a finite number") return result