1+ import contextlib
2+ from contextvars import ContextVar
13from os .path import join , normpath
24
35from django .conf import settings
79from django .utils .translation import gettext_lazy as _ , ngettext
810
911from debug_toolbar import panels
10- from debug_toolbar .utils import ThreadCollector
11-
12- try :
13- import threading
14- except ImportError :
15- threading = None
1612
1713
1814class StaticFile :
@@ -33,15 +29,8 @@ def url(self):
3329 return storage .staticfiles_storage .url (self .path )
3430
3531
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" )
4534
4635
4736class DebugConfiguredStorage (LazyObject ):
@@ -65,15 +54,16 @@ def _setup(self):
6554 configured_storage_cls = get_storage_class (settings .STATICFILES_STORAGE )
6655
6756 class DebugStaticFilesStorage (configured_storage_cls ):
68- def __init__ (self , collector , * args , ** kwargs ):
69- super ().__init__ (* args , ** kwargs )
70- self .collector = collector
71-
7257 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 ))
7464 return super ().url (path )
7565
76- self ._wrapped = DebugStaticFilesStorage (collector )
66+ self ._wrapped = DebugStaticFilesStorage ()
7767
7868
7969_original_storage = storage .staticfiles_storage
@@ -97,7 +87,7 @@ def title(self):
9787 def __init__ (self , * args , ** kwargs ):
9888 super ().__init__ (* args , ** kwargs )
9989 self .num_found = 0
100- self ._paths = {}
90+ self .used_paths = []
10191
10292 def enable_instrumentation (self ):
10393 storage .staticfiles_storage = DebugConfiguredStorage ()
@@ -120,18 +110,22 @@ def nav_subtitle(self):
120110 ) % {"num_used" : num_used }
121111
122112 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
125122
126123 def generate_stats (self , request , response ):
127- used_paths = collector .get_collection ()
128- self ._paths [threading .current_thread ()] = used_paths
129-
130124 self .record_stats (
131125 {
132126 "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 ,
135129 "staticfiles_apps" : self .get_staticfiles_apps (),
136130 "staticfiles_dirs" : self .get_staticfiles_dirs (),
137131 "staticfiles_finders" : self .get_staticfiles_finders (),
0 commit comments