1
1
import contextlib
2
+ import uuid
2
3
from contextvars import ContextVar
3
4
from os .path import join , normpath
4
5
5
6
from django .conf import settings
6
7
from django .contrib .staticfiles import finders , storage
8
+ from django .dispatch import Signal
7
9
from django .utils .functional import LazyObject
8
10
from django .utils .translation import gettext_lazy as _ , ngettext
9
11
@@ -29,7 +31,9 @@ def url(self):
29
31
30
32
31
33
# This will collect the StaticFile instances across threads.
32
- used_static_files = ContextVar ("djdt_static_used_static_files" )
34
+ used_static_files = ContextVar ("djdt_static_used_static_files" , default = [])
35
+ request_id_context_var = ContextVar ("djdt_request_id_store" )
36
+ record_static_file_signal = Signal ()
33
37
34
38
35
39
class DebugConfiguredStorage (LazyObject ):
@@ -59,7 +63,12 @@ def url(self, path):
59
63
# The ContextVar wasn't set yet. Since the toolbar wasn't properly
60
64
# configured to handle this request, we don't need to capture
61
65
# the static file.
62
- used_static_files .get ().append (StaticFile (path ))
66
+ request_id = request_id_context_var .get ()
67
+ record_static_file_signal .send (
68
+ sender = self ,
69
+ staticfile = StaticFile (path ),
70
+ request_id = request_id ,
71
+ )
63
72
return super ().url (path )
64
73
65
74
self ._wrapped = DebugStaticFilesStorage ()
@@ -73,7 +82,7 @@ class StaticFilesPanel(panels.Panel):
73
82
A panel to display the found staticfiles.
74
83
"""
75
84
76
- is_async = False
85
+ is_async = True
77
86
name = "Static files"
78
87
template = "debug_toolbar/panels/staticfiles.html"
79
88
@@ -88,12 +97,25 @@ def __init__(self, *args, **kwargs):
88
97
super ().__init__ (* args , ** kwargs )
89
98
self .num_found = 0
90
99
self .used_paths = []
100
+ self .request_id = str (uuid .uuid4 ())
101
+
102
+ def _store_static_files_signal_handler (self , sender , staticfile , ** kwargs ):
103
+ with contextlib .suppress (LookupError ):
104
+ # Only record the static file if the request_id matches the one
105
+ # that was used to create the panel.
106
+ # as sender of the signal and this handler will have multiple
107
+ # concurrent connections and we want to avoid storing of same
108
+ # staticfile from other connections as well.
109
+ if request_id_context_var .get () == self .request_id :
110
+ used_static_files .get ().append (staticfile )
91
111
92
112
def enable_instrumentation (self ):
93
113
storage .staticfiles_storage = DebugConfiguredStorage ()
114
+ record_static_file_signal .connect (self ._store_static_files_signal_handler )
115
+ request_id_context_var .set (self .request_id )
94
116
95
117
def disable_instrumentation (self ):
96
- storage . staticfiles_storage = _original_storage
118
+ record_static_file_signal . disconnect ( self . _store_static_files_signal_handler )
97
119
98
120
@property
99
121
def num_used (self ):
@@ -109,18 +131,20 @@ def nav_subtitle(self):
109
131
"%(num_used)s file used" , "%(num_used)s files used" , num_used
110
132
) % {"num_used" : num_used }
111
133
112
- def process_request (self , request ):
113
- reset_token = used_static_files .set ([])
114
- response = super ().process_request (request )
115
- # Make a copy of the used paths so that when the
116
- # ContextVar is reset, our panel still has the data.
117
- self .used_paths = used_static_files .get ().copy ()
118
- # Reset the ContextVar to be empty again, removing the reference
119
- # to the list of used files.
120
- used_static_files .reset (reset_token )
121
- return response
134
+ # def process_request(self, request):
135
+ # reset_token = used_static_files.set([])
136
+ # response = super().process_request(request)
137
+ # # Make a copy of the used paths so that when the
138
+ # # ContextVar is reset, our panel still has the data.
139
+ # self.used_paths = used_static_files.get().copy()
140
+ # # Reset the ContextVar to be empty again, removing the reference
141
+ # # to the list of used files.
142
+ # used_static_files.reset(reset_token)
143
+ # return response
122
144
123
145
def generate_stats (self , request , response ):
146
+ self .used_paths = used_static_files .get ().copy ()
147
+ used_static_files .get ().clear ()
124
148
self .record_stats (
125
149
{
126
150
"num_found" : self .num_found ,
0 commit comments