Eliminate Python page helpers from orders — pure .sx defpages with IO primitives
Orders defpages now fetch data via (service ...) and generate URLs via (url-for ...) and (route-prefix) directly in .sx. No Python middleman. - Add url-for, route-prefix IO primitives to shared/sx/primitives_io.py - Add generic register()/\_\_getattr\_\_ to ServiceRegistry for dynamic services - Create OrdersPageService with list_page_data/detail_page_data methods - Rewrite orders.sx defpages to use IO primitives + defcomp calls - Remove ~320 lines of Python page helpers from orders/sxc/pages/__init__.py - Convert :data env merge to use kebab-case keys for SX symbol access Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,8 @@ Usage::
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from shared.contracts.protocols import (
|
||||
CalendarService,
|
||||
MarketService,
|
||||
@@ -36,6 +38,7 @@ class _ServiceRegistry:
|
||||
self._market: MarketService | None = None
|
||||
self._cart: CartService | None = None
|
||||
self._federation: FederationService | None = None
|
||||
self._extra: dict[str, Any] = {}
|
||||
|
||||
# -- calendar -------------------------------------------------------------
|
||||
@property
|
||||
@@ -81,10 +84,27 @@ class _ServiceRegistry:
|
||||
def federation(self, impl: FederationService) -> None:
|
||||
self._federation = impl
|
||||
|
||||
# -- generic registration --------------------------------------------------
|
||||
def register(self, name: str, impl: Any) -> None:
|
||||
"""Register a service by name (for services without typed properties)."""
|
||||
self._extra[name] = impl
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
# Fallback to _extra dict for dynamically registered services
|
||||
try:
|
||||
extra = object.__getattribute__(self, "_extra")
|
||||
if name in extra:
|
||||
return extra[name]
|
||||
except AttributeError:
|
||||
pass
|
||||
raise AttributeError(f"No service registered as: {name}")
|
||||
|
||||
# -- introspection --------------------------------------------------------
|
||||
def has(self, name: str) -> bool:
|
||||
"""Check whether a domain service is registered."""
|
||||
return getattr(self, f"_{name}", None) is not None
|
||||
if getattr(self, f"_{name}", None) is not None:
|
||||
return True
|
||||
return name in self._extra
|
||||
|
||||
|
||||
# Module-level singleton — import this everywhere.
|
||||
|
||||
@@ -184,7 +184,9 @@ async def execute_page(
|
||||
if page_def.data_expr is not None:
|
||||
data_result = await async_eval(page_def.data_expr, env, ctx)
|
||||
if isinstance(data_result, dict):
|
||||
env.update(data_result)
|
||||
# Merge with kebab-case keys so SX symbols can reference them
|
||||
for k, v in data_result.items():
|
||||
env[k.replace("_", "-")] = v
|
||||
|
||||
# Render content slot (required)
|
||||
content_sx = await _eval_slot(page_def.content_expr, env, ctx)
|
||||
|
||||
@@ -43,6 +43,8 @@ IO_PRIMITIVES: frozenset[str] = frozenset({
|
||||
"g",
|
||||
"csrf-token",
|
||||
"abort",
|
||||
"url-for",
|
||||
"route-prefix",
|
||||
})
|
||||
|
||||
|
||||
@@ -345,6 +347,34 @@ async def _io_abort(
|
||||
abort(status, message)
|
||||
|
||||
|
||||
async def _io_url_for(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> str:
|
||||
"""``(url-for "endpoint" :key val ...)`` → url_for(endpoint, **kwargs).
|
||||
|
||||
Generates a URL for the given endpoint. Keyword args become URL
|
||||
parameters (kebab-case converted to snake_case).
|
||||
"""
|
||||
if not args:
|
||||
raise ValueError("url-for requires an endpoint name")
|
||||
from quart import url_for
|
||||
endpoint = str(args[0])
|
||||
clean = {k.replace("-", "_"): v for k, v in _clean_kwargs(kwargs).items()}
|
||||
# Convert numeric values for int URL params
|
||||
for k, v in clean.items():
|
||||
if isinstance(v, str) and v.isdigit():
|
||||
clean[k] = int(v)
|
||||
return url_for(endpoint, **clean)
|
||||
|
||||
|
||||
async def _io_route_prefix(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> str:
|
||||
"""``(route-prefix)`` → current route prefix string."""
|
||||
from shared.utils import route_prefix
|
||||
return route_prefix()
|
||||
|
||||
|
||||
_IO_HANDLERS: dict[str, Any] = {
|
||||
"frag": _io_frag,
|
||||
"query": _io_query,
|
||||
@@ -359,4 +389,6 @@ _IO_HANDLERS: dict[str, Any] = {
|
||||
"g": _io_g,
|
||||
"csrf-token": _io_csrf_token,
|
||||
"abort": _io_abort,
|
||||
"url-for": _io_url_for,
|
||||
"route-prefix": _io_route_prefix,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user