Merge branch 'main' into worktree-typed-sx
# Conflicts: # shared/sx/ref/platform_py.py # shared/sx/ref/sx_ref.py
This commit is contained in:
@@ -470,10 +470,7 @@ def invoke(f, *args):
|
||||
|
||||
def json_serialize(obj):
|
||||
import json
|
||||
try:
|
||||
return json.dumps(obj)
|
||||
except (TypeError, ValueError):
|
||||
return "{}"
|
||||
return json.dumps(obj)
|
||||
|
||||
|
||||
def is_empty_dict(d):
|
||||
@@ -608,7 +605,7 @@ def sx_expr_source(x):
|
||||
|
||||
|
||||
try:
|
||||
from shared.sx.evaluator import EvalError
|
||||
from shared.sx.types import EvalError
|
||||
except ImportError:
|
||||
class EvalError(Exception):
|
||||
pass
|
||||
@@ -1075,10 +1072,19 @@ import inspect
|
||||
|
||||
from shared.sx.primitives_io import (
|
||||
IO_PRIMITIVES, RequestContext, execute_io,
|
||||
css_class_collector as _css_class_collector_cv,
|
||||
_svg_context as _svg_context_cv,
|
||||
)
|
||||
|
||||
# Lazy imports to avoid circular dependency (html.py imports sx_ref.py)
|
||||
_css_class_collector_cv = None
|
||||
_svg_context_cv = None
|
||||
|
||||
def _ensure_html_imports():
|
||||
global _css_class_collector_cv, _svg_context_cv
|
||||
if _css_class_collector_cv is None:
|
||||
from shared.sx.html import css_class_collector, _svg_context
|
||||
_css_class_collector_cv = css_class_collector
|
||||
_svg_context_cv = _svg_context
|
||||
|
||||
# When True, async_aser expands known components server-side
|
||||
_expand_components_cv: contextvars.ContextVar[bool] = contextvars.ContextVar(
|
||||
"_expand_components_ref", default=False
|
||||
@@ -1102,18 +1108,22 @@ def expand_components_p():
|
||||
|
||||
|
||||
def svg_context_p():
|
||||
_ensure_html_imports()
|
||||
return _svg_context_cv.get(False)
|
||||
|
||||
|
||||
def svg_context_set(val):
|
||||
_ensure_html_imports()
|
||||
return _svg_context_cv.set(val)
|
||||
|
||||
|
||||
def svg_context_reset(token):
|
||||
_ensure_html_imports()
|
||||
_svg_context_cv.reset(token)
|
||||
|
||||
|
||||
def css_class_collect(val):
|
||||
_ensure_html_imports()
|
||||
collector = _css_class_collector_cv.get(None)
|
||||
if collector is not None:
|
||||
collector.update(str(val).split())
|
||||
@@ -1131,6 +1141,25 @@ def is_sx_expr(x):
|
||||
return isinstance(x, SxExpr)
|
||||
|
||||
|
||||
# Predicate helpers used by adapter-async (these are in PRIMITIVES but
|
||||
# the bootstrapped code calls them as plain functions)
|
||||
def string_p(x):
|
||||
return isinstance(x, str)
|
||||
|
||||
|
||||
def list_p(x):
|
||||
return isinstance(x, _b_list)
|
||||
|
||||
|
||||
def number_p(x):
|
||||
return isinstance(x, (int, float)) and not isinstance(x, bool)
|
||||
|
||||
|
||||
def sx_parse(src):
|
||||
from shared.sx.parser import parse_all
|
||||
return parse_all(src)
|
||||
|
||||
|
||||
def is_async_coroutine(x):
|
||||
return inspect.iscoroutine(x)
|
||||
|
||||
@@ -1207,48 +1236,16 @@ async def async_eval_slot_to_sx(expr, env, ctx=None):
|
||||
ctx = RequestContext()
|
||||
token = _expand_components_cv.set(True)
|
||||
try:
|
||||
return await _eval_slot_inner(expr, env, ctx)
|
||||
result = await async_eval_slot_inner(expr, env, ctx)
|
||||
if isinstance(result, SxExpr):
|
||||
return result
|
||||
if result is None or result is NIL:
|
||||
return SxExpr("")
|
||||
if isinstance(result, str):
|
||||
return SxExpr(result)
|
||||
return SxExpr(sx_serialize(result))
|
||||
finally:
|
||||
_expand_components_cv.reset(token)
|
||||
|
||||
|
||||
async def _eval_slot_inner(expr, env, ctx):
|
||||
if isinstance(expr, list) and expr:
|
||||
head = expr[0]
|
||||
if isinstance(head, Symbol) and head.name.startswith("~"):
|
||||
comp = env.get(head.name)
|
||||
if isinstance(comp, Component):
|
||||
result = await async_aser_component(comp, expr[1:], env, ctx)
|
||||
if isinstance(result, SxExpr):
|
||||
return result
|
||||
if result is None or result is NIL:
|
||||
return SxExpr("")
|
||||
if isinstance(result, str):
|
||||
return SxExpr(result)
|
||||
return SxExpr(sx_serialize(result))
|
||||
result = await async_aser(expr, env, ctx)
|
||||
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):
|
||||
return SxExpr(result)
|
||||
return SxExpr(sx_serialize(result))
|
||||
|
||||
|
||||
async def _maybe_expand_component_result(result, env, ctx):
|
||||
raw = None
|
||||
if isinstance(result, SxExpr):
|
||||
raw = str(result).strip()
|
||||
elif isinstance(result, str):
|
||||
raw = result.strip()
|
||||
if raw and raw.startswith("(~"):
|
||||
from shared.sx.parser import parse_all as _pa
|
||||
parsed = _pa(raw)
|
||||
if parsed:
|
||||
return await async_eval_slot_to_sx(parsed[0], env, ctx)
|
||||
return result
|
||||
'''
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1374,7 +1371,8 @@ aser_special = _aser_special_with_continuations
|
||||
# Public API generator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def public_api_py(has_html: bool, has_sx: bool, has_deps: bool = False) -> str:
|
||||
def public_api_py(has_html: bool, has_sx: bool, has_deps: bool = False,
|
||||
has_async: bool = False) -> str:
|
||||
lines = [
|
||||
'',
|
||||
'# =========================================================================',
|
||||
@@ -1427,8 +1425,9 @@ def public_api_py(has_html: bool, has_sx: bool, has_deps: bool = False) -> str:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
ADAPTER_FILES = {
|
||||
"html": ("adapter-html.sx", "adapter-html"),
|
||||
"sx": ("adapter-sx.sx", "adapter-sx"),
|
||||
"html": ("adapter-html.sx", "adapter-html"),
|
||||
"sx": ("adapter-sx.sx", "adapter-sx"),
|
||||
"async": ("adapter-async.sx", "adapter-async"),
|
||||
}
|
||||
|
||||
SPEC_MODULES = {
|
||||
@@ -1436,6 +1435,7 @@ SPEC_MODULES = {
|
||||
"router": ("router.sx", "router (client-side route matching)"),
|
||||
"engine": ("engine.sx", "engine (fetch/swap/trigger pure logic)"),
|
||||
"signals": ("signals.sx", "signals (reactive signal runtime)"),
|
||||
"page-helpers": ("page-helpers.sx", "page-helpers (pure data transformation helpers)"),
|
||||
"types": ("types.sx", "types (gradual type system)"),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user