From b3da26a04572506aea92da377cbefb6899a8d1df Mon Sep 17 00:00:00 2001 From: Zakui Date: Fri, 12 Sep 2025 13:02:10 -0500 Subject: [PATCH 1/2] Added support for async to timer panel --- .gitignore | 2 +- debug_toolbar/panels/timer.py | 1 + docs/architecture.rst | 2 +- docs/changes.rst | 1 + tests/test_integration_async.py | 24 ++++++++++++++++++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1b23b0a2d..1bb868787 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,5 @@ coverage.xml venv .direnv/ .envrc -venv +.venv .vscode diff --git a/debug_toolbar/panels/timer.py b/debug_toolbar/panels/timer.py index 6ef9f0d7c..c2e63cf10 100644 --- a/debug_toolbar/panels/timer.py +++ b/debug_toolbar/panels/timer.py @@ -16,6 +16,7 @@ class TimerPanel(Panel): """ Panel that displays the time a response took in milliseconds. """ + is_async = True def nav_subtitle(self): stats = self.get_stats() diff --git a/docs/architecture.rst b/docs/architecture.rst index 54b3b9318..f3bae60df 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -81,7 +81,7 @@ Problematic Parts the main benefit of the toolbar - Support for async and multi-threading: ``debug_toolbar.middleware.DebugToolbarMiddleware`` is now async compatible and can process async requests. However certain - panels such as ``TimerPanel``, ``RequestPanel`` and ``ProfilingPanel`` aren't + panels such as ``RequestPanel`` and ``ProfilingPanel`` aren't fully compatible and currently being worked on. For now, these panels are disabled by default when running in async environment. follow the progress of this issue in `Async compatible toolbar project `_. diff --git a/docs/changes.rst b/docs/changes.rst index 6a58f7e77..452242279 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -4,6 +4,7 @@ Change log Pending ------- +* Added support for async to timer panel. * Added a note about the default password in ``make example``. * Removed logging about the toolbar failing to serialize a value into JSON. * Moved the the import statement of ``debug_toolbar.urls`` to within the if diff --git a/tests/test_integration_async.py b/tests/test_integration_async.py index 3cc2889c5..8c3176542 100644 --- a/tests/test_integration_async.py +++ b/tests/test_integration_async.py @@ -1,4 +1,5 @@ import unittest +import re from unittest.mock import patch import html5lib @@ -506,6 +507,29 @@ async def test_intercept_redirects(self): # Link to LOCATION header. self.assertIn(b'href="/regular/redirect/"', response.content) + async def test_server_timing_headers(self): + response = await self.async_client.get("/execute_sql/") + server_timing = response["Server-Timing"] + expected_partials = [ + r'TimerPanel_utime;dur=(\d)*(\.(\d)*)?;desc="User CPU time", ', + r'TimerPanel_stime;dur=(\d)*(\.(\d)*)?;desc="System CPU time", ', + r'TimerPanel_total;dur=(\d)*(\.(\d)*)?;desc="Total CPU time", ', + r'TimerPanel_total_time;dur=(\d)*(\.(\d)*)?;desc="Elapsed time", ', + r'SQLPanel_sql_time;dur=(\d)*(\.(\d)*)?;desc="SQL 1 queries", ', + r'CachePanel_total_time;dur=0;desc="Cache 0 Calls"', + ] + for expected in expected_partials: + self.assertTrue(re.compile(expected).search(server_timing)) + + @override_settings(DEBUG_TOOLBAR_CONFIG={"RENDER_PANELS": True}) + async def test_timer_panel(self): + response = await self.async_client.get("/regular/basic/") + self.assertEqual(response.status_code, 200) + self.assertContains( + response, + '