Rebrand sexp → sx across web platform (173 files)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 11m37s

Rename all sexp directories, files, identifiers, and references to sx.
artdag/ excluded (separate media processing DSL).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 11:06:57 +00:00
parent 17cebe07e7
commit e8bc228c7f
174 changed files with 3126 additions and 2952 deletions

View File

@@ -26,7 +26,7 @@ COPY shared/ ./shared/
COPY test/ ./test-app-tmp/
# Move service files into /app (flatten), but keep Dockerfile.* in place
RUN cp -r test-app-tmp/app.py test-app-tmp/path_setup.py \
test-app-tmp/bp test-app-tmp/sexp test-app-tmp/services \
test-app-tmp/bp test-app-tmp/sx test-app-tmp/services \
test-app-tmp/runner.py test-app-tmp/__init__.py ./ 2>/dev/null || true && \
rm -rf test-app-tmp

View File

@@ -8,7 +8,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
WORKDIR /app
# Node.js for sexp.js parity tests
# Node.js for sx.js parity tests
RUN apt-get update && apt-get install -y --no-install-recommends nodejs && \
rm -rf /var/lib/apt/lists/*

View File

@@ -1,9 +1,9 @@
from __future__ import annotations
import path_setup # noqa: F401
import sexp.sexp_components as sexp_components # noqa: F401
import sx.sx_components as sx_components # noqa: F401
from shared.infrastructure.factory import create_base_app
from shared.sexp.jinja_bridge import render
from shared.sx.jinja_bridge import render
from bp import register_dashboard
from services import register_domain_services
@@ -36,7 +36,7 @@ def create_app() -> "Quart":
domain_services_fn=register_domain_services,
)
import sexp.sexp_components # noqa: F401
import sx.sx_components # noqa: F401
app.register_blueprint(register_dashboard(url_prefix="/"))

View File

@@ -12,9 +12,9 @@ def register(url_prefix: str = "/") -> Blueprint:
@bp.get("/")
async def index():
"""Full page dashboard with last results."""
from shared.sexp.page import get_template_context
from shared.sx.page import get_template_context
from shared.browser.app.csrf import generate_csrf_token
from sexp.sexp_components import render_dashboard_page_sexp
from sx.sx_components import render_dashboard_page_sx
import runner
ctx = await get_template_context()
@@ -24,7 +24,7 @@ def register(url_prefix: str = "/") -> Blueprint:
active_filter = request.args.get("filter")
active_service = request.args.get("service")
html = await render_dashboard_page_sexp(
html = await render_dashboard_page_sx(
ctx, result, running, csrf,
active_filter=active_filter,
active_service=active_service,
@@ -50,7 +50,7 @@ def register(url_prefix: str = "/") -> Blueprint:
@bp.get("/test/<path:nodeid>")
async def test_detail(nodeid: str):
"""Test detail view — full page or sexp wire format."""
"""Test detail view — full page or sx wire format."""
import runner
test = runner.get_test(nodeid)
@@ -61,24 +61,24 @@ def register(url_prefix: str = "/") -> Blueprint:
is_htmx = bool(request.headers.get("SX-Request") or request.headers.get("HX-Request"))
if is_htmx:
# S-expression wire format — sexp.js renders client-side
from shared.sexp.helpers import sexp_response
from sexp.sexp_components import test_detail_sexp
return sexp_response(test_detail_sexp(test))
# S-expression wire format — sx.js renders client-side
from shared.sx.helpers import sx_response
from sx.sx_components import test_detail_sx
return sx_response(test_detail_sx(test))
# Full page render (direct navigation / refresh)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_test_detail_page_sexp
from shared.sx.page import get_template_context
from sx.sx_components import render_test_detail_page_sx
ctx = await get_template_context()
html = await render_test_detail_page_sexp(ctx, test)
html = await render_test_detail_page_sx(ctx, test)
return await make_response(html, 200)
@bp.get("/results")
async def results():
"""HTMX partial — poll target for results table."""
from shared.browser.app.csrf import generate_csrf_token
from sexp.sexp_components import render_results_partial_sexp
from sx.sx_components import render_results_partial_sx
import runner
result = runner.get_results()
@@ -87,7 +87,7 @@ def register(url_prefix: str = "/") -> Blueprint:
active_filter = request.args.get("filter")
active_service = request.args.get("service")
html = await render_results_partial_sexp(
html = await render_results_partial_sx(
result, running, csrf,
active_filter=active_filter,
active_service=active_service,

View File

@@ -17,7 +17,7 @@ _running: bool = False
# Each service group runs in its own pytest subprocess with its own PYTHONPATH
_SERVICE_GROUPS: list[dict] = [
{"name": "shared", "dirs": ["shared/tests/", "shared/sexp/tests/"],
{"name": "shared", "dirs": ["shared/tests/", "shared/sx/tests/"],
"pythonpath": None},
{"name": "blog", "dirs": ["blog/tests/"], "pythonpath": "/app/blog"},
{"name": "market", "dirs": ["market/tests/"], "pythonpath": "/app/market"},

View File

@@ -4,13 +4,13 @@ from __future__ import annotations
import os
from datetime import datetime
from shared.sexp.jinja_bridge import load_service_components
from shared.sexp.helpers import (
sexp_call, SexpExpr,
root_header_sexp, full_page_sexp, header_child_sexp,
from shared.sx.jinja_bridge import load_service_components
from shared.sx.helpers import (
sx_call, SxExpr,
root_header_sx, full_page_sx, header_child_sx,
)
# Load test-specific .sexpr components at import time
# Load test-specific .sx components at import time
load_service_components(os.path.dirname(os.path.dirname(__file__)))
@@ -50,9 +50,9 @@ def _filter_tests(tests: list[dict], active_filter: str | None,
# Results partial
# ---------------------------------------------------------------------------
def test_detail_sexp(test: dict) -> str:
def test_detail_sx(test: dict) -> str:
"""Return s-expression wire format for a test detail view."""
inner = sexp_call(
inner = sx_call(
"test-detail",
nodeid=test["nodeid"],
outcome=test["outcome"],
@@ -67,31 +67,31 @@ def test_detail_sexp(test: dict) -> str:
# ---------------------------------------------------------------------------
# Sexp-native versions — return sexp source (not HTML)
# Sx-native versions — return sx source (not HTML)
# ---------------------------------------------------------------------------
def _test_header_sexp(ctx: dict, active_service: str | None = None) -> str:
"""Build the Tests menu-row as sexp call."""
nav = _service_nav_sexp(ctx, active_service)
return sexp_call("menu-row-sx",
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",
id="test-row", level=1, colour="sky",
link_href="/", link_label="Tests", icon="fa fa-flask",
nav=SexpExpr(nav),
nav=SxExpr(nav),
child_id="test-header-child",
)
def _service_nav_sexp(ctx: dict, active_service: str | None = None) -> str:
"""Service filter nav as sexp."""
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(sexp_call("nav-link",
parts.append(sx_call("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(sexp_call("nav-link",
parts.append(sx_call("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_sexp(ctx: dict, active_service: str | None = None) -> str:
return "(<> " + " ".join(parts) + ")"
def _header_stack_sexp(ctx: dict, active_service: str | None = None) -> str:
"""Full header stack as sexp."""
hdr = root_header_sexp(ctx)
inner = _test_header_sexp(ctx, active_service)
child = header_child_sexp(inner)
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)
return "(<> " + hdr + " " + child + ")"
def _test_rows_sexp(tests: list[dict]) -> str:
"""Render all test result rows as sexp."""
def _test_rows_sx(tests: list[dict]) -> str:
"""Render all test result rows as sx."""
parts = []
for t in tests:
parts.append(sexp_call("test-row",
parts.append(sx_call("test-row",
nodeid=t["nodeid"],
outcome=t["outcome"],
duration=str(t["duration"]),
@@ -120,46 +120,46 @@ def _test_rows_sexp(tests: list[dict]) -> str:
return "(<> " + " ".join(parts) + ")"
def _grouped_rows_sexp(tests: list[dict]) -> str:
"""Test rows grouped by service as sexp."""
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(sexp_call("test-service-header",
parts.append(sx_call("test-service-header",
service=sec["service"],
total=str(sec["total"]),
passed=str(sec["passed"]),
failed=str(sec["failed"]),
))
parts.append(_test_rows_sexp(sec["tests"]))
parts.append(_test_rows_sx(sec["tests"]))
return "(<> " + " ".join(parts) + ")"
def _results_partial_sexp(result: dict | None, running: bool, csrf: str,
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 sexp."""
"""Results section as sx."""
if running and not result:
summary = sexp_call("test-summary",
summary = sx_call("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 + " " + sexp_call("test-running-indicator") + ")"
return "(<> " + summary + " " + sx_call("test-running-indicator") + ")"
if not result:
summary = sexp_call("test-summary",
summary = sx_call("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 + " " + sexp_call("test-no-results") + ")"
return "(<> " + summary + " " + sx_call("test-no-results") + ")"
status = "running" if running else result["status"]
summary = sexp_call("test-summary",
summary = sx_call("test-summary",
status=status,
passed=str(result["passed"]),
failed=str(result["failed"]),
@@ -174,65 +174,65 @@ def _results_partial_sexp(result: dict | None, running: bool, csrf: str,
)
if running:
return "(<> " + summary + " " + sexp_call("test-running-indicator") + ")"
return "(<> " + summary + " " + sx_call("test-running-indicator") + ")"
tests = result.get("tests", [])
tests = _filter_tests(tests, active_filter, active_service)
if not tests:
return "(<> " + summary + " " + sexp_call("test-no-results") + ")"
return "(<> " + summary + " " + sx_call("test-no-results") + ")"
has_failures = result["failed"] > 0 or result["errors"] > 0
rows = _grouped_rows_sexp(tests)
table = sexp_call("test-results-table",
rows=SexpExpr(rows),
rows = _grouped_rows_sx(tests)
table = sx_call("test-results-table",
rows=SxExpr(rows),
has_failures=str(has_failures).lower(),
)
return "(<> " + summary + " " + table + ")"
def _wrap_results_div_sexp(inner: str, running: bool) -> str:
"""Wrap results in a div with HTMX polling (sexp)."""
def _wrap_results_div_sx(inner: str, running: bool) -> str:
"""Wrap results in a div with HTMX polling (sx)."""
attrs = ':id "test-results" :class "space-y-6 p-4"'
if running:
attrs += ' :sx-get "/results" :sx-trigger "every 2s" :sx-swap "outerHTML"'
return f'(div {attrs} {inner})'
async def render_dashboard_page_sexp(ctx: dict, result: dict | None,
async def render_dashboard_page_sx(ctx: dict, result: dict | None,
running: bool, csrf: str,
active_filter: str | None = None,
active_service: str | None = None) -> str:
"""Full page: test dashboard (sexp wire format)."""
hdr = _header_stack_sexp(ctx, active_service)
inner = _results_partial_sexp(result, running, csrf, active_filter, active_service)
content = _wrap_results_div_sexp(inner, running)
return full_page_sexp(ctx, header_rows=hdr, content=content)
"""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)
content = _wrap_results_div_sx(inner, running)
return full_page_sx(ctx, header_rows=hdr, content=content)
async def render_results_partial_sexp(result: dict | None, running: bool,
async def render_results_partial_sx(result: dict | None, running: bool,
csrf: str,
active_filter: str | None = None,
active_service: str | None = None) -> str:
"""HTMX partial: results section (sexp wire format)."""
inner = _results_partial_sexp(result, running, csrf, active_filter, active_service)
return _wrap_results_div_sexp(inner, running)
"""HTMX partial: results section (sx wire format)."""
inner = _results_partial_sx(result, running, csrf, active_filter, active_service)
return _wrap_results_div_sx(inner, running)
async def render_test_detail_page_sexp(ctx: dict, test: dict) -> str:
"""Full page: test detail (sexp wire format)."""
root_hdr = root_header_sexp(ctx)
test_row = _test_header_sexp(ctx)
detail_row = sexp_call("menu-row-sx",
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",
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_sexp(detail_row, id="test-header-child") + ")"
hdr = "(<> " + root_hdr + " " + header_child_sexp(inner) + ")"
content = sexp_call("test-detail",
inner = "(<> " + test_row + " " + header_child_sx(detail_row, id="test-header-child") + ")"
hdr = "(<> " + root_hdr + " " + header_child_sx(inner) + ")"
content = sx_call("test-detail",
nodeid=test["nodeid"],
outcome=test["outcome"],
duration=str(test["duration"]),
longrepr=test.get("longrepr", ""),
)
return full_page_sexp(ctx, header_rows=hdr, content=content)
return full_page_sx(ctx, header_rows=hdr, content=content)