import asyncio import logging import time from typing import Any logger = logging.getLogger(__name__) class SandboxExecutionError(Exception): """Raised when a plugin exceeds its execution limits.""" pass class SandboxedPlugin: """ Wraps a Plugin instance to enforce execution limits (timeout, error handling). Acts as a proxy to the underlying plugin. """ def __init__( self, plugin_instance: Any, plugin_id: str, timeout_seconds: float = 5.0 ): self._plugin = plugin_instance self._plugin_id = plugin_id self._timeout = timeout_seconds def __getattr__(self, name: str): """Delegate attribute access to the underlying plugin.""" return getattr(self._plugin, name) async def execute(self, *args, **kwargs) -> Any: """ Execute the plugin's main logic with a strict timeout. """ start_time = time.time() try: # Enforce timeout on the execute method result = await asyncio.wait_for( self._plugin.execute(*args, **kwargs), timeout=self._timeout ) return result except asyncio.TimeoutError: duration = time.time() - start_time msg = f"Plugin {self._plugin_id} execution timed out after {duration:.2f}s (limit: {self._timeout}s)" logger.error(msg) raise SandboxExecutionError(msg) except Exception as e: logger.error(f"Plugin {self._plugin_id} execution failed: {e}") raise async def initialize(self, context: Any) -> bool: """ Initialize with timeout protection. """ try: if asyncio.iscoroutinefunction(self._plugin.initialize): return await asyncio.wait_for( self._plugin.initialize(context), timeout=5.0 ) else: return self._plugin.initialize(context) except asyncio.TimeoutError: logger.error(f"Plugin {self._plugin_id} initialization timed out") return False except Exception as e: logger.error(f"Plugin {self._plugin_id} initialization failed: {e}") raise