Fix highlight undefined symbol by expanding component results server-side
When defpage content expressions use case/if branches that resolve to component calls (e.g. `(case slug "intro" (~docs-intro-content) ...)`), _aser serializes them for the client. Components containing Python-only helpers like `highlight` then fail with "Undefined symbol" on the client. Add _maybe_expand_component_result() which detects when the evaluated result (SxExpr or string) is a component call starting with "(~" and re-parses + expands it through async_eval_slot_to_sx server-side. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1018,6 +1018,31 @@ async def async_eval_to_sx(
|
||||
return SxExpr(serialize(result))
|
||||
|
||||
|
||||
async def _maybe_expand_component_result(
|
||||
result: Any,
|
||||
env: dict[str, Any],
|
||||
ctx: RequestContext,
|
||||
) -> Any:
|
||||
"""If *result* is a component call (SxExpr or string starting with
|
||||
``(~``), re-parse and expand it server-side.
|
||||
|
||||
This ensures Python-only helpers (e.g. ``highlight``) inside the
|
||||
component body are evaluated on the server rather than being
|
||||
serialized for the client where they don't exist.
|
||||
"""
|
||||
raw = None
|
||||
if isinstance(result, SxExpr):
|
||||
raw = str(result).strip()
|
||||
elif isinstance(result, str):
|
||||
raw = result.strip()
|
||||
if raw and raw.startswith("(~"):
|
||||
from .parser import parse_all
|
||||
parsed = parse_all(raw)
|
||||
if parsed:
|
||||
return await async_eval_slot_to_sx(parsed[0], env, ctx)
|
||||
return result
|
||||
|
||||
|
||||
async def async_eval_slot_to_sx(
|
||||
expr: Any,
|
||||
env: dict[str, Any],
|
||||
@@ -1059,21 +1084,16 @@ async def async_eval_slot_to_sx(
|
||||
)
|
||||
# Fall back to normal async_eval_to_sx
|
||||
result = await _aser(expr, env, ctx)
|
||||
# If the result is a component call (from case/if/let branches or
|
||||
# page helpers returning strings), re-parse and expand it server-side
|
||||
# so that Python-only helpers like ``highlight`` in the component body
|
||||
# get evaluated here, not on the client.
|
||||
result = await _maybe_expand_component_result(result, env, ctx)
|
||||
if isinstance(result, SxExpr):
|
||||
return result
|
||||
if result is None or result is NIL:
|
||||
return SxExpr("")
|
||||
if isinstance(result, str):
|
||||
# Page helpers return sx source strings. If the string is a
|
||||
# component call (starts with "(~"), re-parse and expand it
|
||||
# server-side so that Python-only helpers like ``highlight``
|
||||
# inside the component body get evaluated here, not on the client.
|
||||
stripped = result.strip()
|
||||
if stripped.startswith("(~"):
|
||||
from .parser import parse_all
|
||||
parsed = parse_all(stripped)
|
||||
if parsed:
|
||||
return await async_eval_slot_to_sx(parsed[0], env, ctx)
|
||||
return SxExpr(result)
|
||||
return SxExpr(serialize(result))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user