Make SxExpr a str subclass, sx_call/render functions return SxExpr

SxExpr is now a str subclass so it works everywhere a plain string
does (join, isinstance, f-strings) while serialize() still emits it
unquoted. sx_call() and all internal render functions (_render_to_sx,
async_eval_to_sx, etc.) return SxExpr, eliminating the "forgot to
wrap" bug class that caused the sx_content leak and list serialization
bugs.

- Phase 0: SxExpr(str) with .source property, __add__/__radd__
- Phase 1: sx_call returns SxExpr (drop-in, all 200+ sites unchanged)
- Phase 2: async_eval_to_sx, async_eval_slot_to_sx, _render_to_sx,
  mobile_menu_sx return SxExpr; remove isinstance(str) workaround
- Phase 3: Remove ~150 redundant SxExpr() wrappings across 45 files
- Phase 4: serialize() docstring, handler return docs, ;; returns: sx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 21:47:00 +00:00
parent ad75798ab7
commit 278ae3e8f6
45 changed files with 378 additions and 379 deletions

View File

@@ -45,7 +45,7 @@ async def _render_checkout_return(ctx: dict, order=None, status: str = "",
else:
img = sx_call("order-item-no-image")
item_parts.append(sx_call("order-item-row",
href=product_url, img=SxExpr(img),
href=product_url, img=img,
title=item.product_title or "Unknown product",
pid=f"Product ID: {item.product_id}",
qty=f"Qty: {item.quantity}",
@@ -109,11 +109,11 @@ async def _render_checkout_return(ctx: dict, order=None, status: str = "",
status_msg = sx_call("checkout-return-paid")
content = sx_call("checkout-return-content",
summary=SxExpr(summary),
items=SxExpr(items) if items else None,
calendar=SxExpr(calendar) if calendar else None,
tickets=SxExpr(tickets) if tickets else None,
status_message=SxExpr(status_msg) if status_msg else None,
summary=summary,
items=items or None,
calendar=calendar or None,
tickets=tickets or None,
status_message=status_msg or None,
)
account_url = call_url(ctx, "account_url", "")

View File

@@ -71,7 +71,6 @@ def register() -> Blueprint:
if not hosted_url:
from shared.sx.page import get_template_context
from shared.sx.helpers import sx_call, root_header_sx, header_child_sx, full_page_sx, call_url
from shared.sx.parser import SxExpr
from shared.infrastructure.urls import cart_url
tctx = await get_template_context()
account_url = call_url(tctx, "account_url", "")
@@ -82,7 +81,7 @@ def register() -> Blueprint:
content = sx_call(
"checkout-error-content",
msg="No hosted checkout URL returned from SumUp when trying to reopen payment.",
order=SxExpr(order_sx),
order=order_sx,
back_url=cart_url("/"),
)
html = await full_page_sx(tctx, header_rows=hdr, filter=filt, content=content)

View File

@@ -1,4 +1,5 @@
;; Orders account-nav-item fragment handler
;; returns: sx
;;
;; Renders the "orders" link for the account dashboard nav.