From d396692de571445537e617ac31627d7bd7ab84aa Mon Sep 17 00:00:00 2001 From: Christian Kolset Date: Tue, 9 Jun 2026 22:35:40 -0600 Subject: Fix custom_script execution + add templates for expression/function kinds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - custom_script now uses exec-style (assign to `result`) matching the built-in templates — previously auto-wrapped in def compute() which had no return statement, causing state and channel vars to silently fail - function kind unchanged: auto-wraps body as def compute(x,t); use return - Add SCRIPT_TEMPLATES for expression, function, custom_script kinds; shown automatically when user switches to a script kind (replacing stale code from previous kind) - New channels default to template when no saved code exists Co-Authored-By: Claude Sonnet 4.6 --- core/signal_processor.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/signal_processor.py b/core/signal_processor.py index 8ac4d8c..e2752cd 100644 --- a/core/signal_processor.py +++ b/core/signal_processor.py @@ -286,7 +286,8 @@ class DerivedChannel: self._fn = _expr_fn - elif self.kind in ("function", "custom_script"): + elif self.kind == "function": + # Auto-wrap body as def compute(x, t): ... return src = self.script if not src.strip().startswith("def compute"): src = "def compute(x, t):\n" + "\n".join( @@ -297,9 +298,8 @@ class DerivedChannel: _fn_ref = _exec_ns["compute"] _state = self._exec_state - def _script_fn(inputs, t, sv, - _f=_fn_ref, _ns=_exec_ns, _st=_state, _names=src_names): - # refresh mutable globals before each call so scripts see live values + def _fn_fn(inputs, t, sv, + _f=_fn_ref, _ns=_exec_ns, _st=_state, _names=src_names): _ns["vars"] = sv _ns["state"] = _st for i, name in enumerate(_names): @@ -307,7 +307,24 @@ class DerivedChannel: _ns[name] = inputs[i] return float(_f(inputs, t)) - self._fn = _script_fn + self._fn = _fn_fn + + elif self.kind == "custom_script": + # Exec-style: script assigns to `result` + code = compile(self.script, "", "exec") + _state = self._exec_state + + def _cs_fn(inputs, t, sv, _code=code, _names=src_names, _st=_state): + ns = {"x": inputs, "t": t, "math": math, "vars": sv, "state": _st} + for i, name in enumerate(_names): + if i < len(inputs): + ns[name] = inputs[i] + exec(_code, ns) + if "result" not in ns: + raise NameError("custom_script must assign to 'result'") + return float(ns["result"]) + + self._fn = _cs_fn else: # Built-in kinds handled in SignalProcessor._compute_derived -- cgit v1.2.3