[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