Source code for debug_toolbar.litestar.plugin

"""Debug toolbar plugin for Litestar."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from litestar.plugins import InitPluginProtocol

from debug_toolbar.core.toolbar import DebugToolbar
from debug_toolbar.litestar.config import LitestarDebugToolbarConfig
from debug_toolbar.litestar.middleware import DebugToolbarMiddleware

if TYPE_CHECKING:
    from litestar.config.app import AppConfig

logger = logging.getLogger(__name__)


[docs] class DebugToolbarPlugin(InitPluginProtocol): """Litestar plugin for the debug toolbar. This plugin automatically configures the debug toolbar middleware and registers API routes for the toolbar interface. Example:: from litestar import Litestar from debug_toolbar.litestar import DebugToolbarPlugin, LitestarDebugToolbarConfig config = LitestarDebugToolbarConfig( enabled=True, exclude_paths=["/health", "/metrics"], ) app = Litestar( route_handlers=[...], plugins=[DebugToolbarPlugin(config)], ) """ __slots__ = ("_config", "_toolbar")
[docs] def __init__(self, config: LitestarDebugToolbarConfig | None = None) -> None: """Initialize the plugin. Args: config: Toolbar configuration. Uses defaults if not provided. """ self._config = config or LitestarDebugToolbarConfig() self._toolbar: DebugToolbar | None = None
@property def config(self) -> LitestarDebugToolbarConfig: """Get the plugin configuration.""" return self._config @property def toolbar(self) -> DebugToolbar | None: """Get the toolbar instance.""" return self._toolbar
[docs] def on_app_init(self, app_config: AppConfig) -> AppConfig: """Configure the application with the debug toolbar. Args: app_config: The application configuration. Returns: The modified application configuration. """ if not self._config.enabled: return app_config from debug_toolbar.litestar.routes import create_debug_toolbar_router from litestar.middleware import DefineMiddleware self._auto_add_websocket_panel(app_config) self._toolbar = DebugToolbar(self._config) middleware = DefineMiddleware( DebugToolbarMiddleware, config=self._config, toolbar=self._toolbar, ) if app_config.middleware is None: app_config.middleware = [middleware] else: app_config.middleware = list(app_config.middleware) + [middleware] router = create_debug_toolbar_router(self._toolbar.storage) if app_config.route_handlers is None: app_config.route_handlers = [router] else: app_config.route_handlers = list(app_config.route_handlers) + [router] return app_config
def _auto_add_websocket_panel(self, app_config: AppConfig) -> None: """Auto-detect WebSocket usage and add WebSocketPanel if found. Checks route handlers for WebSocket decorators/handlers and automatically adds the WebSocketPanel to extra_panels if WebSocket usage is detected. Args: app_config: The application configuration. """ websocket_panel_path = "debug_toolbar.core.panels.websocket.WebSocketPanel" all_panels = list(self._config.panels) + list(self._config.extra_panels) panel_paths = [p if isinstance(p, str) else f"{p.__module__}.{p.__name__}" for p in all_panels] if websocket_panel_path in panel_paths or "WebSocketPanel" in [ p.__name__ if isinstance(p, type) else p.split(".")[-1] for p in all_panels ]: return if self._detect_websocket_usage(app_config): logger.debug("WebSocket usage detected, auto-adding WebSocketPanel") self._config.extra_panels = list(self._config.extra_panels) + [websocket_panel_path] def _detect_websocket_usage(self, app_config: AppConfig) -> bool: # noqa: PLR0911 """Detect if the application uses WebSocket handlers. Checks for: - litestar.handlers.websocket decorator usage - litestar.handlers.WebsocketListener subclasses - Route handlers with WebSocket type annotations Args: app_config: The application configuration. Returns: True if WebSocket usage is detected, False otherwise. """ if app_config.route_handlers is None: return False try: from litestar.handlers import WebsocketListener from litestar.handlers.websocket_handlers import WebsocketRouteHandler except ImportError: return False for handler in app_config.route_handlers: if isinstance(handler, type) and issubclass(handler, WebsocketListener): return True if isinstance(handler, WebsocketRouteHandler): return True if callable(handler) and hasattr(handler, "__wrapped__"): wrapped = handler.__wrapped__ if isinstance(wrapped, WebsocketRouteHandler): return True if hasattr(handler, "fn") and isinstance(handler, WebsocketRouteHandler): return True return False