Skip to content

Added staticfiles panel class. #494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 14, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ matrix:
env: DJANGO_VERSION=1.4.10
install:
- pip install -e .
- pip install Django==$DJANGO_VERSION sqlparse
- pip install Django==$DJANGO_VERSION sqlparse django-discover-runner
script: make test
7 changes: 5 additions & 2 deletions debug_toolbar/panels/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
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,
Expand Down Expand Up @@ -139,7 +142,7 @@ def __init__(self, *args, **kwargs):
self.hits = 0
self.misses = 0
self.calls = []
self.counts = SortedDict((
self.counts = OrderedDict((
('add', 0),
('get', 0),
('set', 0),
Expand Down
41 changes: 9 additions & 32 deletions debug_toolbar/panels/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,19 @@
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):
Expand All @@ -65,7 +42,7 @@ def emit(self, record):
'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.
Expand Down Expand Up @@ -96,10 +73,10 @@ def nav_subtitle(self):
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})
8 changes: 6 additions & 2 deletions debug_toolbar/panels/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
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

Expand All @@ -21,5 +24,6 @@ def title(self):

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])),
})
179 changes: 179 additions & 0 deletions debug_toolbar/panels/staticfiles.py
Original file line number Diff line number Diff line change
@@ -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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@python_2_unicode_compatible :) and make that a __str__.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, is it safe to import from django.utils.encoding.python_2_unicode_compatible on 1.4.x?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work :)

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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact the debug toolbar doesn't work at all when django.contrib.staticfiles isn't installed, since the templates {% load static from staticfiles %}.

I'm not sure what the best place for this check is, but it should be global.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I guess that's because you opened #495?

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
9 changes: 6 additions & 3 deletions debug_toolbar/panels/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

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

Expand Down Expand Up @@ -46,6 +49,6 @@ def process_response(self, request, response):
versions = sorted(versions, key=lambda version: version[0])

self.record_stats({
'versions': SortedDict(versions),
'versions': OrderedDict(versions),
'paths': sys.path,
})
1 change: 1 addition & 0 deletions debug_toolbar/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
'debug_toolbar.panels.request.RequestPanel',
'debug_toolbar.panels.sql.SQLPanel',
'debug_toolbar.panels.templates.TemplatesPanel',
'debug_toolbar.panels.staticfiles.StaticFilesPanel',
'debug_toolbar.panels.cache.CachePanel',
'debug_toolbar.panels.signals.SignalsPanel',
'debug_toolbar.panels.logging.LoggingPanel',
Expand Down
6 changes: 3 additions & 3 deletions debug_toolbar/templates/debug_toolbar/panels/cache.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% load i18n %}
<h3>{% trans "Summary" %}</h3>
<h4>{% trans "Summary" %}</h4>
<table>
<thead>
<tr>
Expand All @@ -18,7 +18,7 @@ <h3>{% trans "Summary" %}</h3>
</tr>
</tbody>
</table>
<h3>{% trans "Commands" %}</h3>
<h4>{% trans "Commands" %}</h4>
<table>
<thead>
<tr>
Expand All @@ -36,7 +36,7 @@ <h3>{% trans "Commands" %}</h3>
</tbody>
</table>
{% if calls %}
<h3>{% trans "Calls" %}</h3>
<h4>{% trans "Calls" %}</h4>
<table>
<thead>
<tr>
Expand Down
Loading