1
+ import contextlib
2
+ from contextvars import ContextVar
1
3
from os .path import join , normpath
2
4
3
5
from django .conf import settings
7
9
from django .utils .translation import gettext_lazy as _ , ngettext
8
10
9
11
from debug_toolbar import panels
10
- from debug_toolbar .utils import ThreadCollector
11
-
12
- try :
13
- import threading
14
- except ImportError :
15
- threading = None
16
12
17
13
18
14
class StaticFile :
@@ -33,15 +29,8 @@ def url(self):
33
29
return storage .staticfiles_storage .url (self .path )
34
30
35
31
36
- class FileCollector (ThreadCollector ):
37
- def collect (self , path , thread = None ):
38
- # handle the case of {% static "admin/" %}
39
- if path .endswith ("/" ):
40
- return
41
- super ().collect (StaticFile (path ), thread )
42
-
43
-
44
- collector = FileCollector ()
32
+ # This will collect the StaticFile instances across threads.
33
+ used_static_files = ContextVar ("djdt_static_used_static_files" )
45
34
46
35
47
36
class DebugConfiguredStorage (LazyObject ):
@@ -65,15 +54,16 @@ def _setup(self):
65
54
configured_storage_cls = get_storage_class (settings .STATICFILES_STORAGE )
66
55
67
56
class DebugStaticFilesStorage (configured_storage_cls ):
68
- def __init__ (self , collector , * args , ** kwargs ):
69
- super ().__init__ (* args , ** kwargs )
70
- self .collector = collector
71
-
72
57
def url (self , path ):
73
- self .collector .collect (path )
58
+ with contextlib .suppress (LookupError ):
59
+ # For LookupError:
60
+ # The ContextVar wasn't set yet. Since the toolbar wasn't properly
61
+ # configured to handle this request, we don't need to capture
62
+ # the static file.
63
+ used_static_files .get ().append (StaticFile (path ))
74
64
return super ().url (path )
75
65
76
- self ._wrapped = DebugStaticFilesStorage (collector )
66
+ self ._wrapped = DebugStaticFilesStorage ()
77
67
78
68
79
69
_original_storage = storage .staticfiles_storage
@@ -97,7 +87,7 @@ def title(self):
97
87
def __init__ (self , * args , ** kwargs ):
98
88
super ().__init__ (* args , ** kwargs )
99
89
self .num_found = 0
100
- self ._paths = {}
90
+ self .used_paths = []
101
91
102
92
def enable_instrumentation (self ):
103
93
storage .staticfiles_storage = DebugConfiguredStorage ()
@@ -120,18 +110,22 @@ def nav_subtitle(self):
120
110
) % {"num_used" : num_used }
121
111
122
112
def process_request (self , request ):
123
- collector .clear_collection ()
124
- return super ().process_request (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
125
122
126
123
def generate_stats (self , request , response ):
127
- used_paths = collector .get_collection ()
128
- self ._paths [threading .current_thread ()] = used_paths
129
-
130
124
self .record_stats (
131
125
{
132
126
"num_found" : self .num_found ,
133
- "num_used" : len (used_paths ),
134
- "staticfiles" : used_paths ,
127
+ "num_used" : len (self . used_paths ),
128
+ "staticfiles" : self . used_paths ,
135
129
"staticfiles_apps" : self .get_staticfiles_apps (),
136
130
"staticfiles_dirs" : self .get_staticfiles_dirs (),
137
131
"staticfiles_finders" : self .get_staticfiles_finders (),
0 commit comments