Coverage for src / lexigram / contracts / web / sse.py: 100%
13 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
1"""Server-Sent Events (SSE) contracts.
3Defines the data transfer types and factory protocol for Server-Sent Events
4so that packages outside of ``lexigram-web`` can produce SSE responses without
5creating a cross-extension dependency.
7Usage::
9 from lexigram.contracts.web.sse import ServerSentEvent, SseResponseFactoryProtocol
11 async def my_handler(
12 factory: SseResponseFactoryProtocol,
13 event_generator: AsyncGenerator[dict[str, Any], None],
14 ) -> Any:
15 async def wrapped():
16 async for event_data in event_generator:
17 yield ServerSentEvent(
18 data=event_data.get("data", event_data),
19 event=event_data.get("event"),
20 event_id=event_data.get("id"),
21 retry=event_data.get("retry"),
22 )
24 return factory.create_response(wrapped())
25"""
27from __future__ import annotations
29from dataclasses import dataclass, field
30from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
32if TYPE_CHECKING:
33 from collections.abc import AsyncGenerator
36@dataclass(frozen=True)
37class ServerSentEvent:
38 """Data transfer object for a single Server-Sent Event.
40 Serialisation / encoding is the responsibility of the web-layer
41 implementation (e.g. ``lexigram-web``).
43 Attributes:
44 data: Event payload — any JSON-serialisable object or plain string.
45 event: Optional event type name (sent as ``event:`` field).
46 event_id: Optional event identifier (sent as ``id:`` field).
47 retry: Optional reconnect interval in milliseconds.
48 """
50 data: Any
51 event: str | None = field(default=None)
52 event_id: str | None = field(default=None)
53 retry: int | None = field(default=None)
56@runtime_checkable
57class SseResponseFactoryProtocol(Protocol):
58 """Factory that wraps an async generator of ``ServerSentEvent`` objects
59 into a framework HTTP response suitable for streaming SSE.
61 Implementations are provided by ``lexigram-web`` and registered in the
62 DI container during ``WebProvider.boot()``.
64 Example::
66 factory = await container.resolve(SseResponseFactoryProtocol)
67 response = factory.create_response(my_event_generator())
68 """
70 def create_response(
71 self,
72 generator: AsyncGenerator[ServerSentEvent, None],
73 *,
74 status_code: int = 200,
75 headers: dict[str, str] | None = None,
76 ) -> Any:
77 """Wrap *generator* into a streaming SSE HTTP response.
79 Args:
80 generator: Async generator yielding ``ServerSentEvent`` objects.
81 status_code: HTTP status code for the response (default 200).
82 headers: Optional additional response headers.
84 Returns:
85 A framework-specific streaming response object.
86 """
87 ...
90__all__ = [
91 "ServerSentEvent",
92 "SseResponseFactoryProtocol",
93]