"""Plugin error isolation wrapper.""" import logging import functools import inspect from fastapi import UploadFile # noqa: F401 — needed for ForwardRef resolution from fastapi import WebSocket # noqa: F401 — needed for ForwardRef resolution from fastapi.responses import JSONResponse logger = logging.getLogger(__name__) def wrap_plugin_route(handler): """Decorator that isolates plugin route errors and returns structured JSON. Copies the original handler's signature so FastAPI sees the correct parameters (path params, query params, body, etc.) instead of *args/**kwargs. UploadFile and WebSocket are imported in this module's namespace so FastAPI can resolve ForwardRef('UploadFile') and ForwardRef('WebSocket'). """ @functools.wraps(handler) async def wrapper(*args, **kwargs): try: return await handler(*args, **kwargs) except Exception as exc: logger.error(f'Plugin route error: {exc}', exc_info=True) return JSONResponse( status_code=500, content={'detail': f'Plugin error: {exc}', 'code': 'plugin_error'} ) # Remove __wrapped__ so FastAPI doesn't try to resolve the original signature # through the wrapper chain — we set __signature__ explicitly instead. if hasattr(wrapper, '__wrapped__'): delattr(wrapper, '__wrapped__') # Copy the signature from the original handler so FastAPI sees correct params. # Keep __annotations__ from functools.wraps (needed for ForwardRef resolution). # Remove only the return annotation to avoid response_model issues. try: orig_sig = inspect.signature(handler) wrapper.__signature__ = orig_sig.replace( return_annotation=inspect.Signature.empty, ) except (ValueError, TypeError): pass return wrapper