66import socket
77from functools import lru_cache
88
9+ from asgiref .sync import iscoroutinefunction , markcoroutinefunction
910from django .conf import settings
1011from django .utils .module_loading import import_string
1112
@@ -62,14 +63,50 @@ class DebugToolbarMiddleware:
6263 on outgoing response.
6364 """
6465
66+ sync_capable = True
67+ async_capable = True
68+
6569 def __init__ (self , get_response ):
6670 self .get_response = get_response
71+ # If get_response is a coroutine function, turns us into async mode so
72+ # a thread is not consumed during a whole request.
73+ self .async_mode = iscoroutinefunction (self .get_response )
74+
75+ if self .async_mode :
76+ # Mark the class as async-capable, but do the actual switch inside
77+ # __call__ to avoid swapping out dunder methods.
78+ markcoroutinefunction (self )
6779
6880 def __call__ (self , request ):
6981 # Decide whether the toolbar is active for this request.
82+ if self .async_mode :
83+ return self .__acall__ (request )
84+ # Decide whether the toolbar is active for this request.
7085 show_toolbar = get_show_toolbar ()
7186 if not show_toolbar (request ) or DebugToolbar .is_toolbar_request (request ):
7287 return self .get_response (request )
88+ toolbar = DebugToolbar (request , self .get_response )
89+ # Activate instrumentation ie. monkey-patch.
90+ for panel in toolbar .enabled_panels :
91+ panel .enable_instrumentation ()
92+ try :
93+ # Run panels like Django middleware.
94+ response = toolbar .process_request (request )
95+ finally :
96+ clear_stack_trace_caches ()
97+ # Deactivate instrumentation ie. monkey-unpatch. This must run
98+ # regardless of the response. Keep 'return' clauses below.
99+ for panel in reversed (toolbar .enabled_panels ):
100+ panel .disable_instrumentation ()
101+
102+ return self ._postprocess (request , response , toolbar )
103+
104+ async def __acall__ (self , request ):
105+ # Decide whether the toolbar is active for this request.
106+ show_toolbar = get_show_toolbar ()
107+ if not show_toolbar (request ) or DebugToolbar .is_toolbar_request (request ):
108+ response = await self .get_response (request )
109+ return response
73110
74111 toolbar = DebugToolbar (request , self .get_response )
75112
@@ -78,14 +115,20 @@ def __call__(self, request):
78115 panel .enable_instrumentation ()
79116 try :
80117 # Run panels like Django middleware.
81- response = toolbar .process_request (request )
118+ response = await toolbar .process_request (request )
82119 finally :
83120 clear_stack_trace_caches ()
84121 # Deactivate instrumentation ie. monkey-unpatch. This must run
85122 # regardless of the response. Keep 'return' clauses below.
86123 for panel in reversed (toolbar .enabled_panels ):
87124 panel .disable_instrumentation ()
88125
126+ return self ._postprocess (request , response , toolbar )
127+
128+ def _postprocess (self , request , response , toolbar ):
129+ """
130+ Post-process the response.
131+ """
89132 # Generate the stats for all requests when the toolbar is being shown,
90133 # but not necessarily inserted.
91134 for panel in reversed (toolbar .enabled_panels ):
0 commit comments