41 lines
1.6 KiB
Python
41 lines
1.6 KiB
Python
"""Best-effort accessors for the single-writer stream fence.
|
|
|
|
The fence lives on ``AIAgent`` (``_claim_stream_writer`` / ``_stream_writer_is_current``)
|
|
but is used from other streaming modules. Calling it directly would turn an
|
|
*additive* safety net into a fatal AttributeError on a partially-updated checkout,
|
|
hot-reloaded gateway, duck-typed agent, or test double (a cron job died this way).
|
|
The fence may only drop a *provably* superseded stream, never the sole writer, so
|
|
when it is unavailable or raises the correct degradation is "no fence": keep streaming.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def claim_stream_writer(agent: Any) -> int:
|
|
"""Claim the delta sink for this stream attempt; ``0`` (never fenced) when the agent lacks the fence or the claim raised."""
|
|
claim = getattr(agent, "_claim_stream_writer", None)
|
|
if callable(claim):
|
|
try:
|
|
return int(claim())
|
|
except Exception:
|
|
logger.debug("stream single-writer: claim failed; proceeding unfenced", exc_info=True)
|
|
return 0
|
|
|
|
|
|
def stream_writer_is_current(agent: Any, token: int) -> bool:
|
|
"""True when ``token`` is still the active writer; a falsy token or a fence-less agent cannot prove supersession, so True."""
|
|
if not token:
|
|
return True
|
|
is_current = getattr(agent, "_stream_writer_is_current", None)
|
|
if callable(is_current):
|
|
try:
|
|
return bool(is_current(token))
|
|
except Exception:
|
|
logger.debug("stream single-writer: is_current check failed; treating as current", exc_info=True)
|
|
return True
|