aboutsummaryrefslogtreecommitdiffstats
path: root/debug_toolbar/panels
diff options
context:
space:
mode:
Diffstat (limited to 'debug_toolbar/panels')
-rw-r--r--debug_toolbar/panels/cache.py7
-rw-r--r--debug_toolbar/panels/logging.py41
-rw-r--r--debug_toolbar/panels/settings.py8
-rw-r--r--debug_toolbar/panels/staticfiles.py179
-rw-r--r--debug_toolbar/panels/templates/panel.py10
-rw-r--r--debug_toolbar/panels/versions.py9
6 files changed, 211 insertions, 43 deletions
diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py
index 1c37c6b..bdc8a4d 100644
--- a/debug_toolbar/panels/cache.py
+++ b/debug_toolbar/panels/cache.py
@@ -10,8 +10,11 @@ from django.core.cache import cache as original_cache, get_cache as original_get
from django.core.cache.backends.base import BaseCache
from django.dispatch import Signal
from django.template import Node
-from django.utils.datastructures import SortedDict
from django.utils.translation import ugettext_lazy as _, ungettext
+try:
+ from collections import OrderedDict
+except ImportError:
+ from django.utils.datastructures import SortedDict as OrderedDict
from debug_toolbar.panels import Panel
from debug_toolbar.utils import (tidy_stacktrace, render_stacktrace,
@@ -139,7 +142,7 @@ class CachePanel(Panel):
self.hits = 0
self.misses = 0
self.calls = []
- self.counts = SortedDict((
+ self.counts = OrderedDict((
('add', 0),
('get', 0),
('set', 0),
diff --git a/debug_toolbar/panels/logging.py b/debug_toolbar/panels/logging.py
index 051d5a3..1ee19ce 100644
--- a/debug_toolbar/panels/logging.py
+++ b/debug_toolbar/panels/logging.py
@@ -8,42 +8,19 @@ except ImportError:
threading = None
from django.utils.translation import ungettext, ugettext_lazy as _
from debug_toolbar.panels import Panel
+from debug_toolbar.utils import ThreadCollector
MESSAGE_IF_STRING_REPRESENTATION_INVALID = '[Could not get log message]'
-class LogCollector(object):
- def __init__(self):
- if threading is None:
- raise NotImplementedError(
- "threading module is not available, "
- "the logging panel cannot be used without it")
- self.records = {} # a dictionary that maps threads to log records
+class LogCollector(ThreadCollector):
- def add_record(self, record, thread=None):
+ def collect(self, item, thread=None):
# Avoid logging SQL queries since they are already in the SQL panel
# TODO: Make this check whether SQL panel is enabled
- if record.get('channel', '') == 'django.db.backends':
+ if item.get('channel', '') == 'django.db.backends':
return
-
- self.get_records(thread).append(record)
-
- def get_records(self, thread=None):
- """
- Returns a list of records for the provided thread, of if none is provided,
- returns a list for the current thread.
- """
- if thread is None:
- thread = threading.currentThread()
- if thread not in self.records:
- self.records[thread] = []
- return self.records[thread]
-
- def clear_records(self, thread=None):
- if thread is None:
- thread = threading.currentThread()
- if thread in self.records:
- del self.records[thread]
+ super(LogCollector, self).collect(item, thread)
class ThreadTrackingHandler(logging.Handler):
@@ -65,7 +42,7 @@ class ThreadTrackingHandler(logging.Handler):
'line': record.lineno,
'channel': record.name,
}
- self.collector.add_record(record)
+ self.collector.collect(record)
# We don't use enable/disable_instrumentation because logging is global.
@@ -96,10 +73,10 @@ class LoggingPanel(Panel):
title = _("Log messages")
def process_request(self, request):
- collector.clear_records()
+ collector.clear_collection()
def process_response(self, request, response):
- records = collector.get_records()
+ records = collector.get_collection()
self._records[threading.currentThread()] = records
- collector.clear_records()
+ collector.clear_collection()
self.record_stats({'records': records})
diff --git a/debug_toolbar/panels/settings.py b/debug_toolbar/panels/settings.py
index c59d1d1..b054f8b 100644
--- a/debug_toolbar/panels/settings.py
+++ b/debug_toolbar/panels/settings.py
@@ -3,7 +3,10 @@ from __future__ import absolute_import, unicode_literals
from django.conf import settings
from django.views.debug import get_safe_settings
from django.utils.translation import ugettext_lazy as _
-from django.utils.datastructures import SortedDict
+try:
+ from collections import OrderedDict
+except ImportError:
+ from django.utils.datastructures import SortedDict as OrderedDict
from debug_toolbar.panels import Panel
@@ -21,5 +24,6 @@ class SettingsPanel(Panel):
def process_response(self, request, response):
self.record_stats({
- 'settings': SortedDict(sorted(get_safe_settings().items(), key=lambda s: s[0])),
+ 'settings': OrderedDict(sorted(get_safe_settings().items(),
+ key=lambda s: s[0])),
})
diff --git a/debug_toolbar/panels/staticfiles.py b/debug_toolbar/panels/staticfiles.py
new file mode 100644
index 0000000..f212f2b
--- /dev/null
+++ b/debug_toolbar/panels/staticfiles.py
@@ -0,0 +1,179 @@
+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.exceptions import ImproperlyConfigured
+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 has_content(self):
+ if "django.contrib.staticfiles" not in settings.INSTALLED_APPS:
+ raise ImproperlyConfigured("Could not find staticfiles in "
+ "INSTALLED_APPS setting.")
+ return True
+
+ @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 = getattr(settings, 'STATICFILES_DIRS', ())
+ return [normpath(d) for d 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
diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py
index df39804..74f28b0 100644
--- a/debug_toolbar/panels/templates/panel.py
+++ b/debug_toolbar/panels/templates/panel.py
@@ -81,12 +81,8 @@ class TemplatesPanel(Panel):
def __init__(self, *args, **kwargs):
super(TemplatesPanel, self).__init__(*args, **kwargs)
self.templates = []
- template_rendered.connect(self._store_template_info)
def _store_template_info(self, sender, **kwargs):
- if not self.enabled:
- return
-
template, context = kwargs['template'], kwargs['context']
# Skip templates that we are generating through the debug toolbar.
@@ -157,6 +153,12 @@ class TemplatesPanel(Panel):
url(r'^template_source/$', 'template_source', name='template_source'),
)
+ def enable_instrumentation(self):
+ template_rendered.connect(self._store_template_info)
+
+ def disable_instrumentation(self):
+ template_rendered.disconnect(self._store_template_info)
+
def process_response(self, request, response):
template_context = []
for template_data in self.templates:
diff --git a/debug_toolbar/panels/versions.py b/debug_toolbar/panels/versions.py
index 85672b8..321ba6e 100644
--- a/debug_toolbar/panels/versions.py
+++ b/debug_toolbar/panels/versions.py
@@ -4,9 +4,12 @@ import sys
import django
from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
-from django.utils.datastructures import SortedDict
from django.utils.importlib import import_module
+from django.utils.translation import ugettext_lazy as _
+try:
+ from collections import OrderedDict
+except ImportError:
+ from django.utils.datastructures import SortedDict as OrderedDict
from debug_toolbar.panels import Panel
@@ -46,6 +49,6 @@ class VersionsPanel(Panel):
versions = sorted(versions, key=lambda version: version[0])
self.record_stats({
- 'versions': SortedDict(versions),
+ 'versions': OrderedDict(versions),
'paths': sys.path,
})