Execute the user script with a clean workflow state, AST transformation, dependency tracking, and final component collection. This prepares the runtime environment, executes the script with reactivity enabled, and sends generated components back to the frontend. If
(self)
| 309 | asyncio.run(self.run_script()) |
| 310 | |
| 311 | async def run_script(self): |
| 312 | """ |
| 313 | Execute the user script with a clean workflow state, AST transformation, |
| 314 | dependency tracking, and final component collection. |
| 315 | |
| 316 | This prepares the runtime environment, executes the script with reactivity enabled, |
| 317 | and sends generated components back to the frontend. If transformation fails, |
| 318 | it falls back to executing the raw script without reactivity. |
| 319 | """ |
| 320 | if not self.is_running or not self.script_path: |
| 321 | logger.warning("[ScriptRunner] Not running or no script path set") |
| 322 | return |
| 323 | |
| 324 | # Ensure we run the script from a clear state |
| 325 | workflow = self._service.get_workflow() |
| 326 | workflow.reset() |
| 327 | |
| 328 | logger.info(f"[ScriptRunner] Starting script execution {self.script_path=} {self._run_count=}") |
| 329 | |
| 330 | try: |
| 331 | # Reset state and connect services |
| 332 | self._service.clear_components() |
| 333 | self._service.connect_data_manager() |
| 334 | |
| 335 | # Prepare script execution environment |
| 336 | self._script_globals = {"widget_states": self.widget_states} |
| 337 | |
| 338 | # Capture script output |
| 339 | with self._redirect_stdout(): |
| 340 | with open(self.script_path, encoding="utf-8") as f: |
| 341 | raw_code = f.read() |
| 342 | |
| 343 | current_working_dir = os.getcwd() |
| 344 | script_dir = os.path.dirname(os.path.realpath(self.script_path)) |
| 345 | os.chdir(script_dir) |
| 346 | |
| 347 | def compile_and_run(src_code, script_path, script_globals, execution_context): |
| 348 | code = compile(src_code, script_path, "exec") |
| 349 | logger.debug(f"[ScriptRunner] Script compiled {script_path=}") |
| 350 | exec(code, script_globals) |
| 351 | logger.debug(f"[ScriptRunner] Script executed {execution_context}") |
| 352 | |
| 353 | try: |
| 354 | if self._service.is_reactivity_enabled: |
| 355 | # Attempt reactive transformation |
| 356 | tree, _ = transform_source(raw_code, filename=self.script_path) |
| 357 | self._script_globals["workflow"] = workflow |
| 358 | compile_and_run(tree, self.script_path, self._script_globals, "(reactive)") |
| 359 | workflow.execute_relevant_atoms() |
| 360 | else: |
| 361 | compile_and_run(raw_code, self.script_path, self._script_globals, "(non-reactive)") |
| 362 | workflow.reset() # just to be safe |
| 363 | |
| 364 | except Exception as transform_error: |
| 365 | if logger.isEnabledFor(logging.WARNING): |
| 366 | logger.warning( |
| 367 | "[ScriptRunner] AST transform or reactive execution failed — falling back to full script rerun\n%s", |
| 368 | traceback.format_exc() |
no test coverage detected