SSE Client with Reconnect Parser
Parse text/event-stream frames into structured events and wrap the HTTP read loop with bounded reconnect attempts and backoff.
Code
import json
import time
import urllib.error
import urllib.request
from collections.abc import Iterable, Iterator
def parse_sse(lines: Iterable[str]) -> Iterator[dict[str, str]]:
event: dict[str, str] = {}
data: list[str] = []
for raw in lines:
line = raw.rstrip("\r\n")
if not line:
if data:
event["data"] = "\n".join(data)
yield event
event, data = {}, []
continue
if line.startswith(":"):
continue
field, _, value = line.partition(":")
value = value.lstrip(" ")
if field == "data":
data.append(value)
elif field:
event[field] = value
if data:
event["data"] = "\n".join(data)
yield event
def iter_sse(
url: str, retries: int = 3, backoff: float = 0.5
) -> Iterator[dict[str, str]]:
attempt = 0
while True:
try:
request = urllib.request.Request(
url, headers={"Accept": "text/event-stream"}
)
with urllib.request.urlopen(request, timeout=30) as response:
yield from parse_sse(line.decode("utf-8") for line in response)
return
except (OSError, urllib.error.URLError):
attempt += 1
if attempt > retries:
raise
time.sleep(backoff * attempt)
FIXTURE = """event: build
id: 7
data: {"status": "ok"}
data: second line
"""
if __name__ == "__main__":
events = list(parse_sse(FIXTURE.splitlines()))
print(events)
for event in events:
first_line = event["data"].splitlines()[0]
print(event.get("event", "message"), json.loads(first_line))
Report a problem with this exampleOpens a pre-filled issue on GitHub.