diff --git a/src/debug_toolbar/core/config.py b/src/debug_toolbar/core/config.py index 7a784f0..901d7b7 100644 --- a/src/debug_toolbar/core/config.py +++ b/src/debug_toolbar/core/config.py @@ -31,6 +31,11 @@ class DebugToolbarConfig: panel_display_depth: Max depth for nested data rendering. Defaults to 10. panel_display_max_items: Max items to show in arrays/objects. Defaults to 100. panel_display_max_string: Max string length before truncation. Defaults to 1000. + profiler_backend: Profiling backend: "cprofile", "pyinstrument", or "tachyon". Defaults to "cprofile". + profiler_top_functions: Max functions shown in the profiling panel. Defaults to 50. + profiler_sort_by: Sort order for profiling results. Defaults to "cumulative". + profiler_tachyon_interval_usec: Tachyon sampling interval in microseconds. Defaults to 1000. + enable_flamegraph: Whether to generate flame graph data. Defaults to True. async_profiler_backend: Async profiler backend. "auto" selects best available. async_blocking_threshold_ms: Threshold for blocking call detection. Defaults to 100. async_enable_blocking_detection: Whether to detect blocking calls. Defaults to True. @@ -69,6 +74,12 @@ class DebugToolbarConfig: panel_display_max_items: int = 100 panel_display_max_string: int = 1000 + profiler_backend: Literal["cprofile", "pyinstrument", "tachyon"] = "cprofile" + profiler_top_functions: int = 50 + profiler_sort_by: str = "cumulative" + profiler_tachyon_interval_usec: int = 1000 + enable_flamegraph: bool = True + async_profiler_backend: Literal["taskfactory", "yappi", "auto"] = "auto" async_blocking_threshold_ms: float = 100.0 async_enable_blocking_detection: bool = True diff --git a/tests/unit/test_profiling_panel.py b/tests/unit/test_profiling_panel.py index d0c8c57..6949355 100644 --- a/tests/unit/test_profiling_panel.py +++ b/tests/unit/test_profiling_panel.py @@ -64,6 +64,39 @@ def test_initialization_defaults(self, mock_toolbar: MagicMock) -> None: assert panel._profiling_overhead == 0.0 +class TestProfilingPanelWithRealConfig: + """Profiling options documented in the configuration guide must be accepted by every config class.""" + + @pytest.mark.parametrize( + "config_path", + [ + "debug_toolbar.core.config:DebugToolbarConfig", + "debug_toolbar.litestar.config:LitestarDebugToolbarConfig", + "debug_toolbar.starlette.config:StarletteDebugToolbarConfig", + "debug_toolbar.fastapi.config:FastAPIDebugToolbarConfig", + ], + ) + def test_panel_uses_profiler_options_from_config(self, config_path: str) -> None: + module_name, class_name = config_path.split(":") + config_class = getattr(pytest.importorskip(module_name), class_name) + config = config_class( + profiler_backend="cprofile", + profiler_top_functions=7, + profiler_sort_by="tottime", + profiler_tachyon_interval_usec=500, + enable_flamegraph=False, + ) + toolbar = MagicMock() + toolbar.config = config + + panel = ProfilingPanel(toolbar) + + assert panel._backend == "cprofile" + assert panel._top_functions == 7 + assert panel._sort_by == "tottime" + assert panel._enable_flamegraph is False + + class TestProfilingPanelBackendSelection: """Tests for backend selection logic."""