diff options
| author | Jannis Leidel | 2012-04-06 12:59:56 +0200 | 
|---|---|---|
| committer | Jannis Leidel | 2013-12-08 23:33:39 +0100 | 
| commit | 184992f2d5773016e2c8315c91acbf8f601775bf (patch) | |
| tree | 17753ea901f2db95e93f5897606fe0cbbda5e860 | |
| parent | e334bf6e4b4975fd87957c473a62c0d889e38cb0 (diff) | |
| download | django-debug-toolbar-184992f2d5773016e2c8315c91acbf8f601775bf.tar.bz2 | |
Added staticfiles panel class.
| -rw-r--r-- | debug_toolbar/panels/logging.py | 41 | ||||
| -rw-r--r-- | debug_toolbar/panels/staticfiles.py | 142 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/panels/cache.html | 6 | ||||
| -rw-r--r-- | debug_toolbar/templates/debug_toolbar/panels/staticfiles.html | 57 | ||||
| -rw-r--r-- | debug_toolbar/utils.py | 33 | ||||
| -rw-r--r-- | example/settings.py | 3 | ||||
| -rw-r--r-- | example/static/test.css | 3 | 
7 files changed, 250 insertions, 35 deletions
| 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/staticfiles.py b/debug_toolbar/panels/staticfiles.py new file mode 100644 index 0000000..9b3829d --- /dev/null +++ b/debug_toolbar/panels/staticfiles.py @@ -0,0 +1,142 @@ +from __future__ import absolute_import +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.translation import ungettext, ugettext_lazy as _ +from django.utils.datastructures import SortedDict +from django.utils.functional import LazyObject + +from debug_toolbar import panels +from debug_toolbar.utils import ThreadCollector + + +class StaticFile(object): + +    def __init__(self, path): +        self.path = path + +    def __unicode__(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): +    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) + +storage.staticfiles_storage = staticfiles.staticfiles_storage = DebugConfiguredStorage() + + +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_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.ignore_patterns = [] +        self._paths = {} + +    @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): +        staticfiles_finders = SortedDict() +        for finder in finders.get_finders(): +            for path, finder_storage in finder.list(self.ignore_patterns): +                if getattr(finder_storage, 'prefix', None): +                    prefixed_path = join(finder_storage.prefix, path) +                else: +                    prefixed_path = path +                finder_path = '.'.join([finder.__class__.__module__, +                                        finder.__class__.__name__]) +                real_path = finder_storage.path(path) +                payload = (prefixed_path, real_path) +                staticfiles_finders.setdefault(finder_path, []).append(payload) +                self.num_found += 1 + +        dirs = getattr(settings, 'STATICFILES_DIRS', ()) + +        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_static_apps(), +            'staticfiles_dirs': [normpath(d) for d in dirs], +            'staticfiles_finders': staticfiles_finders, +        }) + +    def get_static_apps(self): +        for finder in finders.get_finders(): +            if isinstance(finder, finders.AppDirectoriesFinder): +                return finder.apps +        return [] diff --git a/debug_toolbar/templates/debug_toolbar/panels/cache.html b/debug_toolbar/templates/debug_toolbar/panels/cache.html index 595afd6..82ccc49 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/cache.html +++ b/debug_toolbar/templates/debug_toolbar/panels/cache.html @@ -1,5 +1,5 @@  {% load i18n %} -<h3>{% trans "Summary" %}</h3> +<h4>{% trans "Summary" %}</h4>  <table>  	<thead>  	<tr> @@ -18,7 +18,7 @@  	</tr>  	</tbody>  </table> -<h3>{% trans "Commands" %}</h3> +<h4>{% trans "Commands" %}</h4>  <table>  	<thead>  	<tr> @@ -36,7 +36,7 @@  	</tbody>  </table>  {% if calls %} -<h3>{% trans "Calls" %}</h3> +<h4>{% trans "Calls" %}</h4>  <table>  	<thead>  		<tr> diff --git a/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html b/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html new file mode 100644 index 0000000..e405a25 --- /dev/null +++ b/debug_toolbar/templates/debug_toolbar/panels/staticfiles.html @@ -0,0 +1,57 @@ +{% load i18n %} +{% load static from staticfiles%} + +<h4>{% blocktrans count staticfiles_dirs|length as dirs_count %}Static file path{% plural %}Static file paths{% endblocktrans %}</h4> +{% if staticfiles_dirs %} +    <ol> +    {% for staticfiles_dir in staticfiles_dirs %} +        <li>{{ staticfiles_dir }}</li> +    {% endfor %} +    </ol> +{% else %} +    <p>{% trans "None" %}</p> +{% endif %} + +<h4>{% blocktrans count staticfiles_apps|length as apps_count %}Static file app{% plural %}Static file apps{% endblocktrans %}</h4> +{% if staticfiles_apps %} +    <ol> +    {% for static_app in staticfiles_apps %} +        <li>{{ static_app }}</li> +    {% endfor %} +    </ol> +{% else %} +    <p>{% trans "None" %}</p> +{% endif %} + +<h4>{% blocktrans count staticfiles|length as staticfiles_count %}Static file{% plural %}Static files{% endblocktrans %}</h4> +{% if staticfiles %} +<dl> +{% for staticfile in staticfiles %} +    <dt><strong><a class="toggleTemplate" href="{{ staticfile.url }}">{{ staticfile }}</a></strong></dt> +    <dd><samp>{{ staticfile.real_path }}</samp></dd> +{% endfor %} +</dl> +{% else %} +    <p>{% trans "None" %}</p> +{% endif %} + + +{% for finder, payload in staticfiles_finders.items %} +<h4>{{ finder }} ({{ payload|length }} files)</h4> +<table> +    <thead> +        <tr> +            <th>{% trans 'Path' %}</th> +            <th>{% trans 'Location' %}</th> +        </tr> +    </thead> +    <tbody> +    {% for path, real_path in payload %} +    <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> +        <td>{{ path }}</td> +        <td>{{ real_path }}</td> +    </tr> +    {% endfor %} +    </tbody> +</table> +{% endfor %} diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index d84c79c..0aedb54 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -4,6 +4,10 @@ import inspect  import os.path  import re  import sys +try: +    import threading +except ImportError: +    threading = None  import django  from django.core.exceptions import ImproperlyConfigured @@ -199,3 +203,32 @@ def get_stack(context=1):          framelist.append((frame,) + getframeinfo(frame, context))          frame = frame.f_back      return framelist + + +class ThreadCollector(object): +    def __init__(self): +        if threading is None: +            raise NotImplementedError( +                "threading module is not available, " +                "this panel cannot be used without it") +        self.collections = {}  # a dictionary that maps threads to collections + +    def get_collection(self, thread=None): +        """ +        Returns a list of collected items 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.collections: +            self.collections[thread] = [] +        return self.collections[thread] + +    def clear_collection(self, thread=None): +        if thread is None: +            thread = threading.currentThread() +        if thread in self.collections: +            del self.collections[thread] + +    def collect(self, item, thread=None): +        self.get_collection(thread).append(item) diff --git a/example/settings.py b/example/settings.py index 4c29ba9..ca78f70 100644 --- a/example/settings.py +++ b/example/settings.py @@ -89,4 +89,7 @@ DEBUG_TOOLBAR_PANELS = [      'debug_toolbar.panels.logging.LoggingPanel',      'debug_toolbar.panels.redirects.RedirectsPanel',      'debug_toolbar.panels.profiling.ProfilingPanel', +    'debug_toolbar.panels.staticfiles.StaticFilesPanel',  ] + +STATICFILES_DIRS = [os.path.join(BASE_DIR, 'example', 'static')] diff --git a/example/static/test.css b/example/static/test.css new file mode 100644 index 0000000..8d7d127 --- /dev/null +++ b/example/static/test.css @@ -0,0 +1,3 @@ +body { +    color: green; +}
\ No newline at end of file | 
