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:
@@ -36,7 +36,7 @@ def _ticket_widget_html(entry, qty: int, ticket_url: str, *, ctx: dict) -> str:
|
||||
return sx_call("events-tw-form",
|
||||
ticket_url=ticket_url, target=tgt,
|
||||
csrf=csrf_token_val, entry_id=str(eid),
|
||||
count_val=str(count_val), btn=SxExpr(btn_html))
|
||||
count_val=str(count_val), btn=btn_html)
|
||||
|
||||
if qty == 0:
|
||||
inner = _tw_form(1, sx_call("events-tw-cart-plus"))
|
||||
@@ -80,7 +80,7 @@ def _tickets_main_panel_html(ctx: dict, tickets: list) -> str:
|
||||
type_name=tt.name if tt else None,
|
||||
time_str=time_str or None,
|
||||
cal_name=cal.name if cal else None,
|
||||
badge=SxExpr(_ticket_state_badge_html(state)),
|
||||
badge=_ticket_state_badge_html(state),
|
||||
code_prefix=ticket.code[:8]))
|
||||
|
||||
cards_html = "".join(ticket_cards)
|
||||
@@ -193,7 +193,7 @@ def _ticket_admin_main_panel_html(ctx: dict, tickets: list, stats: dict) -> str:
|
||||
entry_name=entry.name if entry else "\u2014",
|
||||
date=SxExpr(date_html),
|
||||
type_name=tt.name if tt else "\u2014",
|
||||
badge=SxExpr(_ticket_state_badge_html(state)),
|
||||
badge=_ticket_state_badge_html(state),
|
||||
action=SxExpr(action_html))
|
||||
|
||||
return sx_call("events-ticket-admin-panel",
|
||||
@@ -238,7 +238,7 @@ def render_checkin_result(success: bool, error: str | None, ticket) -> str:
|
||||
entry_name=entry.name if entry else "\u2014",
|
||||
date=SxExpr(date_html),
|
||||
type_name=tt.name if tt else "\u2014",
|
||||
badge=SxExpr(_ticket_state_badge_html("checked_in")),
|
||||
badge=_ticket_state_badge_html("checked_in"),
|
||||
time_str=time_str)
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ def render_lookup_result(ticket, error: str | None) -> str:
|
||||
if cal:
|
||||
info_html += sx_call("events-lookup-cal", cal_name=cal.name)
|
||||
info_html += sx_call("events-lookup-status",
|
||||
badge=SxExpr(_ticket_state_badge_html(state)), code=code)
|
||||
badge=_ticket_state_badge_html(state), code=code)
|
||||
if checked_in_at:
|
||||
info_html += sx_call("events-lookup-checkin-time",
|
||||
date_str=checked_in_at.strftime("%B %d, %Y at %H:%M"))
|
||||
@@ -328,7 +328,7 @@ def render_entry_tickets_admin(entry, tickets: list) -> str:
|
||||
rows_html += sx_call("events-entry-tickets-admin-row",
|
||||
code=code, code_short=code[:12] + "...",
|
||||
type_name=tt.name if tt else "\u2014",
|
||||
badge=SxExpr(_ticket_state_badge_html(state)),
|
||||
badge=_ticket_state_badge_html(state),
|
||||
action=SxExpr(action_html))
|
||||
|
||||
if tickets:
|
||||
@@ -340,7 +340,7 @@ def render_entry_tickets_admin(entry, tickets: list) -> str:
|
||||
return sx_call("events-entry-tickets-admin-panel",
|
||||
entry_name=entry.name,
|
||||
count_label=f"{count} ticket{suffix}",
|
||||
body=SxExpr(body_html))
|
||||
body=body_html)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -519,16 +519,16 @@ def render_buy_form(entry, ticket_remaining, ticket_sold_count,
|
||||
cost_str = f"\u00a3{tt.cost:.2f}" if tt.cost is not None else "\u00a30.00"
|
||||
type_items += sx_call("events-buy-type-item",
|
||||
type_name=tt.name, cost_str=cost_str,
|
||||
adjust_controls=SxExpr(_ticket_adjust_controls(csrf, adjust_url, target, eid, type_count, ticket_type_id=tt.id)))
|
||||
adjust_controls=_ticket_adjust_controls(csrf, adjust_url, target, eid, type_count, ticket_type_id=tt.id))
|
||||
body_html = sx_call("events-buy-types-wrapper", items=SxExpr(type_items))
|
||||
else:
|
||||
qty = user_ticket_count or 0
|
||||
body_html = sx_call("events-buy-default",
|
||||
price_str=f"\u00a3{tp:.2f}",
|
||||
adjust_controls=SxExpr(_ticket_adjust_controls(csrf, adjust_url, target, eid, qty)))
|
||||
adjust_controls=_ticket_adjust_controls(csrf, adjust_url, target, eid, qty))
|
||||
|
||||
return sx_call("events-buy-panel",
|
||||
entry_id=eid_s, info=SxExpr(info_html), body=SxExpr(body_html))
|
||||
entry_id=eid_s, info=SxExpr(info_html), body=body_html)
|
||||
|
||||
|
||||
def _ticket_adjust_controls(csrf, adjust_url, target, entry_id, count, *, ticket_type_id=None):
|
||||
@@ -543,8 +543,8 @@ def _ticket_adjust_controls(csrf, adjust_url, target, entry_id, count, *, ticket
|
||||
return sx_call("events-adjust-form",
|
||||
adjust_url=adjust_url, target=target,
|
||||
extra_cls=extra_cls, csrf=csrf,
|
||||
entry_id=eid_s, tt=SxExpr(tt_html) if tt_html else None,
|
||||
count_val=str(count_val), btn=SxExpr(btn_html))
|
||||
entry_id=eid_s, tt=tt_html or None,
|
||||
count_val=str(count_val), btn=btn_html)
|
||||
|
||||
if count == 0:
|
||||
return _adj_form(1, sx_call("events-adjust-cart-plus"),
|
||||
@@ -557,7 +557,7 @@ def _ticket_adjust_controls(csrf, adjust_url, target, entry_id, count, *, ticket
|
||||
plus = _adj_form(count + 1, sx_call("events-adjust-plus"))
|
||||
|
||||
return sx_call("events-adjust-controls",
|
||||
minus=SxExpr(minus), cart_icon=SxExpr(cart_icon), plus=SxExpr(plus))
|
||||
minus=minus, cart_icon=cart_icon, plus=plus)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -603,7 +603,7 @@ def _ticket_types_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
|
||||
return sx_call("menu-row-sx", id="ticket_types-row", level=7,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="ticket_type-header-child", oob=oob)
|
||||
nav=nav_html or None, child_id="ticket_type-header-child", oob=oob)
|
||||
|
||||
|
||||
|
||||
@@ -639,7 +639,7 @@ def _ticket_type_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
|
||||
return sx_call("menu-row-sx", id="ticket_type-row", level=8,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="ticket_type-header-child-inner", oob=oob)
|
||||
nav=nav_html or None, child_id="ticket_type-header-child-inner", oob=oob)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user