""" Python Runtime Native Python tool execution runtime. """ import asyncio import importlib.util import sys from pathlib import Path from typing import Any, Callable, Dict from tools.runtime.base import BaseRuntime class PythonRuntime(BaseRuntime): """Python runtime for executing Python-based tools""" def __init__(self): self._executors: Dict[str, Callable] = {} self._modules: Dict[str, Any] = {} def get_name(self) -> str: return "python" async def validate(self, entry: str) -> bool: """Validate Python tool entry point""" path = Path(entry) if not path.exists(): return False if path.suffix != ".py": return False return True async def execute( self, entry: str, command: str, parameters: Dict[str, Any], timeout: int, ) -> Dict[str, Any]: """Execute a Python tool""" try: # Load module dynamically module = self._load_module(entry, command) if module is None: return { "status": "error", "error": f"Failed to load module from {entry}", } # Get the execute function if not hasattr(module, "execute"): return { "status": "error", "error": "Module does not have 'execute' function", } execute_func = module.execute # Run in executor to avoid blocking loop = asyncio.get_event_loop() result = await asyncio.wait_for( loop.run_in_executor( None, lambda: execute_func(command, parameters), ), timeout=timeout / 1000, ) return { "status": "success", "result": result, } except asyncio.TimeoutError: return { "status": "error", "error": f"Execution timed out after {timeout}ms", } except Exception as e: return { "status": "error", "error": str(e), } def _load_module(self, entry: str, command: str) -> Any: """Load Python module from file path""" cache_key = f"{entry}:{command}" if cache_key in self._modules: return self._modules[cache_key] try: path = Path(entry) module_name = path.stem spec = importlib.util.spec_from_file_location(module_name, entry) if spec is None or spec.loader is None: return None module = importlib.util.module_from_spec(spec) sys.modules[module_name] = module spec.loader.exec_module(module) self._modules[cache_key] = module return module except Exception: return None def clear_cache(self) -> None: """Clear module cache""" self._modules.clear()