Source code for aeat.core.telemetry._emit

"""The single remote-telemetry emit call site.

:func:`~core.telemetry.emit_telemetry_event` is the ONLY function a producer
calls to emit a remote-eligible telemetry event. It composes the consent gate
(:func:`~core.telemetry.telemetry_emit_permitted`) with a pluggable
:class:`~core.telemetry.TelemetrySink`; when the gate refuses, emission is a
pure no-op -- nothing is constructed, nothing is written, nothing is sent.

No network-transmitting sink exists in this slice. The only implementation
here, :class:`~core.telemetry.LocalNoopTelemetrySink`, deliberately does
nothing observable: it exists so callers and tests can exercise the full
gate-then-emit sequence without a real transport, proving the payload a future
HTTP sink would send is already the allowlisted, scrubbed shape
(``2026-07-04-remote-telemetry-adr``).

See Also:
    :func:`~core.telemetry.build_telemetry_payload`
        Constructs the allowlisted payload consumed here.
    :class:`~core.telemetry.HttpTelemetrySink`
        Optional network transport that implements the same sink protocol.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol

from ._consent import telemetry_emit_permitted

if TYPE_CHECKING:
    from ..config import Settings
    from ._schema import TelemetryEventPayload

__all__ = ["LocalNoopTelemetrySink", "TelemetrySink", "emit_telemetry_event"]


[docs] class TelemetrySink(Protocol): """A destination for an allowlisted :class:`~core.telemetry.TelemetryEventPayload`. Implementations of this protocol are the only place a telemetry payload can leave this function's caller. No implementation in this slice transmits over the network; a future slice adds the real HTTP sink behind this same protocol once the gate and schema have been reviewed. """
[docs] def send(self, payload: TelemetryEventPayload) -> None: """Deliver ``payload`` to this sink's destination.""" ...
[docs] class LocalNoopTelemetrySink: """A sink that intentionally does nothing. Used as the default sink in this slice: there is no remote transport yet, so the safe default is to accept the already-gated, already-allowlisted payload and discard it. This lets :func:`~core.telemetry.emit_telemetry_event` be called end-to-end (gate, schema validation, sink dispatch) without any observable network or disk side effect. """
[docs] def send(self, payload: TelemetryEventPayload) -> None: """Discard ``payload``. Deliberately a no-op.""" return None
[docs] def emit_telemetry_event( payload: TelemetryEventPayload, *, settings: Settings, acknowledged: bool, sink: TelemetrySink | None = None, ) -> bool: """Emit ``payload`` to ``sink`` if and only if the consent gate permits it. Args: payload: The already-constructed, already-allowlisted :class:`~core.telemetry.TelemetryEventPayload` (build it with :func:`~core.telemetry.build_telemetry_payload` so its counters/timings are schema-validated). settings: Resolved deployment settings. acknowledged: Whether the operator acknowledged remote telemetry for this specific invocation. Never sticky; re-affirm at every call site. sink: The destination to hand the payload to when permitted. Defaults to :class:`~core.telemetry.LocalNoopTelemetrySink` (no transport exists in this slice). Returns: ``True`` when the event was handed to the sink; ``False`` when the consent gate refused and emission was a no-op. """ if not telemetry_emit_permitted(settings, acknowledged=acknowledged): return False resolved_sink = sink if sink is not None else LocalNoopTelemetrySink() resolved_sink.send(payload) return True