From 405f9f23d3e233fabb88d2012b28b37c2d89f29e Mon Sep 17 00:00:00 2001 From: Aman Pandey Date: Tue, 6 Aug 2024 17:36:38 +0530 Subject: [PATCH] Async compatible redirect panel (#1976) * make redirect panel async capable by using aprocess_request patterm * added async compability test for redirect panel * remove redundant call for super process_request --- debug_toolbar/panels/redirects.py | 25 ++++++++++++++++++++++--- docs/architecture.rst | 2 +- docs/changes.rst | 1 + tests/panels/test_redirects.py | 15 +++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/debug_toolbar/panels/redirects.py b/debug_toolbar/panels/redirects.py index 349564edb..71c008f1b 100644 --- a/debug_toolbar/panels/redirects.py +++ b/debug_toolbar/panels/redirects.py @@ -1,3 +1,5 @@ +from inspect import iscoroutine + from django.template.response import SimpleTemplateResponse from django.utils.translation import gettext_lazy as _ @@ -9,13 +11,15 @@ class RedirectsPanel(Panel): Panel that intercepts redirects and displays a page with debug info. """ - is_async = False + is_async = True has_content = False nav_title = _("Intercept redirects") - def process_request(self, request): - response = super().process_request(request) + def _process_response(self, response): + """ + Common response processing logic. + """ if 300 <= response.status_code < 400: redirect_to = response.get("Location") if redirect_to: @@ -33,3 +37,18 @@ def process_request(self, request): response.cookies = cookies response.render() return response + + async def aprocess_request(self, request, response_coroutine): + """ + Async version of process_request. used for accessing the response + by awaiting it when running in ASGI. + """ + + response = await response_coroutine + return self._process_response(response) + + def process_request(self, request): + response = super().process_request(request) + if iscoroutine(response): + return self.aprocess_request(request, response) + return self._process_response(response) diff --git a/docs/architecture.rst b/docs/architecture.rst index 145676459..c49bfef0f 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -82,7 +82,7 @@ Problematic Parts - Support for async and multi-threading: ``debug_toolbar.middleware.DebugToolbarMiddleware`` is now async compatible and can process async requests. However certain panels such as ``SQLPanel``, ``TimerPanel``, ``StaticFilesPanel``, - ``RequestPanel``, ``RedirectsPanel`` and ``ProfilingPanel`` aren't fully + ``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 d867b7d80..d4a81ffca 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -8,6 +8,7 @@ Pending * Fixed internal toolbar requests being instrumented if the Django setting ``FORCE_SCRIPT_NAME`` was set. * Increase opacity of show Debug Toolbar handle to improve accessibility. +* Changed the ``RedirectsPanel`` to be async compatible. 4.4.6 (2024-07-10) ------------------ diff --git a/tests/panels/test_redirects.py b/tests/panels/test_redirects.py index 6b67e6f1d..2abed9fd0 100644 --- a/tests/panels/test_redirects.py +++ b/tests/panels/test_redirects.py @@ -2,6 +2,7 @@ from django.conf import settings from django.http import HttpResponse +from django.test import AsyncRequestFactory from ..base import BaseTestCase @@ -70,3 +71,17 @@ def test_insert_content(self): self.assertIsNotNone(response) response = self.panel.generate_stats(self.request, redirect) self.assertIsNone(response) + + async def test_async_compatibility(self): + redirect = HttpResponse(status=302) + + async def get_response(request): + return redirect + + await_response = await get_response(self.request) + self._get_response = get_response + + self.request = AsyncRequestFactory().get("/") + response = await self.panel.process_request(self.request) + self.assertIsInstance(response, HttpResponse) + self.assertTrue(response is await_response)