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:
@@ -73,10 +73,10 @@ def _entry_card_html(entry, page_info: dict, pending_tickets: dict,
|
||||
if tp is not None:
|
||||
qty = pending_tickets.get(entry.id, 0)
|
||||
widget_html = sx_call("events-entry-widget-wrapper",
|
||||
widget=SxExpr(_ticket_widget_html(entry, qty, ticket_url, ctx={})))
|
||||
widget=_ticket_widget_html(entry, qty, ticket_url, ctx={}))
|
||||
|
||||
return sx_call("events-entry-card",
|
||||
title=SxExpr(title_html), badges=SxExpr(badges_html),
|
||||
title=title_html, badges=SxExpr(badges_html),
|
||||
time_parts=SxExpr(time_parts), cost=SxExpr(cost_html),
|
||||
widget=SxExpr(widget_html))
|
||||
|
||||
@@ -137,10 +137,10 @@ def _entry_card_tile_html(entry, page_info: dict, pending_tickets: dict,
|
||||
if tp is not None:
|
||||
qty = pending_tickets.get(entry.id, 0)
|
||||
widget_html = sx_call("events-entry-tile-widget-wrapper",
|
||||
widget=SxExpr(_ticket_widget_html(entry, qty, ticket_url, ctx={})))
|
||||
widget=_ticket_widget_html(entry, qty, ticket_url, ctx={}))
|
||||
|
||||
return sx_call("events-entry-card-tile",
|
||||
title=SxExpr(title_html), badges=SxExpr(badges_html),
|
||||
title=title_html, badges=SxExpr(badges_html),
|
||||
time=SxExpr(time_html), cost=SxExpr(cost_html),
|
||||
widget=SxExpr(widget_html))
|
||||
|
||||
@@ -199,7 +199,7 @@ def _events_main_panel_html(ctx: dict, entries, has_more, pending_tickets, page_
|
||||
cls="px-3 py-12 text-center text-stone-400")
|
||||
|
||||
return sx_call("events-main-panel-body",
|
||||
toggle=SxExpr(toggle), body=SxExpr(body))
|
||||
toggle=toggle, body=body)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -253,7 +253,7 @@ def _entry_main_panel_html(ctx: dict) -> str:
|
||||
# State
|
||||
state_html = _field("State", sx_call("events-entry-state-field",
|
||||
entry_id=str(eid),
|
||||
badge=SxExpr(_entry_state_badge_html(state))))
|
||||
badge=_entry_state_badge_html(state)))
|
||||
|
||||
# Cost
|
||||
cost = getattr(entry, "cost", None)
|
||||
@@ -284,7 +284,7 @@ def _entry_main_panel_html(ctx: dict) -> str:
|
||||
entry_posts = ctx.get("entry_posts") or []
|
||||
posts_html = _field("Associated Posts", sx_call("events-entry-posts-field",
|
||||
entry_id=str(eid),
|
||||
posts_panel=SxExpr(render_entry_posts_panel(entry_posts, entry, calendar, day, month, year))))
|
||||
posts_panel=render_entry_posts_panel(entry_posts, entry, calendar, day, month, year)))
|
||||
|
||||
# Options and Edit Button
|
||||
edit_url = url_for(
|
||||
@@ -295,12 +295,12 @@ def _entry_main_panel_html(ctx: dict) -> str:
|
||||
|
||||
return sx_call("events-entry-panel",
|
||||
entry_id=str(eid), list_container=list_container,
|
||||
name=SxExpr(name_html), slot=SxExpr(slot_html),
|
||||
time=SxExpr(time_html), state=SxExpr(state_html),
|
||||
cost=SxExpr(cost_html), tickets=SxExpr(tickets_html),
|
||||
buy=SxExpr(buy_html), date=SxExpr(date_html),
|
||||
posts=SxExpr(posts_html),
|
||||
options=SxExpr(_entry_options_html(entry, calendar, day, month, year)),
|
||||
name=name_html, slot=slot_html,
|
||||
time=time_html, state=state_html,
|
||||
cost=cost_html, tickets=tickets_html,
|
||||
buy=SxExpr(buy_html), date=date_html,
|
||||
posts=posts_html,
|
||||
options=_entry_options_html(entry, calendar, day, month, year),
|
||||
pre_action=pre_action, edit_url=edit_url)
|
||||
|
||||
|
||||
@@ -331,13 +331,13 @@ def _entry_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
)
|
||||
label_html = sx_call("events-entry-label",
|
||||
entry_id=str(entry.id),
|
||||
title=SxExpr(_entry_title_html(entry)),
|
||||
title=_entry_title_html(entry),
|
||||
times=SxExpr(_entry_times_html(entry)))
|
||||
|
||||
nav_html = _entry_nav_html(ctx)
|
||||
|
||||
return sx_call("menu-row-sx", id="entry-row", level=5,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
link_href=link_href, link_label_content=label_html,
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="entry-header-child", oob=oob)
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ def _entry_nav_html(ctx: dict) -> str:
|
||||
else:
|
||||
img_html = sx_call("events-post-img-placeholder")
|
||||
post_links += sx_call("events-entry-nav-post-link",
|
||||
href=href, img=SxExpr(img_html), title=title)
|
||||
href=href, img=img_html, title=title)
|
||||
parts.append((sx_call("events-entry-posts-nav-oob",
|
||||
items=SxExpr(post_links))).replace(' :hx-swap-oob "true"', ''))
|
||||
|
||||
@@ -420,7 +420,7 @@ def render_entry_optioned(entry, calendar, day, month, year) -> str:
|
||||
|
||||
return options + sx_call("events-entry-optioned-oob",
|
||||
entry_id=str(entry.id),
|
||||
title=SxExpr(title), state=SxExpr(state))
|
||||
title=title, state=state)
|
||||
|
||||
|
||||
def _entry_title_html(entry) -> str:
|
||||
@@ -428,7 +428,7 @@ def _entry_title_html(entry) -> str:
|
||||
state = getattr(entry, "state", "pending") or "pending"
|
||||
return sx_call("events-entry-title",
|
||||
name=entry.name,
|
||||
badge=SxExpr(_entry_state_badge_html(state)))
|
||||
badge=_entry_state_badge_html(state))
|
||||
|
||||
|
||||
def _entry_options_html(entry, calendar, day, month, year) -> str:
|
||||
@@ -550,7 +550,7 @@ def render_entry_posts_panel(entry_posts, entry, calendar, day, month, year) ->
|
||||
entry_id=eid, post_id=ep_id,
|
||||
)
|
||||
items += sx_call("events-entry-post-item",
|
||||
img=SxExpr(img_html), title=ep_title,
|
||||
img=img_html, title=ep_title,
|
||||
del_url=del_url, entry_id=eid_s,
|
||||
csrf_hdr=f'{{"X-CSRFToken": "{csrf}"}}')
|
||||
posts_html = sx_call("events-entry-posts-list", items=SxExpr(items))
|
||||
@@ -563,7 +563,7 @@ def render_entry_posts_panel(entry_posts, entry, calendar, day, month, year) ->
|
||||
)
|
||||
|
||||
return sx_call("events-entry-posts-panel",
|
||||
posts=SxExpr(posts_html), search_url=search_url,
|
||||
posts=posts_html, search_url=search_url,
|
||||
entry_id=eid_s)
|
||||
|
||||
|
||||
@@ -591,7 +591,7 @@ def render_entry_posts_nav_oob(entry_posts) -> str:
|
||||
if feat else sx_call("events-post-img-placeholder"))
|
||||
items += sx_call("events-entry-nav-post",
|
||||
href=href, nav_btn=nav_btn,
|
||||
img=SxExpr(img_html), title=title)
|
||||
img=img_html, title=title)
|
||||
|
||||
return sx_call("events-entry-posts-nav-oob", items=SxExpr(items))
|
||||
|
||||
@@ -743,7 +743,7 @@ def _entry_admin_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
|
||||
return sx_call("menu-row-sx", id="entry-admin-row", level=6,
|
||||
link_href=link_href, link_label="admin", icon="fa fa-cog",
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="entry-admin-header-child", oob=oob)
|
||||
nav=nav_html or None, child_id="entry-admin-header-child", oob=oob)
|
||||
|
||||
|
||||
def _entry_admin_nav_html(ctx: dict) -> str:
|
||||
@@ -822,7 +822,7 @@ def render_post_search_results(search_posts, search_query, page, total_pages,
|
||||
|
||||
parts.append(sx_call("events-post-search-item",
|
||||
post_url=post_url, entry_id=str(eid), csrf=csrf,
|
||||
post_id=str(sp.id), img=SxExpr(img_html), title=title))
|
||||
post_id=str(sp.id), img=img_html, title=title))
|
||||
|
||||
result = "".join(parts)
|
||||
|
||||
@@ -882,7 +882,7 @@ def render_entry_edit_form(entry, calendar, day, month, year, day_slots) -> str:
|
||||
html = sx_call("events-entry-edit-form",
|
||||
entry_id=str(eid), list_container=list_container,
|
||||
put_url=put_url, cancel_url=cancel_url, csrf=csrf,
|
||||
name_val=entry.name or "", slot_picker=SxExpr(slot_picker_html),
|
||||
name_val=entry.name or "", slot_picker=slot_picker_html,
|
||||
start_val=start_val, end_val=end_val, cost_display=cost_display,
|
||||
ticket_price_val=tp_val, ticket_count_val=tc_val,
|
||||
action_btn=action_btn, cancel_btn=cancel_btn)
|
||||
@@ -920,7 +920,7 @@ def render_entry_add_form(calendar, day, month, year, day_slots) -> str:
|
||||
|
||||
html = sx_call("events-entry-add-form",
|
||||
post_url=post_url, csrf=csrf,
|
||||
slot_picker=SxExpr(slot_picker_html),
|
||||
slot_picker=slot_picker_html,
|
||||
action_btn=action_btn, cancel_btn=cancel_btn,
|
||||
cancel_url=cancel_url)
|
||||
return html + _SLOT_PICKER_JS
|
||||
@@ -998,13 +998,13 @@ def render_fragment_account_tickets(tickets) -> str:
|
||||
items_html += sx_call("events-frag-ticket-item",
|
||||
href=href, entry_name=ticket.entry_name,
|
||||
date_str=date_str, calendar_name=cal_name,
|
||||
type_name=type_name, badge=SxExpr(badge_html))
|
||||
type_name=type_name, badge=badge_html)
|
||||
body = sx_call("events-frag-tickets-list", items=SxExpr(items_html))
|
||||
else:
|
||||
body = sx_call("empty-state", message="No tickets yet.",
|
||||
cls="text-sm text-stone-500")
|
||||
|
||||
return sx_call("events-frag-tickets-panel", items=SxExpr(body))
|
||||
return sx_call("events-frag-tickets-panel", items=body)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1033,10 +1033,10 @@ def render_fragment_account_bookings(bookings) -> str:
|
||||
name=booking.name,
|
||||
date_str=date_str + date_str_extra,
|
||||
calendar_name=cal_name, cost_str=cost_str,
|
||||
badge=SxExpr(badge_html))
|
||||
badge=badge_html)
|
||||
body = sx_call("events-frag-bookings-list", items=SxExpr(items_html))
|
||||
else:
|
||||
body = sx_call("empty-state", message="No bookings yet.",
|
||||
cls="text-sm text-stone-500")
|
||||
|
||||
return sx_call("events-frag-bookings-panel", items=SxExpr(body))
|
||||
return sx_call("events-frag-bookings-panel", items=body)
|
||||
|
||||
Reference in New Issue
Block a user