1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
from __future__ import absolute_import, unicode_literals
from os.path import normpath, join
try:
import threading
except ImportError:
threading = None
from django.conf import settings
from django.core.files.storage import get_storage_class
from django.contrib.staticfiles import finders, storage
from django.contrib.staticfiles.templatetags import staticfiles
from django.utils.encoding import python_2_unicode_compatible
from django.utils.functional import LazyObject
from django.utils.translation import ungettext, ugettext_lazy as _
try:
from collections import OrderedDict
except ImportError:
from django.utils.datastructures import SortedDict as OrderedDict
from debug_toolbar import panels
from debug_toolbar.utils import ThreadCollector
@python_2_unicode_compatible
class StaticFile(object):
"""
Representing the different properties of a static file.
"""
def __init__(self, path):
self.path = path
def __str__(self):
return self.path
def real_path(self):
return finders.find(self.path)
def url(self):
return storage.staticfiles_storage.url(self.path)
class FileCollector(ThreadCollector):
def collect(self, path, thread=None):
# handle the case of {% static "admin/" %}
if path.endswith('/'):
return
super(FileCollector, self).collect(StaticFile(path), thread)
collector = FileCollector()
class DebugConfiguredStorage(LazyObject):
"""
A staticfiles storage class to be used for collecting which paths
are resolved by using the {% static %} template tag (which uses the
`url` method).
"""
def _setup(self):
configured_storage_cls = get_storage_class(settings.STATICFILES_STORAGE)
class DebugStaticFilesStorage(configured_storage_cls):
def __init__(self, collector, *args, **kwargs):
super(DebugStaticFilesStorage, self).__init__(*args, **kwargs)
self.collector = collector
def url(self, path):
self.collector.collect(path)
return super(DebugStaticFilesStorage, self).url(path)
self._wrapped = DebugStaticFilesStorage(collector)
_original_storage = storage.staticfiles_storage
class StaticFilesPanel(panels.Panel):
"""
A panel to display the found staticfiles.
"""
name = 'Static files'
template = 'debug_toolbar/panels/staticfiles.html'
@property
def title(self):
return (_("Static files (%(num_found)s found, %(num_used)s used)") %
{'num_found': self.num_found, 'num_used': self.num_used})
def __init__(self, *args, **kwargs):
super(StaticFilesPanel, self).__init__(*args, **kwargs)
self.num_found = 0
self._paths = {}
def enable_instrumentation(self):
storage.staticfiles_storage = staticfiles.staticfiles_storage = DebugConfiguredStorage()
def disable_instrumentation(self):
storage.staticfiles_storage = staticfiles.staticfiles_storage = _original_storage
@property
def num_used(self):
return len(self._paths[threading.currentThread()])
nav_title = _('Static files')
@property
def nav_subtitle(self):
num_used = self.num_used
return ungettext("%(num_used)s file used",
"%(num_used)s files used",
num_used) % {'num_used': num_used}
def process_request(self, request):
collector.clear_collection()
def process_response(self, request, response):
used_paths = collector.get_collection()
self._paths[threading.currentThread()] = used_paths
self.record_stats({
'num_found': self.num_found,
'num_used': self.num_used,
'staticfiles': used_paths,
'staticfiles_apps': self.get_staticfiles_apps(),
'staticfiles_dirs': self.get_staticfiles_dirs(),
'staticfiles_finders': self.get_staticfiles_finders(),
})
def get_staticfiles_finders(self):
"""
Returns a sorted mapping between the finder path and the list
of relative and file system paths which that finder was able
to find.
"""
finders_mapping = OrderedDict()
for finder in finders.get_finders():
for path, finder_storage in finder.list([]):
if getattr(finder_storage, 'prefix', None):
prefixed_path = join(finder_storage.prefix, path)
else:
prefixed_path = path
finder_cls = finder.__class__
finder_path = '.'.join([finder_cls.__module__,
finder_cls.__name__])
real_path = finder_storage.path(path)
payload = (prefixed_path, real_path)
finders_mapping.setdefault(finder_path, []).append(payload)
self.num_found += 1
return finders_mapping
def get_staticfiles_dirs(self):
"""
Returns a list of paths to inspect for additional static files
"""
dirs = []
for finder in finders.get_finders():
if isinstance(finder, finders.FileSystemFinder):
dirs.extend(finder.locations)
return [(prefix, normpath(dir)) for prefix, dir in dirs]
def get_staticfiles_apps(self):
"""
Returns a list of app paths that have a static directory
"""
apps = []
for finder in finders.get_finders():
if isinstance(finder, finders.AppDirectoriesFinder):
for app in finder.apps:
if app not in apps:
apps.append(app)
return apps
|