Replace sx_call() with render_to_sx() across all services
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m6s

Python no longer generates s-expression strings. All SX rendering now
goes through render_to_sx() which builds AST from native Python values
and evaluates via async_eval_to_sx() — no SX string literals in Python.

- Add render_to_sx()/render_to_html() infrastructure in shared/sx/helpers.py
- Add (abort status msg) IO primitive in shared/sx/primitives_io.py
- Convert all 9 services: ~650 sx_call() invocations replaced
- Convert shared helpers (root_header_sx, full_page_sx, etc.) to async
- Fix likes service import bug (likes.models → models)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 00:08:33 +00:00
parent 0554f8a113
commit e085fe43b4
51 changed files with 1824 additions and 1742 deletions

View File

@@ -6,7 +6,7 @@ from datetime import datetime
from shared.sx.jinja_bridge import load_service_components
from shared.sx.helpers import (
sx_call, SxExpr,
render_to_sx, SxExpr,
root_header_sx, full_page_sx, header_child_sx,
)
@@ -50,9 +50,9 @@ def _filter_tests(tests: list[dict], active_filter: str | None,
# Results partial
# ---------------------------------------------------------------------------
def test_detail_sx(test: dict) -> str:
async def test_detail_sx(test: dict) -> str:
"""Return s-expression wire format for a test detail view."""
inner = sx_call(
inner = await render_to_sx(
"test-detail",
nodeid=test["nodeid"],
outcome=test["outcome"],
@@ -70,10 +70,10 @@ def test_detail_sx(test: dict) -> str:
# Sx-native versions — return sx source (not HTML)
# ---------------------------------------------------------------------------
def _test_header_sx(ctx: dict, active_service: str | None = None) -> str:
async def _test_header_sx(ctx: dict, active_service: str | None = None) -> str:
"""Build the Tests menu-row as sx call."""
nav = _service_nav_sx(ctx, active_service)
return sx_call("menu-row-sx",
nav = await _service_nav_sx(ctx, active_service)
return await render_to_sx("menu-row-sx",
id="test-row", level=1, colour="sky",
link_href="/", link_label="Tests", icon="fa fa-flask",
nav=SxExpr(nav),
@@ -81,17 +81,17 @@ def _test_header_sx(ctx: dict, active_service: str | None = None) -> str:
)
def _service_nav_sx(ctx: dict, active_service: str | None = None) -> str:
async def _service_nav_sx(ctx: dict, active_service: str | None = None) -> str:
"""Service filter nav as sx."""
from runner import _SERVICE_ORDER
parts = []
parts.append(sx_call("nav-link",
parts.append(await render_to_sx("nav-link",
href="/", label="all",
is_selected="true" if not active_service else None,
select_colours="aria-selected:bg-sky-200 aria-selected:text-sky-900",
))
for svc in _SERVICE_ORDER:
parts.append(sx_call("nav-link",
parts.append(await render_to_sx("nav-link",
href=f"/?service={svc}", label=svc,
is_selected="true" if active_service == svc else None,
select_colours="aria-selected:bg-sky-200 aria-selected:text-sky-900",
@@ -99,19 +99,19 @@ def _service_nav_sx(ctx: dict, active_service: str | None = None) -> str:
return "(<> " + " ".join(parts) + ")"
def _header_stack_sx(ctx: dict, active_service: str | None = None) -> str:
async def _header_stack_sx(ctx: dict, active_service: str | None = None) -> str:
"""Full header stack as sx."""
hdr = root_header_sx(ctx)
inner = _test_header_sx(ctx, active_service)
child = header_child_sx(inner)
hdr = await root_header_sx(ctx)
inner = await _test_header_sx(ctx, active_service)
child = await header_child_sx(inner)
return "(<> " + hdr + " " + child + ")"
def _test_rows_sx(tests: list[dict]) -> str:
async def _test_rows_sx(tests: list[dict]) -> str:
"""Render all test result rows as sx."""
parts = []
for t in tests:
parts.append(sx_call("test-row",
parts.append(await render_to_sx("test-row",
nodeid=t["nodeid"],
outcome=t["outcome"],
duration=str(t["duration"]),
@@ -120,46 +120,46 @@ def _test_rows_sx(tests: list[dict]) -> str:
return "(<> " + " ".join(parts) + ")"
def _grouped_rows_sx(tests: list[dict]) -> str:
async def _grouped_rows_sx(tests: list[dict]) -> str:
"""Test rows grouped by service as sx."""
from runner import group_tests_by_service
sections = group_tests_by_service(tests)
parts = []
for sec in sections:
parts.append(sx_call("test-service-header",
parts.append(await render_to_sx("test-service-header",
service=sec["service"],
total=str(sec["total"]),
passed=str(sec["passed"]),
failed=str(sec["failed"]),
))
parts.append(_test_rows_sx(sec["tests"]))
parts.append(await _test_rows_sx(sec["tests"]))
return "(<> " + " ".join(parts) + ")"
def _results_partial_sx(result: dict | None, running: bool, csrf: str,
async def _results_partial_sx(result: dict | None, running: bool, csrf: str,
active_filter: str | None = None,
active_service: str | None = None) -> str:
"""Results section as sx."""
if running and not result:
summary = sx_call("test-summary",
summary = await render_to_sx("test-summary",
status="running", passed="0", failed="0", errors="0",
skipped="0", total="0", duration="...",
last_run="in progress", running=True, csrf=csrf,
active_filter=active_filter,
)
return "(<> " + summary + " " + sx_call("test-running-indicator") + ")"
return "(<> " + summary + " " + await render_to_sx("test-running-indicator") + ")"
if not result:
summary = sx_call("test-summary",
summary = await render_to_sx("test-summary",
status=None, passed="0", failed="0", errors="0",
skipped="0", total="0", duration="0",
last_run="never", running=running, csrf=csrf,
active_filter=active_filter,
)
return "(<> " + summary + " " + sx_call("test-no-results") + ")"
return "(<> " + summary + " " + await render_to_sx("test-no-results") + ")"
status = "running" if running else result["status"]
summary = sx_call("test-summary",
summary = await render_to_sx("test-summary",
status=status,
passed=str(result["passed"]),
failed=str(result["failed"]),
@@ -174,16 +174,16 @@ def _results_partial_sx(result: dict | None, running: bool, csrf: str,
)
if running:
return "(<> " + summary + " " + sx_call("test-running-indicator") + ")"
return "(<> " + summary + " " + await render_to_sx("test-running-indicator") + ")"
tests = result.get("tests", [])
tests = _filter_tests(tests, active_filter, active_service)
if not tests:
return "(<> " + summary + " " + sx_call("test-no-results") + ")"
return "(<> " + summary + " " + await render_to_sx("test-no-results") + ")"
has_failures = result["failed"] > 0 or result["errors"] > 0
rows = _grouped_rows_sx(tests)
table = sx_call("test-results-table",
rows = await _grouped_rows_sx(tests)
table = await render_to_sx("test-results-table",
rows=SxExpr(rows),
has_failures=str(has_failures).lower(),
)
@@ -203,10 +203,10 @@ async def render_dashboard_page_sx(ctx: dict, result: dict | None,
active_filter: str | None = None,
active_service: str | None = None) -> str:
"""Full page: test dashboard (sx wire format)."""
hdr = _header_stack_sx(ctx, active_service)
inner = _results_partial_sx(result, running, csrf, active_filter, active_service)
hdr = await _header_stack_sx(ctx, active_service)
inner = await _results_partial_sx(result, running, csrf, active_filter, active_service)
content = _wrap_results_div_sx(inner, running)
return full_page_sx(ctx, header_rows=hdr, content=content)
return await full_page_sx(ctx, header_rows=hdr, content=content)
async def render_results_partial_sx(result: dict | None, running: bool,
@@ -214,25 +214,27 @@ async def render_results_partial_sx(result: dict | None, running: bool,
active_filter: str | None = None,
active_service: str | None = None) -> str:
"""HTMX partial: results section (sx wire format)."""
inner = _results_partial_sx(result, running, csrf, active_filter, active_service)
inner = await _results_partial_sx(result, running, csrf, active_filter, active_service)
return _wrap_results_div_sx(inner, running)
async def render_test_detail_page_sx(ctx: dict, test: dict) -> str:
"""Full page: test detail (sx wire format)."""
root_hdr = root_header_sx(ctx)
test_row = _test_header_sx(ctx)
detail_row = sx_call("menu-row-sx",
root_hdr = await root_header_sx(ctx)
test_row = await _test_header_sx(ctx)
detail_row = await render_to_sx("menu-row-sx",
id="test-detail-row", level=2, colour="sky",
link_href=f"/test/{test['nodeid']}",
link_label=test["nodeid"].rsplit("::", 1)[-1],
)
inner = "(<> " + test_row + " " + header_child_sx(detail_row, id="test-header-child") + ")"
hdr = "(<> " + root_hdr + " " + header_child_sx(inner) + ")"
content = sx_call("test-detail",
hdr_child_detail = await header_child_sx(detail_row, id="test-header-child")
inner = "(<> " + test_row + " " + hdr_child_detail + ")"
hdr_child_inner = await header_child_sx(inner)
hdr = "(<> " + root_hdr + " " + hdr_child_inner + ")"
content = await render_to_sx("test-detail",
nodeid=test["nodeid"],
outcome=test["outcome"],
duration=str(test["duration"]),
longrepr=test.get("longrepr", ""),
)
return full_page_sx(ctx, header_rows=hdr, content=content)
return await full_page_sx(ctx, header_rows=hdr, content=content)