Spaces:
Paused
Paused
File size: 2,237 Bytes
628b3d0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 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
|