GitHub

API reference for sse.

Server-Sent Events: a parser, channels and an encoder (Effect's encoding/Sse).

decode reads chunks of text and writes Event values; a retry: directive fails it with Retry (after the events before it), so the caller can reconnect after Retry.duration. encode writes events as SSE text and ends with a retry: line when its upstream fails with Retry. make_parser is the push parser both build on. The schema variants (decodeSchema, decodeDataSchema, encodeSchema) come with Schema.

AttributeTypeDescription
DEFAULT_MAX_EVENT_SIZE= 10 * 1024 * 1024

The most characters a pending event may hold by default (10 Mi).

Classes

Functions

make_parser

make_parser(on_parse, *, max_event_size=DEFAULT_MAX_EVENT_SIZE, dispatch_empty_named_events=False) -> Parser

A parser reporting to on_parse. max_event_size bounds the characters of the pending line and event. With dispatch_empty_named_events, an event with an event: name but no data: line is reported with empty data (WHATWG drops it; some protocols, such as graphql-sse's complete, use it).

ParameterTypeDescription
on_parseCallable[[AnyEvent], object]
max_event_sizeint = DEFAULT_MAX_EVENT_SIZE
dispatch_empty_named_eventsbool = False

Returns Parser

def make_parser(
    on_parse: Callable[[AnyEvent], object],
    *,
    max_event_size: int = DEFAULT_MAX_EVENT_SIZE,
    dispatch_empty_named_events: bool = False,
) -> Parser:
    """A parser reporting to ``on_parse``. ``max_event_size`` bounds the
    characters of the pending line and event. With
    ``dispatch_empty_named_events``, an event with an ``event:`` name but no
    ``data:`` line is reported with empty data (WHATWG drops it; some
    protocols, such as graphql-sse's ``complete``, use it)."""
    return Parser(on_parse, max_event_size, dispatch_empty_named_events)

decode

decode(*, max_event_size=DEFAULT_MAX_EVENT_SIZE, dispatch_empty_named_events=False) -> Channel[tuple[Event, ...], E | Retry | SseError, D, tuple[str, ...], E, D]

Parse chunks of SSE text into events (see make_parser). A retry: directive fails the channel with Retry once the events before it are written; an oversized event fails it with SseError.

ParameterTypeDescription
max_event_sizeint = DEFAULT_MAX_EVENT_SIZE
dispatch_empty_named_eventsbool = False

Returns Channel[tuple[Event, ...], E | Retry | SseError, D, tuple[str, ...], E, D]

def decode[E = Never, D = None](
    *,
    max_event_size: int = DEFAULT_MAX_EVENT_SIZE,
    dispatch_empty_named_events: bool = False,
) -> Channel[tuple[Event, ...], E | Retry | SseError, D, tuple[str, ...], E, D]:
    """Parse chunks of SSE text into events (see ``make_parser``). A
    ``retry:`` directive fails the channel with ``Retry`` once the events
    before it are written; an oversized event fails it with ``SseError``."""

    def build(upstream: AnyPull) -> AnyPull:
        events: list[Event] = []
        retry: Retry | None = None

        def parsed(event: AnyEvent) -> None:
            nonlocal retry
            if isinstance(event, Retry):
                retry = event
            else:
                events.append(event)

        parser = make_parser(
            parsed,
            max_event_size=max_event_size,
            dispatch_empty_named_events=dispatch_empty_named_events,
        )

        def feed(chunk: tuple[str, ...]) -> AnyPull:
            for text in chunk:
                error = parser.feed(text)
                if error is not None:
                    return _FailCause(fail_cause(error))
            return _Succeed(None)

        pump = upstream.flat_map(feed)

        def loop() -> AnyPull:
            if events:
                out = tuple(events)
                events.clear()
                return _Succeed(out)
            if retry is not None:
                return _FailCause(fail_cause(retry))
            return pump.flat_map(lambda _: loop())

        return _Suspend(loop)

    channel: Channel[tuple[Event, ...], E | Retry | SseError, D, tuple[str, ...], E, D] = (
        from_transform(lambda upstream, _scope: _Sync(lambda: build(upstream)))
    )
    return channel

write

write(event) -> str

The SSE text of event (Effect's encoder.write): id:, event: (unless "message") and one data: line per line of data, then a blank line; a Retry is a retry: line in milliseconds.

ParameterTypeDescription
eventAnyEvent

Returns str

def write(event: AnyEvent) -> str:
    """The SSE text of ``event`` (Effect's ``encoder.write``): ``id:``,
    ``event:`` (unless ``"message"``) and one ``data:`` line per line of
    data, then a blank line; a ``Retry`` is a ``retry:`` line in
    milliseconds."""
    if isinstance(event, Retry):
        millis = to_millis(event.duration)
        return f"retry: {int(millis) if millis.is_integer() else millis}\n\n"
    text = "" if event.id is None else f"id: {event.id}\n"
    if event.event != "message":
        text += f"event: {event.event}\n"
    return text + "data: " + _DATA_BREAK.sub("\ndata: ", event.data) + "\n\n"

encode

encode() -> Channel[tuple[str, ...], E, None, tuple[Event, ...], E | Retry, D]

Write events as SSE text. When the upstream fails with Retry, write its retry: line and end. Pipe a stream that cannot fail with Retry through it with pipe_through_channel_or_fail.

Returns Channel[tuple[str, ...], E, None, tuple[Event, ...], E | Retry, D]

def encode[E = Never, D = None]() -> Channel[
    tuple[str, ...], E, None, tuple[Event, ...], E | Retry, D
]:
    """Write events as SSE text. When the upstream fails with ``Retry``,
    write its ``retry:`` line and end. Pipe a stream that cannot fail with
    ``Retry`` through it with ``pipe_through_channel_or_fail``."""

    def build(upstream: AnyPull) -> AnyPull:
        retried = False

        def on_retry(retry: Any) -> AnyPull:
            nonlocal retried
            retried = True
            return _Succeed((write(retry),))

        written = upstream.map(lambda chunk: tuple(map(write, chunk)))
        pull = catch_done(written.catch_if(_is_retry, on_retry), lambda _: end_with(None))
        return _Suspend(lambda: end_with(None) if retried else pull)

    channel: Channel[tuple[str, ...], E, None, tuple[Event, ...], E | Retry, D] = from_transform(
        lambda upstream, _scope: _Sync(lambda: build(upstream))
    )
    return channel