Fix sync IO primitives unreachable from sx_ref.py evaluator
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m49s

app-url, asset-url, config, jinja-global, relations-from are declared
as IO in boundary.sx but called inline in .sx code (inside let/filter).
async_eval_ref.py only intercepts IO at the top level — nested calls
fall through to sx_ref.eval_expr which couldn't find them.

Register sync bridge wrappers directly in _PRIMITIVES (bypassing
@register_primitive validation since they're boundary.sx, not
primitives.sx). Both async and sync eval paths now work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 00:06:09 +00:00
parent abeb4551da
commit cfde5bc491

View File

@@ -558,3 +558,55 @@ def prim_merge_styles(*styles: Any) -> Any:
if len(valid) == 1:
return valid[0]
return merge_styles(valid)
# ---------------------------------------------------------------------------
# Sync IO bridge primitives
#
# These are declared in boundary.sx (I/O tier), NOT primitives.sx.
# They bypass @register_primitive validation because they aren't pure.
# But they must be evaluator-visible because they're called inline in .sx
# code (inside let, filter, etc.) where the async IO interceptor can't
# reach them — particularly in async_eval_ref.py which only intercepts
# IO at the top level.
#
# The async evaluators also intercept these via IO_PRIMITIVES, so the
# async path works too. This registration ensures the sync fallback works.
# ---------------------------------------------------------------------------
def _bridge_app_url(service, *path_parts):
from shared.infrastructure.urls import app_url
path = str(path_parts[0]) if path_parts else "/"
return app_url(str(service), path)
def _bridge_asset_url(*path_parts):
from shared.infrastructure.urls import asset_url
path = str(path_parts[0]) if path_parts else ""
return asset_url(path)
def _bridge_config(key):
from shared.config import config
cfg = config()
return cfg.get(str(key))
def _bridge_jinja_global(key, *default):
from quart import current_app
d = default[0] if default else None
return current_app.jinja_env.globals.get(str(key), d)
def _bridge_relations_from(entity_type):
from shared.sx.relations import relations_from
return [
{
"name": d.name, "from_type": d.from_type, "to_type": d.to_type,
"cardinality": d.cardinality, "nav": d.nav,
"nav_icon": d.nav_icon, "nav_label": d.nav_label,
}
for d in relations_from(str(entity_type))
]
_PRIMITIVES["app-url"] = _bridge_app_url
_PRIMITIVES["asset-url"] = _bridge_asset_url
_PRIMITIVES["config"] = _bridge_config
_PRIMITIVES["jinja-global"] = _bridge_jinja_global
_PRIMITIVES["relations-from"] = _bridge_relations_from