Remove render_to_sx from public API: enforce sx_call for all service code
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m44s

Replace ~250 render_to_sx calls across all services with sync sx_call,
converting many async functions to sync where no other awaits remained.
Make render_to_sx/render_to_sx_with_env private (_render_to_sx).
Add (post-header-ctx) IO primitive and shared post/post-admin defmacros.
Convert built-in post/post-admin layouts from Python to register_sx_layout
with .sx defcomps. Remove dead post_admin_mobile_nav_sx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 19:30:45 +00:00
parent 57e0d0c341
commit 959e63d440
61 changed files with 1352 additions and 1208 deletions

View File

@@ -2,15 +2,15 @@
from __future__ import annotations
from shared.sx.helpers import (
render_to_sx, SxExpr,
sx_call, SxExpr,
)
async def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> str:
def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> str:
"""Build nav link items as sx."""
parts = []
for label, href in items:
parts.append(await render_to_sx("nav-link",
parts.append(sx_call("nav-link",
href=href, label=label,
is_selected="true" if current == label else None,
select_colours="aria-selected:bg-violet-200 aria-selected:text-violet-900",
@@ -18,54 +18,54 @@ async def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None
return "(<> " + " ".join(parts) + ")"
async def _doc_nav_sx(items: list[tuple[str, str]], current: str) -> str:
def _doc_nav_sx(items: list[tuple[str, str]], current: str) -> str:
"""Build the in-page doc navigation pills."""
items_sx = " ".join(
f'(list "{label}" "{href}")'
for label, href in items
)
return await render_to_sx("doc-nav", items=SxExpr(f"(list {items_sx})"), current=current)
return sx_call("doc-nav", items=SxExpr(f"(list {items_sx})"), current=current)
async def _attr_table_sx(title: str, attrs: list[tuple[str, str, bool]]) -> str:
def _attr_table_sx(title: str, attrs: list[tuple[str, str, bool]]) -> str:
"""Build an attribute reference table."""
from content.pages import ATTR_DETAILS
rows = []
for attr, desc, exists in attrs:
href = f"/reference/attributes/{attr}" if exists and attr in ATTR_DETAILS else None
rows.append(await render_to_sx("doc-attr-row", attr=attr, description=desc,
rows.append(sx_call("doc-attr-row", attr=attr, description=desc,
exists="true" if exists else None,
href=href))
rows_sx = "(<> " + " ".join(rows) + ")"
return await render_to_sx("doc-attr-table", title=title, rows=SxExpr(rows_sx))
return sx_call("doc-attr-table", title=title, rows=SxExpr(rows_sx))
async def _headers_table_sx(title: str, headers: list[tuple[str, str, str]]) -> str:
def _headers_table_sx(title: str, headers: list[tuple[str, str, str]]) -> str:
"""Build a headers reference table."""
rows = []
for name, value, desc in headers:
rows.append(await render_to_sx("doc-headers-row",
rows.append(sx_call("doc-headers-row",
name=name, value=value, description=desc))
rows_sx = "(<> " + " ".join(rows) + ")"
return await render_to_sx("doc-headers-table", title=title, rows=SxExpr(rows_sx))
return sx_call("doc-headers-table", title=title, rows=SxExpr(rows_sx))
async def _primitives_section_sx() -> str:
def _primitives_section_sx() -> str:
"""Build the primitives section."""
from content.pages import PRIMITIVES
parts = []
for category, prims in PRIMITIVES.items():
prims_sx = " ".join(f'"{p}"' for p in prims)
parts.append(await render_to_sx("doc-primitives-table",
parts.append(sx_call("doc-primitives-table",
category=category,
primitives=SxExpr(f"(list {prims_sx})")))
return " ".join(parts)
async def _sx_header_sx(nav: str | None = None, *, child: str | None = None) -> str:
def _sx_header_sx(nav: str | None = None, *, child: str | None = None) -> str:
"""Build the sx docs menu-row."""
label_sx = await render_to_sx("sx-docs-label")
return await render_to_sx("menu-row-sx",
label_sx = sx_call("sx-docs-label")
return sx_call("menu-row-sx",
id="sx-row", level=1, colour="violet",
link_href="/", link_label="sx",
link_label_content=SxExpr(label_sx),
@@ -75,40 +75,40 @@ async def _sx_header_sx(nav: str | None = None, *, child: str | None = None) ->
)
async def _docs_nav_sx(current: str | None = None) -> str:
def _docs_nav_sx(current: str | None = None) -> str:
from content.pages import DOCS_NAV
return await _nav_items_sx(DOCS_NAV, current)
return _nav_items_sx(DOCS_NAV, current)
async def _reference_nav_sx(current: str | None = None) -> str:
def _reference_nav_sx(current: str | None = None) -> str:
from content.pages import REFERENCE_NAV
return await _nav_items_sx(REFERENCE_NAV, current)
return _nav_items_sx(REFERENCE_NAV, current)
async def _protocols_nav_sx(current: str | None = None) -> str:
def _protocols_nav_sx(current: str | None = None) -> str:
from content.pages import PROTOCOLS_NAV
return await _nav_items_sx(PROTOCOLS_NAV, current)
return _nav_items_sx(PROTOCOLS_NAV, current)
async def _examples_nav_sx(current: str | None = None) -> str:
def _examples_nav_sx(current: str | None = None) -> str:
from content.pages import EXAMPLES_NAV
return await _nav_items_sx(EXAMPLES_NAV, current)
return _nav_items_sx(EXAMPLES_NAV, current)
async def _essays_nav_sx(current: str | None = None) -> str:
def _essays_nav_sx(current: str | None = None) -> str:
from content.pages import ESSAYS_NAV
return await _nav_items_sx(ESSAYS_NAV, current)
return _nav_items_sx(ESSAYS_NAV, current)
async def _main_nav_sx(current_section: str | None = None) -> str:
def _main_nav_sx(current_section: str | None = None) -> str:
from content.pages import MAIN_NAV
return await _nav_items_sx(MAIN_NAV, current_section)
return _nav_items_sx(MAIN_NAV, current_section)
async def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
selected: str = "") -> str:
"""Build the level-2 sub-section menu-row."""
return await render_to_sx("menu-row-sx",
return sx_call("menu-row-sx",
id="sx-sub-row", level=2, colour="violet",
link_href=sub_href, link_label=sub_label,
selected=selected or None,