mirror of
https://github.com/smittix/intercept.git
synced 2026-06-11 23:43:30 -07:00
v2.26.0: fix SSE fanout crash and branded logo FOUC
- Fix SSE fanout thread AttributeError when source queue is None during interpreter shutdown by snapshotting to local variable with null guard - Fix branded "i" logo rendering oversized on first page load (FOUC) by adding inline width/height to SVG elements across 10 templates - Bump version to 2.26.0 in config.py, pyproject.toml, and CHANGELOG.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+57
-51
@@ -2,12 +2,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import queue
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Generator
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Callable, Generator
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -29,6 +31,12 @@ def _run_fanout(channel: _QueueFanoutChannel) -> None:
|
||||
idle_drain_batch = 512
|
||||
|
||||
while True:
|
||||
src = channel.source_queue
|
||||
if src is None:
|
||||
# Source queue was cleared (e.g. during interpreter shutdown).
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
|
||||
with channel.lock:
|
||||
subscribers = tuple(channel.subscribers)
|
||||
|
||||
@@ -39,7 +47,7 @@ def _run_fanout(channel: _QueueFanoutChannel) -> None:
|
||||
drained = 0
|
||||
for _ in range(idle_drain_batch):
|
||||
try:
|
||||
channel.source_queue.get_nowait()
|
||||
src.get_nowait()
|
||||
drained += 1
|
||||
except queue.Empty:
|
||||
break
|
||||
@@ -49,7 +57,7 @@ def _run_fanout(channel: _QueueFanoutChannel) -> None:
|
||||
continue
|
||||
|
||||
try:
|
||||
msg = channel.source_queue.get(timeout=channel.source_timeout)
|
||||
msg = src.get(timeout=channel.source_timeout)
|
||||
except queue.Empty:
|
||||
continue
|
||||
|
||||
@@ -153,10 +161,8 @@ def sse_stream_fanout(
|
||||
msg = subscriber.get(timeout=timeout)
|
||||
last_keepalive = time.time()
|
||||
if on_message and isinstance(msg, dict):
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
on_message(msg)
|
||||
except Exception:
|
||||
pass
|
||||
yield format_sse(msg)
|
||||
except queue.Empty:
|
||||
now = time.time()
|
||||
@@ -174,7 +180,7 @@ def sse_stream(
|
||||
stop_check: Callable[[], bool] | None = None,
|
||||
channel_key: str | None = None,
|
||||
) -> Generator[str, None, None]:
|
||||
"""
|
||||
"""
|
||||
Generate SSE stream from a queue.
|
||||
|
||||
Args:
|
||||
@@ -195,47 +201,47 @@ def sse_stream(
|
||||
keepalive_interval=keepalive_interval,
|
||||
stop_check=stop_check,
|
||||
)
|
||||
|
||||
|
||||
def format_sse(data: dict[str, Any] | str, event: str | None = None) -> str:
|
||||
"""
|
||||
Format data as SSE message.
|
||||
|
||||
Args:
|
||||
data: Data to send (will be JSON encoded if dict)
|
||||
event: Optional event name
|
||||
|
||||
Returns:
|
||||
SSE formatted string
|
||||
"""
|
||||
if isinstance(data, dict):
|
||||
data = json.dumps(data)
|
||||
|
||||
lines = []
|
||||
if event:
|
||||
lines.append(f"event: {event}")
|
||||
lines.append(f"data: {data}")
|
||||
lines.append("")
|
||||
lines.append("")
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def clear_queue(q: queue.Queue) -> int:
|
||||
"""
|
||||
Clear all items from a queue.
|
||||
|
||||
Args:
|
||||
q: Queue to clear
|
||||
|
||||
Returns:
|
||||
Number of items cleared
|
||||
"""
|
||||
count = 0
|
||||
while True:
|
||||
try:
|
||||
q.get_nowait()
|
||||
count += 1
|
||||
except queue.Empty:
|
||||
break
|
||||
return count
|
||||
|
||||
|
||||
def format_sse(data: dict[str, Any] | str, event: str | None = None) -> str:
|
||||
"""
|
||||
Format data as SSE message.
|
||||
|
||||
Args:
|
||||
data: Data to send (will be JSON encoded if dict)
|
||||
event: Optional event name
|
||||
|
||||
Returns:
|
||||
SSE formatted string
|
||||
"""
|
||||
if isinstance(data, dict):
|
||||
data = json.dumps(data)
|
||||
|
||||
lines = []
|
||||
if event:
|
||||
lines.append(f"event: {event}")
|
||||
lines.append(f"data: {data}")
|
||||
lines.append("")
|
||||
lines.append("")
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def clear_queue(q: queue.Queue) -> int:
|
||||
"""
|
||||
Clear all items from a queue.
|
||||
|
||||
Args:
|
||||
q: Queue to clear
|
||||
|
||||
Returns:
|
||||
Number of items cleared
|
||||
"""
|
||||
count = 0
|
||||
while True:
|
||||
try:
|
||||
q.get_nowait()
|
||||
count += 1
|
||||
except queue.Empty:
|
||||
break
|
||||
return count
|
||||
|
||||
Reference in New Issue
Block a user