"""Small deterministic helpers.""" from __future__ import annotations from dataclasses import is_dataclass, asdict from datetime import datetime, timezone from enum import Enum import hashlib import json from typing import Any def utc_now_iso() -> str: return datetime.now(timezone.utc).replace(microsecond=0).isoformat() def stable_digest(value: Any, *, length: int = 12) -> str: encoded = json.dumps(to_plain(value), sort_keys=True, separators=(",", ":")).encode() return hashlib.sha256(encoded).hexdigest()[:length] def compact_dict(mapping: dict[str, Any]) -> dict[str, Any]: return { key: to_plain(value) for key, value in mapping.items() if value not in (None, "", [], {}, ()) } def to_plain(value: Any) -> Any: if isinstance(value, Enum): return value.value if is_dataclass(value): return to_plain(asdict(value)) if isinstance(value, dict): return { str(key): to_plain(item) for key, item in value.items() if item not in (None, "", [], {}, ()) } if isinstance(value, (list, tuple, set)): return [to_plain(item) for item in value] return value def parse_iso_datetime(value: str | None) -> datetime | None: if not value: return None raw = value.strip() if raw.endswith("Z"): raw = raw[:-1] + "+00:00" try: parsed = datetime.fromisoformat(raw) except ValueError: return None if parsed.tzinfo is None: return parsed.replace(tzinfo=timezone.utc) return parsed.astimezone(timezone.utc)