[Python-modules-commits] [python-django-debug-toolbar] 01/01: New upstream version 1.8
Andrew Starr-Bochicchio
asb at moszumanska.debian.org
Mon Jul 3 15:22:32 UTC 2017
This is an automated email from the git hooks/post-receive script.
asb pushed a commit to annotated tag upstream/1.8
in repository python-django-debug-toolbar.
commit f5bbe7578391d058ce34cec5e5a48b342f01380b
Author: Andrew Starr-Bochicchio <a.starr.b at gmail.com>
Date: Mon Jul 3 10:58:24 2017 -0400
New upstream version 1.8
---
.travis.yml | 48 +++++---
README.rst | 2 +-
debug_toolbar/__init__.py | 9 +-
debug_toolbar/apps.py | 4 +-
debug_toolbar/decorators.py | 16 +++
debug_toolbar/middleware.py | 35 +++---
debug_toolbar/panels/request.py | 6 +-
debug_toolbar/panels/signals.py | 9 +-
debug_toolbar/panels/sql/forms.py | 15 +--
debug_toolbar/panels/sql/panel.py | 14 +--
debug_toolbar/panels/sql/tracking.py | 17 ++-
debug_toolbar/panels/sql/views.py | 6 +-
debug_toolbar/panels/staticfiles.py | 1 +
debug_toolbar/panels/templates/panel.py | 123 +++++++++++++--------
debug_toolbar/panels/templates/views.py | 35 ++++--
debug_toolbar/settings.py | 8 +-
debug_toolbar/static/debug_toolbar/css/toolbar.css | 3 +-
.../static/debug_toolbar/js/toolbar.timer.js | 4 +-
.../templates/debug_toolbar/panels/request.html | 16 +--
.../templates/debug_toolbar/panels/sql.html | 8 +-
.../templates/debug_toolbar/panels/templates.html | 2 +-
.../templates/debug_toolbar/panels/timer.html | 12 +-
.../templates/debug_toolbar/panels/versions.html | 7 +-
debug_toolbar/toolbar.py | 28 +----
debug_toolbar/utils.py | 7 +-
debug_toolbar/views.py | 2 +
docs/changes.rst | 44 ++++++++
docs/conf.py | 8 +-
docs/configuration.rst | 15 ++-
docs/contributing.rst | 8 +-
docs/installation.rst | 4 +-
docs/panels.rst | 16 ++-
docs/tips.rst | 5 +-
example/templates/index.html | 2 +-
example/templates/jquery/index.html | 2 +-
example/templates/mootools/index.html | 2 +-
example/templates/prototype/index.html | 3 +-
requirements_dev.txt | 1 +
setup.py | 6 +-
tests/panels/test_redirects.py | 2 +-
tests/panels/test_sql.py | 2 +-
tests/panels/test_template.py | 14 ++-
tests/settings.py | 7 ++
tests/templates/jinja2/basic.jinja | 2 +
tests/test_integration.py | 113 +++++++++++++++++++
tests/urls.py | 1 +
tests/views.py | 4 +
tox.ini | 10 +-
48 files changed, 512 insertions(+), 196 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 538d1ac..dd5722f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,20 +1,40 @@
language: python
sudo: false
-python: 3.5
cache: pip
-env:
- - TOXENV="py27-dj18"
- - TOXENV="py33-dj18"
- - TOXENV="py34-dj18"
- - TOXENV="py27-dj19"
- - TOXENV="py27-dj110"
- - TOXENV="py34-dj19"
- - TOXENV="py34-dj110"
- - TOXENV="py35-dj19"
- - TOXENV="py35-dj110"
- - TOXENV="flake8"
- - TOXENV="isort"
- - TOXENV="readme"
+matrix:
+ include:
+ - python: 2.7
+ env: TOXENV=py27-dj18
+ - python: 3.3
+ env: TOXENV=py33-dj18
+ - python: 3.4
+ env: TOXENV=py34-dj18
+ - python: 2.7
+ env: TOXENV=py27-dj19
+ - python: 3.4
+ env: TOXENV=py34-dj19
+ - python: 3.5
+ env: TOXENV=py35-dj19
+ - python: 2.7
+ env: TOXENV=py27-dj110
+ - python: 3.4
+ env: TOXENV=py34-dj110
+ - python: 3.5
+ env: TOXENV=py35-dj110
+ - python: 2.7
+ env: TOXENV=py27-dj111
+ - python: 3.4
+ env: TOXENV=py34-dj111
+ - python: 3.5
+ env: TOXENV=py35-dj111
+ - python: 3.6
+ env: TOXENV=py36-dj111
+ - python: 2.7
+ env: TOXENV=flake8
+ - python: 2.7
+ env: TOXENV=isort
+ - python: 2.7
+ env: TOXENV=readme
install:
- pip install tox codecov
script:
diff --git a/README.rst b/README.rst
index 0bd577e..343c05c 100644
--- a/README.rst
+++ b/README.rst
@@ -31,7 +31,7 @@ Here's a screenshot of the toolbar in action:
In addition to the built-in panels, a number of third-party panels are
contributed by the community.
-The current version of the Debug Toolbar is 1.6. It works on Django ≥ 1.8.
+The current version of the Debug Toolbar is 1.8. It works on Django ≥ 1.8.
Documentation, including installation and configuration instructions, is
available at https://django-debug-toolbar.readthedocs.io/.
diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py
index 0d45138..3ee4fdc 100644
--- a/debug_toolbar/__init__.py
+++ b/debug_toolbar/__init__.py
@@ -1,5 +1,7 @@
from __future__ import absolute_import, unicode_literals
+import django
+
__all__ = ['VERSION']
@@ -11,9 +13,10 @@ except Exception:
# Code that discovers files or modules in INSTALLED_APPS imports this module.
-# Reference URLpatterns with a string to avoid the risk of circular imports.
-
-urls = 'debug_toolbar.toolbar', 'djdt', 'djdt'
+if django.VERSION < (1, 9):
+ urls = 'debug_toolbar.toolbar', 'djdt', 'djdt'
+else:
+ urls = 'debug_toolbar.toolbar', 'djdt'
default_app_config = 'debug_toolbar.apps.DebugToolbarConfig'
diff --git a/debug_toolbar/apps.py b/debug_toolbar/apps.py
index a170c8f..6b9f710 100644
--- a/debug_toolbar/apps.py
+++ b/debug_toolbar/apps.py
@@ -9,8 +9,6 @@ from django.middleware.gzip import GZipMiddleware
from django.utils.module_loading import import_string
from django.utils.translation import ugettext_lazy as _
-from debug_toolbar.middleware import DebugToolbarMiddleware
-
class DebugToolbarConfig(AppConfig):
name = 'debug_toolbar'
@@ -19,6 +17,8 @@ class DebugToolbarConfig(AppConfig):
@register
def check_middleware(app_configs, **kwargs):
+ from debug_toolbar.middleware import DebugToolbarMiddleware
+
errors = []
gzip_index = None
debug_toolbar_index = None
diff --git a/debug_toolbar/decorators.py b/debug_toolbar/decorators.py
new file mode 100644
index 0000000..a1f7f3a
--- /dev/null
+++ b/debug_toolbar/decorators.py
@@ -0,0 +1,16 @@
+import functools
+
+from django.http import Http404
+
+
+def require_show_toolbar(view):
+ @functools.wraps(view)
+ def inner(request, *args, **kwargs):
+ from debug_toolbar.middleware import get_show_toolbar
+
+ show_toolbar = get_show_toolbar()
+ if not show_toolbar(request):
+ raise Http404
+
+ return view(request, *args, **kwargs)
+ return inner
diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py
index 4b91bae..c387611 100644
--- a/debug_toolbar/middleware.py
+++ b/debug_toolbar/middleware.py
@@ -10,7 +10,7 @@ import threading
from django.conf import settings
from django.utils import six
from django.utils.encoding import force_text
-from django.utils.functional import cached_property
+from django.utils.lru_cache import lru_cache
from django.utils.module_loading import import_string
from debug_toolbar import settings as dt_settings
@@ -33,12 +33,20 @@ def show_toolbar(request):
if request.META.get('REMOTE_ADDR', None) not in settings.INTERNAL_IPS:
return False
- if request.is_ajax():
- return False
-
return bool(settings.DEBUG)
+ at lru_cache()
+def get_show_toolbar():
+ # If SHOW_TOOLBAR_CALLBACK is a string, which is the recommended
+ # setup, resolve it to the corresponding callable.
+ func_or_path = dt_settings.get_config()['SHOW_TOOLBAR_CALLBACK']
+ if isinstance(func_or_path, six.string_types):
+ return import_string(func_or_path)
+ else:
+ return func_or_path
+
+
class DebugToolbarMiddleware(MiddlewareMixin):
"""
Middleware to set up Debug Toolbar on incoming request and render toolbar
@@ -46,19 +54,14 @@ class DebugToolbarMiddleware(MiddlewareMixin):
"""
debug_toolbars = {}
- @cached_property
- def show_toolbar(self):
- # If SHOW_TOOLBAR_CALLBACK is a string, which is the recommended
- # setup, resolve it to the corresponding callable.
- func_or_path = dt_settings.get_config()['SHOW_TOOLBAR_CALLBACK']
- if isinstance(func_or_path, six.string_types):
- return import_string(func_or_path)
- else:
- return func_or_path
-
def process_request(self, request):
# Decide whether the toolbar is active for this request.
- if not self.show_toolbar(request):
+ show_toolbar = get_show_toolbar()
+ if not show_toolbar(request):
+ return
+
+ # Don't render the toolbar during AJAX requests.
+ if request.is_ajax():
return
toolbar = DebugToolbar(request)
@@ -119,7 +122,7 @@ class DebugToolbarMiddleware(MiddlewareMixin):
response.set_cookie('djdt', 'hide', 864000)
# Insert the toolbar in the response.
- content = force_text(response.content, encoding=settings.DEFAULT_CHARSET)
+ content = force_text(response.content, encoding=response.charset)
insert_before = dt_settings.get_config()['INSERT_BEFORE']
pattern = re.escape(insert_before)
bits = re.split(pattern, content, flags=re.IGNORECASE)
diff --git a/debug_toolbar/panels/request.py b/debug_toolbar/panels/request.py
index 0aecda2..ae5b2a3 100644
--- a/debug_toolbar/panels/request.py
+++ b/debug_toolbar/panels/request.py
@@ -1,6 +1,5 @@
from __future__ import absolute_import, unicode_literals
-from django.core.urlresolvers import resolve
from django.http import Http404
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
@@ -8,6 +7,11 @@ from django.utils.translation import ugettext_lazy as _
from debug_toolbar.panels import Panel
from debug_toolbar.utils import get_name_from_obj
+try:
+ from django.urls import resolve
+except: # Django < 1.10 pragma: no cover
+ from django.core.urlresolvers import resolve
+
class RequestPanel(Panel):
"""
diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py
index fd2ed3b..cd647db 100644
--- a/debug_toolbar/panels/signals.py
+++ b/debug_toolbar/panels/signals.py
@@ -1,7 +1,6 @@
from __future__ import absolute_import, unicode_literals
import weakref
-from importlib import import_module
from django.core.signals import (
got_request_exception, request_finished, request_started,
@@ -11,6 +10,7 @@ from django.db.models.signals import (
class_prepared, post_delete, post_init, post_migrate, post_save,
pre_delete, pre_init, pre_save,
)
+from django.utils.module_loading import import_string
from django.utils.translation import ugettext_lazy as _, ungettext
from debug_toolbar.panels import Panel
@@ -55,16 +55,13 @@ class SignalsPanel(Panel):
def signals(self):
signals = self.SIGNALS.copy()
for signal in self.toolbar.config['EXTRA_SIGNALS']:
- mod_path, signal_name = signal.rsplit('.', 1)
- signals_mod = import_module(mod_path)
- signals[signal_name] = getattr(signals_mod, signal_name)
+ signal_name = signal.rsplit('.', 1)[-1]
+ signals[signal_name] = import_string(signal)
return signals
def generate_stats(self, request, response):
signals = []
for name, signal in sorted(self.signals.items(), key=lambda x: x[0]):
- if signal is None:
- continue
receivers = []
for receiver in signal.receivers:
receiver = receiver[1]
diff --git a/debug_toolbar/panels/sql/forms.py b/debug_toolbar/panels/sql/forms.py
index 6278a4c..a4e622f 100644
--- a/debug_toolbar/panels/sql/forms.py
+++ b/debug_toolbar/panels/sql/forms.py
@@ -1,13 +1,15 @@
from __future__ import absolute_import, unicode_literals
import hashlib
+import hmac
import json
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import connections
-from django.utils.encoding import force_text
+from django.utils.crypto import constant_time_compare
+from django.utils.encoding import force_bytes
from django.utils.functional import cached_property
from debug_toolbar.panels.sql.utils import reformat_sql
@@ -68,7 +70,7 @@ class SQLSelectForm(forms.Form):
def clean_hash(self):
hash = self.cleaned_data['hash']
- if hash != self.make_hash(self.data):
+ if not constant_time_compare(hash, self.make_hash(self.data)):
raise ValidationError('Tamper alert')
return hash
@@ -77,11 +79,10 @@ class SQLSelectForm(forms.Form):
return reformat_sql(self.cleaned_data['sql'])
def make_hash(self, data):
- items = [settings.SECRET_KEY, data['sql'], data['params']]
- # Replace lines endings with spaces to preserve the hash value
- # even when the browser normalizes \r\n to \n in inputs.
- items = [' '.join(force_text(item).splitlines()) for item in items]
- return hashlib.sha1(''.join(items).encode('utf-8')).hexdigest()
+ m = hmac.new(key=force_bytes(settings.SECRET_KEY), digestmod=hashlib.sha1)
+ for item in [data['sql'], data['params']]:
+ m.update(force_bytes(item))
+ return m.hexdigest()
@property
def connection(self):
diff --git a/debug_toolbar/panels/sql/panel.py b/debug_toolbar/panels/sql/panel.py
index 275ec96..94f22ff 100644
--- a/debug_toolbar/panels/sql/panel.py
+++ b/debug_toolbar/panels/sql/panel.py
@@ -55,7 +55,7 @@ class SQLPanel(Panel):
"""
def __init__(self, *args, **kwargs):
super(SQLPanel, self).__init__(*args, **kwargs)
- self._offset = dict((k, len(connections[k].queries)) for k in connections)
+ self._offset = {k: len(connections[k].queries) for k in connections}
self._sql_time = 0
self._num_queries = 0
self._queries = []
@@ -148,7 +148,7 @@ class SQLPanel(Panel):
for n, db in enumerate(self._databases.values()):
rgb = [0, 0, 0]
color = n % 3
- rgb[color] = 256 - n / 3 * factor
+ rgb[color] = 256 - n // 3 * factor
nn = color
# XXX: pretty sure this is horrible after so many aliases
while rgb[color] < factor:
@@ -212,14 +212,14 @@ class SQLPanel(Panel):
# Queries are duplicates only if there's as least 2 of them.
# Also, to hide queries, we need to give all the duplicate groups an id
query_colors = contrasting_color_generator()
- query_duplicates = dict(
- (alias, dict(
- (query, (duplicate_count, next(query_colors)))
+ query_duplicates = {
+ alias: {
+ query: (duplicate_count, next(query_colors))
for query, duplicate_count in queries.items()
if duplicate_count >= 2
- ))
+ }
for alias, queries in query_duplicates.items()
- )
+ }
for alias, query in self._queries:
try:
diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py
index de8c866..cf7cae2 100644
--- a/debug_toolbar/panels/sql/tracking.py
+++ b/debug_toolbar/panels/sql/tracking.py
@@ -38,8 +38,14 @@ def wrap_cursor(connection, panel):
if not hasattr(connection, '_djdt_cursor'):
connection._djdt_cursor = connection.cursor
- def cursor():
- return state.Wrapper(connection._djdt_cursor(), connection, panel)
+ def cursor(*args, **kwargs):
+ # Per the DB API cursor() does not accept any arguments. There's
+ # some code in the wild which does not follow that convention,
+ # so we pass on the arguments even though it's not clean.
+ # See:
+ # https://github.com/jazzband/django-debug-toolbar/pull/615
+ # https://github.com/jazzband/django-debug-toolbar/pull/896
+ return state.Wrapper(connection._djdt_cursor(*args, **kwargs), connection, panel)
connection.cursor = cursor
return cursor
@@ -85,9 +91,8 @@ class NormalCursorWrapper(object):
if not params:
return params
if isinstance(params, dict):
- return dict((key, self._quote_expr(value))
- for key, value in params.items())
- return list(map(self._quote_expr, params))
+ return {key: self._quote_expr(value) for key, value in params.items()}
+ return [self._quote_expr(p) for p in params]
def _decode(self, param):
try:
@@ -108,7 +113,7 @@ class NormalCursorWrapper(object):
stacktrace = []
_params = ''
try:
- _params = json.dumps(list(map(self._decode, params)))
+ _params = json.dumps([self._decode(p) for p in params])
except Exception:
pass # object not JSON serializable
diff --git a/debug_toolbar/panels/sql/views.py b/debug_toolbar/panels/sql/views.py
index 5c7d590..0d6b4e3 100644
--- a/debug_toolbar/panels/sql/views.py
+++ b/debug_toolbar/panels/sql/views.py
@@ -4,10 +4,12 @@ from django.http import HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.views.decorators.csrf import csrf_exempt
+from debug_toolbar.decorators import require_show_toolbar
from debug_toolbar.panels.sql.forms import SQLSelectForm
@csrf_exempt
+ at require_show_toolbar
def sql_select(request):
"""Returns the output of the SQL SELECT statement"""
form = SQLSelectForm(request.POST or None)
@@ -33,6 +35,7 @@ def sql_select(request):
@csrf_exempt
+ at require_show_toolbar
def sql_explain(request):
"""Returns the output of the SQL EXPLAIN on the given query"""
form = SQLSelectForm(request.POST or None)
@@ -46,7 +49,7 @@ def sql_explain(request):
if vendor == 'sqlite':
# SQLite's EXPLAIN dumps the low-level opcodes generated for a query;
# EXPLAIN QUERY PLAN dumps a more human-readable summary
- # See http://www.sqlite.org/lang_explain.html for details
+ # See https://www.sqlite.org/lang_explain.html for details
cursor.execute("EXPLAIN QUERY PLAN %s" % (sql,), params)
elif vendor == 'postgresql':
cursor.execute("EXPLAIN ANALYZE %s" % (sql,), params)
@@ -69,6 +72,7 @@ def sql_explain(request):
@csrf_exempt
+ at require_show_toolbar
def sql_profile(request):
"""Returns the output of running the SQL and getting the profiling statistics"""
form = SQLSelectForm(request.POST or None)
diff --git a/debug_toolbar/panels/staticfiles.py b/debug_toolbar/panels/staticfiles.py
index ea6d30c..cd400d7 100644
--- a/debug_toolbar/panels/staticfiles.py
+++ b/debug_toolbar/panels/staticfiles.py
@@ -72,6 +72,7 @@ class DebugConfiguredStorage(LazyObject):
self._wrapped = DebugStaticFilesStorage(collector)
+
_original_storage = storage.staticfiles_storage
diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py
index 7d8a079..7367cc1 100644
--- a/debug_toolbar/panels/templates/panel.py
+++ b/debug_toolbar/panels/templates/panel.py
@@ -23,7 +23,7 @@ from debug_toolbar.panels.templates import views
# immediately when the panel is disabled to keep the overhead small.
# Code taken and adapted from Simon Willison and Django Snippets:
-# http://www.djangosnippets.org/snippets/766/
+# https://www.djangosnippets.org/snippets/766/
if Template._render != instrumented_test_render:
Template.original_render = Template._render
@@ -58,6 +58,7 @@ def _request_context_bind_template(self, template):
# Unset context processors.
self.dicts[self._processors_index] = {}
+
RequestContext.bind_template = _request_context_bind_template
@@ -68,58 +69,89 @@ class TemplatesPanel(Panel):
def __init__(self, *args, **kwargs):
super(TemplatesPanel, self).__init__(*args, **kwargs)
self.templates = []
+ # Refs GitHub issue #910
+ # Hold a series of seen dictionaries within Contexts. A dictionary is
+ # considered seen if it is `in` this list, requiring that the __eq__
+ # for the dictionary matches. If *anything* in the dictionary is
+ # different it is counted as a new layer.
+ self.seen_layers = []
+ # Holds all dictionaries which have been prettified for output.
+ # This should align with the seen_layers such that an index here is
+ # the same as the index there.
+ self.pformat_layers = []
def _store_template_info(self, sender, **kwargs):
template, context = kwargs['template'], kwargs['context']
# Skip templates that we are generating through the debug toolbar.
- if (isinstance(template.name, six.string_types) and
- template.name.startswith('debug_toolbar/')):
+ if (isinstance(template.name, six.string_types) and (
+ template.name.startswith('debug_toolbar/') or
+ template.name.startswith(
+ tuple(self.toolbar.config['SKIP_TEMPLATE_PREFIXES'])))):
return
context_list = []
for context_layer in context.dicts:
- temp_layer = {}
- if hasattr(context_layer, 'items'):
- for key, value in context_layer.items():
- # Replace any request elements - they have a large
- # unicode representation and the request data is
- # already made available from the Request panel.
- if isinstance(value, http.HttpRequest):
- temp_layer[key] = '<<request>>'
- # Replace the debugging sql_queries element. The SQL
- # data is already made available from the SQL panel.
- elif key == 'sql_queries' and isinstance(value, list):
- temp_layer[key] = '<<sql_queries>>'
- # Replace LANGUAGES, which is available in i18n context processor
- elif key == 'LANGUAGES' and isinstance(value, tuple):
- temp_layer[key] = '<<languages>>'
- # QuerySet would trigger the database: user can run the query from SQL Panel
- elif isinstance(value, (QuerySet, RawQuerySet)):
- model_name = "%s.%s" % (
- value.model._meta.app_label, value.model.__name__)
- temp_layer[key] = '<<%s of %s>>' % (
- value.__class__.__name__.lower(), model_name)
- else:
- try:
- recording(False)
- pformat(value) # this MAY trigger a db query
- except SQLQueryTriggered:
- temp_layer[key] = '<<triggers database query>>'
- except UnicodeEncodeError:
- temp_layer[key] = '<<unicode encode error>>'
- except Exception:
- temp_layer[key] = '<<unhandled exception>>'
+ if hasattr(context_layer, 'items') and context_layer:
+ # Refs GitHub issue #910
+ # If we can find this layer in our pseudo-cache then find the
+ # matching prettified version in the associated list.
+ key_values = sorted(context_layer.items())
+ if key_values in self.seen_layers:
+ index = self.seen_layers.index(key_values)
+ pformatted = self.pformat_layers[index]
+ context_list.append(pformatted)
+ else:
+ temp_layer = {}
+ for key, value in context_layer.items():
+ # Replace any request elements - they have a large
+ # unicode representation and the request data is
+ # already made available from the Request panel.
+ if isinstance(value, http.HttpRequest):
+ temp_layer[key] = '<<request>>'
+ # Replace the debugging sql_queries element. The SQL
+ # data is already made available from the SQL panel.
+ elif key == 'sql_queries' and isinstance(value, list):
+ temp_layer[key] = '<<sql_queries>>'
+ # Replace LANGUAGES, which is available in i18n context processor
+ elif key == 'LANGUAGES' and isinstance(value, tuple):
+ temp_layer[key] = '<<languages>>'
+ # QuerySet would trigger the database: user can run the query from SQL Panel
+ elif isinstance(value, (QuerySet, RawQuerySet)):
+ model_name = "%s.%s" % (
+ value.model._meta.app_label, value.model.__name__)
+ temp_layer[key] = '<<%s of %s>>' % (
+ value.__class__.__name__.lower(), model_name)
else:
- temp_layer[key] = value
- finally:
- recording(True)
- try:
- context_list.append(pformat(temp_layer))
- except UnicodeEncodeError:
- pass
-
- kwargs['context'] = [force_text(item) for item in context_list]
+ try:
+ recording(False)
+ force_text(value) # this MAY trigger a db query
+ except SQLQueryTriggered:
+ temp_layer[key] = '<<triggers database query>>'
+ except UnicodeEncodeError:
+ temp_layer[key] = '<<unicode encode error>>'
+ except Exception:
+ temp_layer[key] = '<<unhandled exception>>'
+ else:
+ temp_layer[key] = value
+ finally:
+ recording(True)
+ # Refs GitHub issue #910
+ # If we've not seen the layer before then we will add it
+ # so that if we see it again we can skip formatting it.
+ self.seen_layers.append(key_values)
+ # Note: this *ought* to be len(...) - 1 but let's be safe.
+ index = self.seen_layers.index(key_values)
+ try:
+ pformatted = force_text(pformat(temp_layer))
+ except UnicodeEncodeError:
+ pass
+ else:
+ # Note: this *ought* to be len(...) - 1 but let's be safe.
+ self.pformat_layers.insert(index, pformatted)
+ context_list.append(pformatted)
+
+ kwargs['context'] = context_list
kwargs['context_processors'] = getattr(context, 'context_processors', None)
self.templates.append(kwargs)
@@ -172,7 +204,10 @@ class TemplatesPanel(Panel):
# Fetch context_processors/template_dirs from any template
if self.templates:
context_processors = self.templates[0]['context_processors']
- template_dirs = self.templates[0]['template'].engine.dirs
+ template = self.templates[0]['template']
+ # django templates have the 'engine' attribute, while jinja templates use 'backend'
+ engine_backend = getattr(template, 'engine', None) or getattr(template, 'backend')
+ template_dirs = engine_backend.dirs
else:
context_processors = None
template_dirs = []
diff --git a/debug_toolbar/panels/templates/views.py b/debug_toolbar/panels/templates/views.py
index db505d8..3ab2a6d 100644
--- a/debug_toolbar/panels/templates/views.py
+++ b/debug_toolbar/panels/templates/views.py
@@ -6,15 +6,24 @@ from django.template import TemplateDoesNotExist
from django.template.engine import Engine
from django.utils.safestring import mark_safe
+from debug_toolbar.decorators import require_show_toolbar
+try:
+ from django.template import Origin
+except ImportError:
+ Origin = None
+
+
+ at require_show_toolbar
def template_source(request):
"""
Return the source of a template, syntax-highlighted by Pygments if
it's available.
"""
- template_name = request.GET.get('template', None)
- if template_name is None:
- return HttpResponseBadRequest('"template" key is required')
+ template_origin_name = request.GET.get('template_origin', None)
+ if template_origin_name is None:
+ return HttpResponseBadRequest('"template_origin" key is required')
+ template_name = request.GET.get('template', template_origin_name)
final_loaders = []
loaders = Engine.get_default().template_loaders
@@ -30,11 +39,21 @@ def template_source(request):
final_loaders.append(loader)
for loader in final_loaders:
- try:
- source, display_name = loader.load_template_source(template_name)
- break
- except TemplateDoesNotExist:
- source = "Template Does Not Exist: %s" % (template_name,)
+ if Origin: # django>=1.9
+ origin = Origin(template_origin_name)
+ try:
+ source = loader.get_contents(origin)
+ break
+ except TemplateDoesNotExist:
+ pass
+ else: # django<1.9
+ try:
+ source, _ = loader.load_template_source(template_name)
+ break
+ except TemplateDoesNotExist:
+ pass
+ else:
+ source = "Template Does Not Exist: %s" % (template_origin_name,)
try:
from pygments import highlight
diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py
index 43ede91..dd7eda0 100644
--- a/debug_toolbar/settings.py
+++ b/debug_toolbar/settings.py
@@ -15,9 +15,9 @@ from django.utils.lru_cache import lru_cache
CONFIG_DEFAULTS = {
# Toolbar options
- 'DISABLE_PANELS': set(['debug_toolbar.panels.redirects.RedirectsPanel']),
+ 'DISABLE_PANELS': {'debug_toolbar.panels.redirects.RedirectsPanel'},
'INSERT_BEFORE': '</body>',
- 'JQUERY_URL': '//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js',
+ 'JQUERY_URL': '//ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js',
'RENDER_PANELS': None,
'RESULTS_CACHE_SIZE': 10,
'ROOT_TAG_EXTRA_ATTRS': '',
@@ -35,6 +35,10 @@ CONFIG_DEFAULTS = {
),
'PROFILER_MAX_DEPTH': 10,
'SHOW_TEMPLATE_CONTEXT': True,
+ 'SKIP_TEMPLATE_PREFIXES': (
+ 'django/forms/widgets/',
+ 'admin/widgets/',
+ ),
'SQL_WARNING_THRESHOLD': 500, # milliseconds
}
diff --git a/debug_toolbar/static/debug_toolbar/css/toolbar.css b/debug_toolbar/static/debug_toolbar/css/toolbar.css
index ba929ef..6aa4f71 100644
--- a/debug_toolbar/static/debug_toolbar/css/toolbar.css
+++ b/debug_toolbar/static/debug_toolbar/css/toolbar.css
@@ -458,6 +458,7 @@
}
#djDebug .djDebugSql {
+ word-break:break-word;
z-index:100000002;
}
@@ -503,7 +504,7 @@
#djDebug .highlight .s2 { color:#333 } /* Literal.String.Double */
#djDebug .highlight .cp { color:#333 } /* Comment.Preproc */
-#djDebug .timeline {
+#djDebug .djdt-timeline {
width: 30%;
}
#djDebug .djDebugTimeline {
diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.timer.js b/debug_toolbar/static/debug_toolbar/js/toolbar.timer.js
index b9bf3b4..ba2e065 100644
--- a/debug_toolbar/static/debug_toolbar/js/toolbar.timer.js
+++ b/debug_toolbar/static/debug_toolbar/js/toolbar.timer.js
@@ -24,13 +24,13 @@
if (endStat) {
// Render a start through end bar
$row.html('<td>' + stat.replace('Start', '') + '</td>' +
- '<td class="timeline"><div class="djDebugTimeline"><div class="djDebugLineChart"><strong> </strong></div></div></td>' +
+ '<td class="djdt-timeline"><div class="djDebugTimeline"><div class="djDebugLineChart"><strong> </strong></div></div></td>' +
'<td>' + (perf.timing[stat] - timingOffset) + ' (+' + (perf.timing[endStat] - perf.timing[stat]) + ')</td>');
$row.find('strong').css({width: getCSSWidth(stat, endStat)});
} else {
// Render a point in time
$row.html('<td>' + stat + '</td>' +
- '<td class="timeline"><div class="djDebugTimeline"><div class="djDebugLineChart"><strong> </strong></div></div></td>' +
+ '<td class="djdt-timeline"><div class="djDebugTimeline"><div class="djDebugLineChart"><strong> </strong></div></div></td>' +
'<td>' + (perf.timing[stat] - timingOffset) + '</td>');
$row.find('strong').css({width: 2});
}
diff --git a/debug_toolbar/templates/debug_toolbar/panels/request.html b/debug_toolbar/templates/debug_toolbar/panels/request.html
index c697237..9cfc25f 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/request.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/request.html
@@ -24,8 +24,8 @@
<h4>{% trans "Cookies" %}</h4>
<table>
<colgroup>
- <col class="djdt-width-20"/>
- <col/>
+ <col class="djdt-width-20" />
+ <col />
</colgroup>
<thead>
<tr>
@@ -50,8 +50,8 @@
<h4>{% trans "Session data" %}</h4>
<table>
<colgroup>
- <col class="djdt-width-20"/>
- <col/>
+ <col class="djdt-width-20" />
+ <col />
</colgroup>
<thead>
<tr>
@@ -76,8 +76,8 @@
<h4>{% trans "GET data" %}</h4>
<table>
<colgroup>
- <col class="djdt-width-20"/>
- <col/>
+ <col class="djdt-width-20" />
+ <col />
</colgroup>
<thead>
<tr>
@@ -102,8 +102,8 @@
<h4>{% trans "POST data" %}</h4>
<table>
<colgroup>
- <col class="djdt-width-20"/>
- <col/>
+ <col class="djdt-width-20" />
+ <col />
</colgroup>
<tr>
<th>{% trans "Variable" %}</th>
diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql.html b/debug_toolbar/templates/debug_toolbar/panels/sql.html
index 8b40f45..792f25f 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/sql.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/sql.html
@@ -18,8 +18,8 @@
<thead>
<tr>
<th class="djdt-color"> </th>
- <th class="query" colspan="2">{% trans "Query" %}</th>
- <th class="timeline">{% trans "Timeline" %}</th>
+ <th class="djdt-query" colspan="2">{% trans "Query" %}</th>
+ <th class="djdt-timeline">{% trans "Timeline" %}</th>
<th class="djdt-time">{% trans "Time (ms)" %}</th>
<th class="djdt-actions">{% trans "Action" %}</th>
</tr>
@@ -31,7 +31,7 @@
<td class="djdt-toggle">
<a class="djToggleSwitch" data-toggle-name="sqlMain" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href="">+</a>
</td>
- <td class="query">
+ <td class="djdt-query">
<div class="djDebugSqlWrap">
<div class="djDebugSql">{{ query.sql|safe }}</div>
</div>
@@ -42,7 +42,7 @@
</strong>
{% endif %}
</td>
- <td class="timeline">
+ <td class="djdt-timeline">
<div class="djDebugTimeline"><div class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" data-left="{{ query.start_offset|unlocalize }}%"><strong data-width="{{ query.width_ratio_relative|unlocalize }}%" data-background-color="{{ query.trace_color }}">{{ query.width_ratio }}%</strong></div></div>
</td>
<td class="djdt-time">
diff --git a/debug_toolbar/templates/debug_toolbar/panels/templates.html b/debug_toolbar/templates/debug_toolbar/panels/templates.html
index 4358288..c657f0b 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/templates.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/templates.html
@@ -14,7 +14,7 @@
{% if templates %}
<dl>
{% for template in templates %}
- <dt><strong><a class="remoteCall toggleTemplate" href="{% url 'djdt:template_source' %}?template={{ template.template.name }}">{{ template.template.name|addslashes }}</a></strong></dt>
+<dt><strong><a class="remoteCall toggleTemplate" href="{% url 'djdt:template_source' %}?template={{ template.template.name }}&template_origin={{ template.template.origin_name }}">{{ template.template.name|addslashes }}</a></strong></dt>
<dd><samp>{{ template.template.origin_name|addslashes }}</samp></dd>
{% if template.context %}
<dd>
diff --git a/debug_toolbar/templates/debug_toolbar/panels/timer.html b/debug_toolbar/templates/debug_toolbar/panels/timer.html
index 2f44e88..96641bb 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/timer.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/timer.html
@@ -2,8 +2,8 @@
<h4>{% trans "Resource usage" %}</h4>
<table>
<colgroup>
- <col class="djdt-width-20"/>
- <col/>
+ <col class="djdt-width-20" />
+ <col />
</colgroup>
<thead>
<tr>
@@ -26,14 +26,14 @@
<h4>{% trans "Browser timing" %}</h4>
<table>
<colgroup>
- <col class="djdt-width-20"/>
- <col class="djdt-width-60"/>
- <col class="djdt-width-20"/>
+ <col class="djdt-width-20" />
+ <col class="djdt-width-60" />
+ <col class="djdt-width-20" />
</colgroup>
<thead>
<tr>
<th>{% trans "Timing attribute" %}</th>
- <th class="timeline">{% trans "Timeline" %}</th>
+ <th class="djdt-timeline">{% trans "Timeline" %}</th>
<th class="djdt-time">{% trans "Milliseconds since navigation start (+length)" %}</th>
</tr>
</thead>
diff --git a/debug_toolbar/templates/debug_toolbar/panels/versions.html b/debug_toolbar/templates/debug_toolbar/panels/versions.html
index b393c3f..a26e4f9 100644
--- a/debug_toolbar/templates/debug_toolbar/panels/versions.html
+++ b/debug_toolbar/templates/debug_toolbar/panels/versions.html
@@ -1,7 +1,10 @@
{% load i18n %}
<table>
- <col style="width:15%">
- <col style="width:15%">
+ <colgroup>
+ <col style="width:15%" />
+ <col style="width:15%" />
+ <col />
+ </colgroup>
<thead>
<tr>
<th>{% trans "Package" %}</th>
diff --git a/debug_toolbar/toolbar.py b/debug_toolbar/toolbar.py
index 0ba661a..9f5e52a 100644
--- a/debug_toolbar/toolbar.py
+++ b/debug_toolbar/toolbar.py
@@ -6,13 +6,13 @@ from __future__ import absolute_import, unicode_literals
import uuid
from collections import OrderedDict
-from importlib import import_module
from django.apps import apps
from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured
from django.template import TemplateSyntaxError
from django.template.loader import render_to_string
+from django.utils.module_loading import import_string
from debug_toolbar import settings as dt_settings
@@ -106,27 +106,10 @@ class DebugToolbar(object):
def get_panel_classes(cls):
if cls._panel_classes is None:
# Load panels in a temporary variable for thread safety.
- panel_classes = []
- for panel_path in dt_settings.get_panels():
- # This logic could be replaced with import_by_path in Django 1.6.
- try:
- panel_module, panel_classname = panel_path.rsplit('.', 1)
- except ValueError:
- raise ImproperlyConfigured(
- "%s isn't a debug panel module" % panel_path)
- try:
- mod = import_module(panel_module)
- except ImportError as e:
- raise ImproperlyConfigured(
- 'Error importing debug panel %s: "%s"' %
- (panel_module, e))
- try:
- panel_class = getattr(mod, panel_classname)
- except AttributeError:
- raise ImproperlyConfigured(
- 'Toolbar Panel module "%s" does not define a "%s" class' %
- (panel_module, panel_classname))
- panel_classes.append(panel_class)
+ panel_classes = [
+ import_string(panel_path)
+ for panel_path in dt_settings.get_panels()
... 696 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-debug-toolbar.git
More information about the Python-modules-commits
mailing list