jinja_bridge, helpers, handlers, query_executor now conditionally import from ref/sx_ref and ref/async_eval_ref when SX_USE_REF=1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
79 lines
2.5 KiB
Python
79 lines
2.5 KiB
Python
"""
|
|
Execute defquery / defaction definitions.
|
|
|
|
Unlike fragment handlers (which produce SX markup via ``async_eval_to_sx``),
|
|
query/action defs produce **data** (dicts, lists, scalars) that get
|
|
JSON-serialized by the calling blueprint. Uses ``async_eval()`` with
|
|
the I/O primitive pipeline so ``(service ...)`` calls are awaited inline.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from .types import QueryDef, ActionDef, NIL
|
|
|
|
|
|
async def execute_query(query_def: QueryDef, params: dict[str, str]) -> Any:
|
|
"""Execute a defquery and return a JSON-serializable result.
|
|
|
|
Parameters are bound from request query string args.
|
|
"""
|
|
from .jinja_bridge import get_component_env, _get_request_context
|
|
import os
|
|
if os.environ.get("SX_USE_REF") == "1":
|
|
from .ref.async_eval_ref import async_eval
|
|
else:
|
|
from .async_eval import async_eval
|
|
|
|
env = dict(get_component_env())
|
|
env.update(query_def.closure)
|
|
|
|
# Bind params from request args (try kebab-case and snake_case)
|
|
for param in query_def.params:
|
|
snake = param.replace("-", "_")
|
|
val = params.get(param, params.get(snake, NIL))
|
|
# Coerce type=int for common patterns
|
|
if isinstance(val, str) and val.lstrip("-").isdigit():
|
|
val = int(val)
|
|
env[param] = val
|
|
|
|
ctx = _get_request_context()
|
|
result = await async_eval(query_def.body, env, ctx)
|
|
return _normalize(result)
|
|
|
|
|
|
async def execute_action(action_def: ActionDef, payload: dict[str, Any]) -> Any:
|
|
"""Execute a defaction and return a JSON-serializable result.
|
|
|
|
Parameters are bound from the JSON request body.
|
|
"""
|
|
from .jinja_bridge import get_component_env, _get_request_context
|
|
import os
|
|
if os.environ.get("SX_USE_REF") == "1":
|
|
from .ref.async_eval_ref import async_eval
|
|
else:
|
|
from .async_eval import async_eval
|
|
|
|
env = dict(get_component_env())
|
|
env.update(action_def.closure)
|
|
|
|
# Bind params from JSON payload (try kebab-case and snake_case)
|
|
for param in action_def.params:
|
|
snake = param.replace("-", "_")
|
|
val = payload.get(param, payload.get(snake, NIL))
|
|
env[param] = val
|
|
|
|
ctx = _get_request_context()
|
|
result = await async_eval(action_def.body, env, ctx)
|
|
return _normalize(result)
|
|
|
|
|
|
def _normalize(value: Any) -> Any:
|
|
"""Ensure result is JSON-serializable (strip NIL, convert sets, etc)."""
|
|
if value is NIL or value is None:
|
|
return None
|
|
if isinstance(value, set):
|
|
return list(value)
|
|
return value
|