# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import ast import logging from pathlib import Path import queue import time import jupyter_client logger = logging.getLogger(__name__) def validate_coder_script(code_str: str): """Validates that the generated python code does not violate constraints. Specifically checks for usage of cv2.resize and numpy slicing which could mess up the transformation tracking. """ try: tree = ast.parse(code_str) except SyntaxError as e: raise ValueError(f"Syntax error in code: {e}") for node in ast.walk(tree): # Prevent cv2.resize if isinstance(node, ast.Attribute): if node.attr == "resize": # Quick check if it might be cv2.resize raise ValueError( "SECURITY/VALIDATION ERROR: Direct usage of resize() is" " forbidden. You MUST use canvas.resize_by_factor(factor)." ) # Prevent slicing (very aggressive, but forces them to use canvas.crop) # Note: We cannot easily block all slicing because they might slice other arrays. # But we can look for specific variable names or just warn. # Let's be lenient on slicing but strict in the prompt. return True class PythonExecutor: def __init__(self, session_dir: Path | None = None): self.session_dir = session_dir try: self.km = jupyter_client.KernelManager(kernel_name="python3") self.km.start_kernel() self.kc = self.km.client() self.kc.start_channels() self.kc.wait_for_ready(timeout=10) except Exception as e: logger.error(f"Failed to start Jupyter kernel: {e}") raise RuntimeError(f"Kernel startup failed: {e}") def execute(self, code: str) -> str: # First validate code try: validate_coder_script(code) except ValueError as ve: return str(ve) start_time = time.time() self.kc.execute(code) output_text = [] while True: try: msg = self.kc.get_iopub_msg(timeout=15) msg_type = msg["header"]["msg_type"] if msg_type == "stream": output_text.append(msg["content"]["text"]) elif msg_type == "error": traceback = "\n".join(msg["content"]["traceback"]) output_text.append(f"Traceback:\n{traceback}") elif msg_type in ("display_data", "execute_result"): data = msg["content"]["data"] if "text/plain" in data: output_text.append(data["text/plain"]) elif msg_type == "status" and msg["content"]["execution_state"] == "idle": break except queue.Empty: output_text.append("[Execution Timeout Error]") break return "".join(output_text) def close(self): try: self.kc.stop_channels() self.km.shutdown_kernel() except Exception as e: logger.error(f"Failed to cleanly shutdown kernel: {e}")