[Python-modules-commits] [python-django-debug-toolbar] 02/08: New upstream version 1.9.1

Andrew Starr-Bochicchio asb at moszumanska.debian.org
Mon Dec 25 15:47:28 UTC 2017


This is an automated email from the git hooks/post-receive script.

asb pushed a commit to branch master
in repository python-django-debug-toolbar.

commit 6c405cb14c38548a87b28805e4d15b764737c18d
Author: Andrew Starr-Bochicchio <a.starr.b at gmail.com>
Date:   Mon Dec 25 09:11:37 2017 -0500

    New upstream version 1.9.1
---
 .travis.yml                                        | 13 +++---
 Makefile                                           |  6 +--
 README.rst                                         |  2 +-
 debug_toolbar/panels/profiling.py                  | 33 +++++++-------
 debug_toolbar/panels/redirects.py                  |  6 ++-
 debug_toolbar/panels/request.py                    |  2 +-
 debug_toolbar/panels/sql/views.py                  | 14 +++---
 debug_toolbar/panels/templates/panel.py            |  3 ++
 debug_toolbar/panels/templates/views.py            | 11 +++--
 debug_toolbar/panels/versions.py                   | 29 +++++++-----
 debug_toolbar/static/debug_toolbar/js/toolbar.js   |  7 ++-
 debug_toolbar/templates/debug_toolbar/base.html    |  5 ++-
 .../templates/debug_toolbar/panels/templates.html  |  2 +-
 docs/changes.rst                                   | 27 +++++++++++
 docs/conf.py                                       |  4 +-
 docs/configuration.rst                             | 13 ++++--
 requirements_dev.txt                               |  3 +-
 setup.cfg                                          |  5 ++-
 setup.py                                           |  3 +-
 tests/base.py                                      | 14 ++++++
 tests/panels/test_cache.py                         |  1 +
 tests/panels/test_logging.py                       |  1 +
 tests/panels/test_profiling.py                     | 31 ++++++++++++-
 tests/panels/test_redirects.py                     |  2 -
 tests/panels/test_request.py                       |  1 +
 tests/panels/test_sql.py                           |  1 +
 tests/panels/test_staticfiles.py                   |  1 +
 tests/panels/test_template.py                      |  1 +
 tests/panels/test_versions.py                      | 52 ++++++++++++++++++++++
 tests/settings.py                                  |  4 +-
 tests/test_integration.py                          | 23 +++++++++-
 tests/urls.py                                      |  2 +
 tests/views.py                                     | 16 ++++++-
 tox.ini                                            | 17 +++----
 34 files changed, 271 insertions(+), 84 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index dd5722f..7c8a4c6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,14 +27,15 @@ matrix:
       env: TOXENV=py34-dj111
     - python: 3.5
       env: TOXENV=py35-dj111
+    - python: 3.5
+      env: TOXENV=py35-dj20
     - 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
+    - python: 3.6
+      env: TOXENV=py36-dj20
+    - env: TOXENV=flake8
+    - env: TOXENV=isort
+    - env: TOXENV=readme
 install:
   - pip install tox codecov
 script:
diff --git a/Makefile b/Makefile
index 3d6d015..1cb754a 100644
--- a/Makefile
+++ b/Makefile
@@ -21,17 +21,17 @@ node_modules/jshint/bin/jshint:
 
 test:
 	DJANGO_SETTINGS_MODULE=tests.settings \
-		django-admin test tests
+		django-admin test $${TEST_ARGS:-tests}
 
 test_selenium:
 	DJANGO_SELENIUM_TESTS=true DJANGO_SETTINGS_MODULE=tests.settings \
-		django-admin test tests
+		django-admin test $${TEST_ARGS:-tests}
 
 coverage:
 	python --version
 	coverage erase
 	DJANGO_SETTINGS_MODULE=tests.settings \
-		coverage run `which django-admin` test -v2 tests
+		coverage run `which django-admin` test -v2 $${TEST_ARGS:-tests}
 	coverage report
 	coverage html
 
diff --git a/README.rst b/README.rst
index 343c05c..476c8c5 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.8. It works on Django ≥ 1.8.
+The current version of the Debug Toolbar is 1.9. 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/panels/profiling.py b/debug_toolbar/panels/profiling.py
index 370a58c..e64fd1d 100644
--- a/debug_toolbar/panels/profiling.py
+++ b/debug_toolbar/panels/profiling.py
@@ -6,7 +6,7 @@ from colorsys import hsv_to_rgb
 from pstats import Stats
 
 from django.utils import six
-from django.utils.safestring import mark_safe
+from django.utils.html import format_html
 from django.utils.translation import ugettext_lazy as _
 
 from debug_toolbar import settings as dt_settings
@@ -78,15 +78,15 @@ class FunctionCall(object):
 
             file_path, file_name = file_name.rsplit(os.sep, 1)
 
-            return mark_safe(
+            return format_html(
                 '<span class="djdt-path">{0}/</span>'
                 '<span class="djdt-file">{1}</span>'
                 ' in <span class="djdt-func">{3}</span>'
-                '(<span class="djdt-lineno">{2}</span>)'.format(
-                    file_path,
-                    file_name,
-                    line_num,
-                    method))
+                '(<span class="djdt-lineno">{2}</span>)',
+                file_path,
+                file_name,
+                line_num,
+                method)
 
     def subfuncs(self):
         i = 0
@@ -167,12 +167,13 @@ class ProfilingPanel(Panel):
         self.stats = DjangoDebugToolbarStats(self.profiler)
         self.stats.calc_callees()
 
-        root = FunctionCall(self.stats, self.stats.get_root_func(), depth=0)
-
-        func_list = []
-        self.add_node(func_list,
-                      root,
-                      dt_settings.get_config()['PROFILER_MAX_DEPTH'],
-                      root.stats[3] / 8)
-
-        self.record_stats({'func_list': func_list})
+        root_func = self.stats.get_root_func()
+        # Ensure root function exists before continuing with function call analysis
+        if root_func:
+            root = FunctionCall(self.stats, root_func, depth=0)
+            func_list = []
+            self.add_node(func_list,
+                          root,
+                          dt_settings.get_config()['PROFILER_MAX_DEPTH'],
+                          root.stats[3] / 8)
+            self.record_stats({'func_list': func_list})
diff --git a/debug_toolbar/panels/redirects.py b/debug_toolbar/panels/redirects.py
index 14dc7ac..01a6016 100644
--- a/debug_toolbar/panels/redirects.py
+++ b/debug_toolbar/panels/redirects.py
@@ -1,6 +1,6 @@
 from __future__ import absolute_import, unicode_literals
 
-from django.shortcuts import render_to_response
+from django.template.response import SimpleTemplateResponse
 from django.utils.translation import ugettext_lazy as _
 
 from debug_toolbar.panels import Panel
@@ -22,6 +22,8 @@ class RedirectsPanel(Panel):
                 status_line = '%s %s' % (response.status_code, response.reason_phrase)
                 cookies = response.cookies
                 context = {'redirect_to': redirect_to, 'status_line': status_line}
-                response = render_to_response('debug_toolbar/redirect.html', context)
+                # Using SimpleTemplateResponse avoids running global context processors.
+                response = SimpleTemplateResponse('debug_toolbar/redirect.html', context)
                 response.cookies = cookies
+                response.render()
         return response
diff --git a/debug_toolbar/panels/request.py b/debug_toolbar/panels/request.py
index ae5b2a3..bf202f5 100644
--- a/debug_toolbar/panels/request.py
+++ b/debug_toolbar/panels/request.py
@@ -9,7 +9,7 @@ from debug_toolbar.utils import get_name_from_obj
 
 try:
     from django.urls import resolve
-except:  # Django < 1.10 pragma: no cover
+except ImportError:  # Django < 1.10 pragma: no cover
     from django.core.urlresolvers import resolve
 
 
diff --git a/debug_toolbar/panels/sql/views.py b/debug_toolbar/panels/sql/views.py
index 0d6b4e3..47fca42 100644
--- a/debug_toolbar/panels/sql/views.py
+++ b/debug_toolbar/panels/sql/views.py
@@ -1,7 +1,7 @@
 from __future__ import absolute_import, unicode_literals
 
 from django.http import HttpResponseBadRequest
-from django.shortcuts import render_to_response
+from django.template.response import SimpleTemplateResponse
 from django.views.decorators.csrf import csrf_exempt
 
 from debug_toolbar.decorators import require_show_toolbar
@@ -29,8 +29,8 @@ def sql_select(request):
             'headers': headers,
             'alias': form.cleaned_data['alias'],
         }
-        # Using render_to_response avoids running global context processors.
-        return render_to_response('debug_toolbar/panels/sql_select.html', context)
+        # Using SimpleTemplateResponse avoids running global context processors.
+        return SimpleTemplateResponse('debug_toolbar/panels/sql_select.html', context)
     return HttpResponseBadRequest('Form errors')
 
 
@@ -66,8 +66,8 @@ def sql_explain(request):
             'headers': headers,
             'alias': form.cleaned_data['alias'],
         }
-        # Using render_to_response avoids running global context processors.
-        return render_to_response('debug_toolbar/panels/sql_explain.html', context)
+        # Using SimpleTemplateResponse avoids running global context processors.
+        return SimpleTemplateResponse('debug_toolbar/panels/sql_explain.html', context)
     return HttpResponseBadRequest('Form errors')
 
 
@@ -113,6 +113,6 @@ def sql_profile(request):
             'headers': headers,
             'alias': form.cleaned_data['alias'],
         }
-        # Using render_to_response avoids running global context processors.
-        return render_to_response('debug_toolbar/panels/sql_profile.html', context)
+        # Using SimpleTemplateResponse avoids running global context processors.
+        return SimpleTemplateResponse('debug_toolbar/panels/sql_profile.html', context)
     return HttpResponseBadRequest('Form errors')
diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py
index 7367cc1..ace5a6b 100644
--- a/debug_toolbar/panels/templates/panel.py
+++ b/debug_toolbar/panels/templates/panel.py
@@ -7,6 +7,7 @@ from pprint import pformat
 
 from django import http
 from django.conf.urls import url
+from django.core import signing
 from django.db.models.query import QuerySet, RawQuerySet
 from django.template import RequestContext, Template
 from django.test.signals import template_rendered
@@ -192,8 +193,10 @@ class TemplatesPanel(Panel):
             template = template_data.get('template', None)
             if hasattr(template, 'origin') and template.origin and template.origin.name:
                 template.origin_name = template.origin.name
+                template.origin_hash = signing.dumps(template.origin.name)
             else:
                 template.origin_name = _('No origin')
+                template.origin_hash = ''
             info['template'] = template
             # Clean up context for better readability
             if self.toolbar.config['SHOW_TEMPLATE_CONTEXT']:
diff --git a/debug_toolbar/panels/templates/views.py b/debug_toolbar/panels/templates/views.py
index 3ab2a6d..b458f17 100644
--- a/debug_toolbar/panels/templates/views.py
+++ b/debug_toolbar/panels/templates/views.py
@@ -1,9 +1,10 @@
 from __future__ import absolute_import, unicode_literals
 
+from django.core import signing
 from django.http import HttpResponseBadRequest
-from django.shortcuts import render_to_response
 from django.template import TemplateDoesNotExist
 from django.template.engine import Engine
+from django.template.response import SimpleTemplateResponse
 from django.utils.safestring import mark_safe
 
 from debug_toolbar.decorators import require_show_toolbar
@@ -23,6 +24,10 @@ def template_source(request):
     template_origin_name = request.GET.get('template_origin', None)
     if template_origin_name is None:
         return HttpResponseBadRequest('"template_origin" key is required')
+    try:
+        template_origin_name = signing.loads(template_origin_name)
+    except Exception:
+        return HttpResponseBadRequest('"template_origin" is invalid')
     template_name = request.GET.get('template', template_origin_name)
 
     final_loaders = []
@@ -66,8 +71,8 @@ def template_source(request):
     except ImportError:
         pass
 
-    # Using render_to_response avoids running global context processors.
-    return render_to_response('debug_toolbar/panels/template_source.html', {
+    # Using SimpleTemplateResponse avoids running global context processors.
+    return SimpleTemplateResponse('debug_toolbar/panels/template_source.html', {
         'source': source,
         'template_name': template_name
     })
diff --git a/debug_toolbar/panels/versions.py b/debug_toolbar/panels/versions.py
index 26650d7..2e8d588 100644
--- a/debug_toolbar/panels/versions.py
+++ b/debug_toolbar/panels/versions.py
@@ -41,18 +41,25 @@ class VersionsPanel(Panel):
                 yield app.__name__, name, version
 
     def get_app_version(self, app):
+        version = self.get_version_from_app(app)
+        if isinstance(version, (list, tuple)):
+            # We strip dots from the right because we do not want to show
+            # trailing dots if there are empty elements in the list/tuple
+            version = '.'.join(str(o) for o in version).rstrip('.')
+        return version
+
+    def get_version_from_app(self, app):
         if hasattr(app, 'get_version'):
             get_version = app.get_version
             if callable(get_version):
-                version = get_version()
+                try:
+                    return get_version()
+                except TypeError:
+                    pass
             else:
-                version = get_version
-        elif hasattr(app, 'VERSION'):
-            version = app.VERSION
-        elif hasattr(app, '__version__'):
-            version = app.__version__
-        else:
-            return
-        if isinstance(version, (list, tuple)):
-            version = '.'.join(str(o) for o in version)
-        return version
+                return get_version
+        if hasattr(app, 'VERSION'):
+            return app.VERSION
+        if hasattr(app, '__version__'):
+            return app.__version__
+        return
diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.js b/debug_toolbar/static/debug_toolbar/js/toolbar.js
index 5fbdaf1..7fddbb2 100644
--- a/debug_toolbar/static/debug_toolbar/js/toolbar.js
+++ b/debug_toolbar/static/debug_toolbar/js/toolbar.js
@@ -19,16 +19,15 @@
                 } else {
                     $('.djdt-panelContent').hide(); // Hide any that are already open
                     var inner = current.find('.djDebugPanelContent .djdt-scroll'),
-                        store_id = $('#djDebug').data('store-id'),
-                        render_panel_url = $('#djDebug').data('render-panel-url');
-                    if (store_id !== '' && inner.children().length === 0) {
+                        store_id = $('#djDebug').data('store-id');
+                    if (store_id && inner.children().length === 0) {
                         var ajax_data = {
                             data: {
                                 store_id: store_id,
                                 panel_id: this.className
                             },
                             type: 'GET',
-                            url: render_panel_url
+                            url: $('#djDebug').data('render-panel-url')
                         };
                         $.ajax(ajax_data).done(function(data){
                             inner.prev().remove();  // Remove AJAX loader
diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html
index deaae80..e5df55c 100644
--- a/debug_toolbar/templates/debug_toolbar/base.html
+++ b/debug_toolbar/templates/debug_toolbar/base.html
@@ -11,7 +11,10 @@
 {% endif %}
 <script src="{% static 'debug_toolbar/js/toolbar.js' %}"></script>
 <div id="djDebug" class="djdt-hidden" dir="ltr"
-     data-store-id="{{ toolbar.store_id }}" data-render-panel-url="{% url 'djdt:render_panel' %}"
+     {% if toolbar.store_id %}
+     data-store-id="{{ toolbar.store_id }}"
+     data-render-panel-url="{% url 'djdt:render_panel' %}"
+     {% endif %}
      {{ toolbar.config.ROOT_TAG_EXTRA_ATTRS|safe }}>
 	<div class="djdt-hidden" id="djDebugToolbar">
 		<ul id="djDebugPanelList">
diff --git a/debug_toolbar/templates/debug_toolbar/panels/templates.html b/debug_toolbar/templates/debug_toolbar/panels/templates.html
index c657f0b..0440dc3 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_origin={{ template.template.origin_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_hash }}">{{ template.template.name|addslashes }}</a></strong></dt>
 	<dd><samp>{{ template.template.origin_name|addslashes }}</samp></dd>
 	{% if template.context %}
 	<dd>
diff --git a/docs/changes.rst b/docs/changes.rst
index e1c6fdf..c8e0cbe 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -1,12 +1,39 @@
 Change log
 ==========
 
+UNRELEASED
+----------
+
+* Fix erroneous ``ContentNotRenderedError`` raised by the redirects panel.
+
+1.9
+---
+
+This version is compatible with Django 2.0 and requires Django 1.8 or
+later.
+
+Bugfixes
+~~~~~~~~
+
+* The profiling panel now escapes reported data resulting in valid HTML.
+* Many minor cleanups and bugfixes.
+
 1.8
 ---
 
 This version is compatible with Django 1.11 and requires Django 1.8 or
 later.
 
+**Backwards incompatible changes**
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* ``debug_toolbar.middleware.show_toolbar`` (the default value of setting
+  ``SHOW_TOOLBAR_CALLBACK``) no longer returns ``False`` for AJAX requests.
+  This is to allow reusing the ``SHOW_TOOLBAR_CALLBACK`` function to verify
+  access to panel views requested via AJAX. Projects defining a custom
+  ``SHOW_TOOLBAR_CALLBACK`` should remove checks for AJAX requests in order to
+  continue to allow access to these panels.
+
 Features
 ~~~~~~~~
 
diff --git a/docs/conf.py b/docs/conf.py
index 90e406b..c567460 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -59,9 +59,9 @@ copyright = copyright.format(datetime.date.today().year)
 # built documents.
 #
 # The short X.Y version.
-version = '1.8'
+version = '1.9'
 # The full version, including alpha/beta/rc tags.
-release = '1.8'
+release = '1.9'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/docs/configuration.rst b/docs/configuration.rst
index babc6ec..3be92b9 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -112,10 +112,15 @@ Toolbar options
   Default: 'debug_toolbar.middleware.show_toolbar'
 
   This is the dotted path to a function used for determining whether the
-  toolbar should show or not. The default checks are that ``DEBUG`` must be
-  set to ``True``, the IP of the request must be in ``INTERNAL_IPS``, and the
-  request must not be an AJAX request. You can provide your own function
-  ``callback(request)`` which returns ``True`` or ``False``.
+  toolbar should show or not. The default checks are that ``DEBUG`` must be set
+  to ``True`` and the IP of the request must be in ``INTERNAL_IPS``. You can
+  provide your own function ``callback(request)`` which returns ``True`` or
+  ``False``.
+
+  For versions < 1.8, the callback should also return ``False`` for AJAX
+  requests. Since version 1.8, AJAX requests are checked in the middleware, not
+  the callback. This allows reusing the callback to verify access to panel
+  views requested via AJAX.
 
 Panel options
 ~~~~~~~~~~~~~
diff --git a/requirements_dev.txt b/requirements_dev.txt
index 200ab13..7b3c6e6 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -11,8 +11,9 @@ django_jinja
 # Testing
 
 coverage
-isort
 flake8
+html5lib
+isort
 selenium
 tox
 
diff --git a/setup.cfg b/setup.cfg
index 7ba8b74..aeef374 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -13,5 +13,8 @@ known_first_party = debug_toolbar
 multi_line_output = 5
 not_skip = __init__.py
 
-[wheel]
+[bdist_wheel]
 universal = 1
+
+[metadata]
+license-file = LICENSE
diff --git a/setup.py b/setup.py
index 05e3f15..255d732 100755
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ from setuptools import find_packages, setup
 
 setup(
     name='django-debug-toolbar',
-    version='1.8',
+    version='1.9.1',
     description='A configurable set of panels that display various debug '
                 'information about the current request/response.',
     long_description=open('README.rst', encoding='utf-8').read(),
@@ -30,6 +30,7 @@ setup(
         'Framework :: Django :: 1.9',
         'Framework :: Django :: 1.10',
         'Framework :: Django :: 1.11',
+        'Framework :: Django :: 2.0',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: BSD License',
         'Operating System :: OS Independent',
diff --git a/tests/base.py b/tests/base.py
index 2518d8c..a7b57e5 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
 
 import threading
 
+import html5lib
 from django.http import HttpResponse
 from django.test import RequestFactory, TestCase
 
@@ -24,3 +25,16 @@ class BaseTestCase(TestCase):
         self.response = response
         self.toolbar = toolbar
         self.toolbar.stats = {}
+
+    def assertValidHTML(self, content, msg=None):
+        parser = html5lib.HTMLParser()
+        parser.parseFragment(self.panel.content)
+        if parser.errors:
+            default_msg = ['Content is invalid HTML:']
+            lines = content.split('\n')
+            for position, errorcode, datavars in parser.errors:
+                default_msg.append('  %s' % html5lib.constants.E[errorcode] % datavars)
+                default_msg.append('    %s' % lines[position[0] - 1])
+
+            msg = self._formatMessage(msg, '\n'.join(default_msg))
+            raise self.failureException(msg)
diff --git a/tests/panels/test_cache.py b/tests/panels/test_cache.py
index 940b5a5..e6f3fb1 100644
--- a/tests/panels/test_cache.py
+++ b/tests/panels/test_cache.py
@@ -47,3 +47,4 @@ class CachePanelTestCase(BaseTestCase):
         self.panel.generate_stats(self.request, self.response)
         # ensure the panel renders correctly.
         self.assertIn('café', self.panel.content)
+        self.assertValidHTML(self.panel.content)
diff --git a/tests/panels/test_logging.py b/tests/panels/test_logging.py
index 3168668..cb5a631 100644
--- a/tests/panels/test_logging.py
+++ b/tests/panels/test_logging.py
@@ -57,6 +57,7 @@ class LoggingPanelTestCase(BaseTestCase):
         self.panel.generate_stats(self.request, self.response)
         # ensure the panel renders correctly.
         self.assertIn('café', self.panel.content)
+        self.assertValidHTML(self.panel.content)
 
     def test_failing_formatting(self):
         class BadClass(object):
diff --git a/tests/panels/test_profiling.py b/tests/panels/test_profiling.py
index 0b57bf1..7e0ef25 100644
--- a/tests/panels/test_profiling.py
+++ b/tests/panels/test_profiling.py
@@ -1,12 +1,15 @@
 from __future__ import absolute_import, unicode_literals
 
+import unittest
+
 from django.contrib.auth.models import User
 from django.db import IntegrityError, transaction
 from django.test import TestCase
 from django.test.utils import override_settings
+from django.utils import six
 
 from ..base import BaseTestCase
-from ..views import regular_view
+from ..views import listcomp_view, regular_view
 
 
 @override_settings(DEBUG_TOOLBAR_PANELS=['debug_toolbar.panels.profiling.ProfilingPanel'])
@@ -35,6 +38,32 @@ class ProfilingPanelTestCase(BaseTestCase):
         self.panel.generate_stats(self.request, self.response)
         # ensure the panel renders correctly.
         self.assertIn('regular_view', self.panel.content)
+        self.assertValidHTML(self.panel.content)
+
+    @unittest.skipIf(six.PY2, 'list comprehension not listed on Python 2')
+    def test_listcomp_escaped(self):
+        self.panel.process_view(self.request, listcomp_view, (), {})
+        self.panel.generate_stats(self.request, self.response)
+        self.assertNotIn('<span class="djdt-func"><listcomp></span>', self.panel.content)
+        self.assertIn('<span class="djdt-func"><listcomp></span>', self.panel.content)
+
+    def test_generate_stats_no_profiler(self):
+        """
+        Test generating stats with no profiler.
+        """
+        self.assertIsNone(self.panel.generate_stats(self.request, self.response))
+
+    def test_generate_stats_no_root_func(self):
+        """
+        Test generating stats using profiler without root function.
+        """
+        self.panel.process_view(self.request, regular_view, ('profiling',), {})
+        self.panel.process_response(self.request, self.response)
+        self.panel.profiler.clear()
+        self.panel.profiler.enable()
+        self.panel.profiler.disable()
+        self.panel.generate_stats(self.request, self.response)
+        self.assertNotIn('func_list', self.panel.get_stats())
 
 
 @override_settings(DEBUG=True,
diff --git a/tests/panels/test_redirects.py b/tests/panels/test_redirects.py
index d6fe5a3..980130e 100644
--- a/tests/panels/test_redirects.py
+++ b/tests/panels/test_redirects.py
@@ -4,12 +4,10 @@ import copy
 
 from django.conf import settings
 from django.http import HttpResponse
-from django.test.utils import override_settings
 
 from ..base import BaseTestCase
 
 
- at override_settings(DEBUG_TOOLBAR_CONFIG={'INTERCEPT_REDIRECTS': True})
 class RedirectsPanelTestCase(BaseTestCase):
 
     def setUp(self):
diff --git a/tests/panels/test_request.py b/tests/panels/test_request.py
index 452086f..17119b6 100644
--- a/tests/panels/test_request.py
+++ b/tests/panels/test_request.py
@@ -46,3 +46,4 @@ class RequestPanelTestCase(BaseTestCase):
         self.panel.generate_stats(self.request, self.response)
         # ensure the panel renders correctly.
         self.assertIn('nôt åscíì', self.panel.content)
+        self.assertValidHTML(self.panel.content)
diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py
index 827758b..886c8f2 100644
--- a/tests/panels/test_sql.py
+++ b/tests/panels/test_sql.py
@@ -81,6 +81,7 @@ class SQLPanelTestCase(BaseTestCase):
         self.panel.generate_stats(self.request, self.response)
         # ensure the panel renders correctly.
         self.assertIn('café', self.panel.content)
+        self.assertValidHTML(self.panel.content)
 
     @unittest.skipUnless(connection.vendor == 'postgresql',
                          'Test valid only on PostgreSQL')
diff --git a/tests/panels/test_staticfiles.py b/tests/panels/test_staticfiles.py
index 4002087..3773af6 100644
--- a/tests/panels/test_staticfiles.py
+++ b/tests/panels/test_staticfiles.py
@@ -42,3 +42,4 @@ class StaticFilesPanelTestCase(BaseTestCase):
         # ensure the panel renders correctly.
         self.assertIn('django.contrib.staticfiles.finders.'
                       'AppDirectoriesFinder', self.panel.content)
+        self.assertValidHTML(self.panel.content)
diff --git a/tests/panels/test_template.py b/tests/panels/test_template.py
index 1d5a24b..ea80763 100644
--- a/tests/panels/test_template.py
+++ b/tests/panels/test_template.py
@@ -64,6 +64,7 @@ class TemplatesPanelTestCase(BaseTestCase):
         self.panel.generate_stats(self.request, self.response)
         # ensure the panel renders correctly.
         self.assertIn('nôt åscíì', self.panel.content)
+        self.assertValidHTML(self.panel.content)
 
     def test_custom_context_processor(self):
         self.panel.process_request(self.request)
diff --git a/tests/panels/test_versions.py b/tests/panels/test_versions.py
new file mode 100644
index 0000000..d6541a9
--- /dev/null
+++ b/tests/panels/test_versions.py
@@ -0,0 +1,52 @@
+# coding: utf-8
+
+from __future__ import absolute_import, unicode_literals
+
+from collections import namedtuple
+
+from ..base import BaseTestCase
+
+version_info_t = namedtuple('version_info_t', (
+    'major', 'minor', 'micro', 'releaselevel', 'serial',
+))
+
+
+class VersionsPanelTestCase(BaseTestCase):
+
+    def setUp(self):
+        super(VersionsPanelTestCase, self).setUp()
+        self.panel = self.toolbar.get_panel_by_id('VersionsPanel')
+
+    def test_app_version_from_get_version_fn(self):
+
+        class FakeApp:
+            def get_version(self):
+                return version_info_t(1, 2, 3, '', '')
+
+        self.assertEqual(self.panel.get_app_version(FakeApp()), '1.2.3')
+
+    def test_incompatible_app_version_fn(self):
+
+        class FakeApp:
+
+            def get_version(self, some_other_arg):
+                # This should be ignored by the get_version_from_app
+                return version_info_t(0, 0, 0, '', '')
+
+            VERSION = version_info_t(1, 2, 3, '', '')
+
+        self.assertEqual(self.panel.get_app_version(FakeApp()), '1.2.3')
+
+    def test_app_version_from_VERSION(self):
+
+        class FakeApp:
+            VERSION = version_info_t(1, 2, 3, '', '')
+
+        self.assertEqual(self.panel.get_app_version(FakeApp()), '1.2.3')
+
+    def test_app_version_from_underscore_version(self):
+
+        class FakeApp:
+            __version__ = version_info_t(1, 2, 3, '', '')
+
+        self.assertEqual(self.panel.get_app_version(FakeApp()), '1.2.3')
diff --git a/tests/settings.py b/tests/settings.py
index a9de290..6ea78f3 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -30,7 +30,7 @@ INSTALLED_APPS = [
 
 MEDIA_URL = '/media/'   # Avoids https://code.djangoproject.com/ticket/21451
 
-MIDDLEWARE_CLASSES = [
+MIDDLEWARE = [
     'debug_toolbar.middleware.DebugToolbarMiddleware',
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
@@ -40,6 +40,8 @@ MIDDLEWARE_CLASSES = [
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
 ]
+# Django < 1.10
+MIDDLEWARE_CLASSES = MIDDLEWARE
 
 ROOT_URLCONF = 'tests.urls'
 
diff --git a/tests/test_integration.py b/tests/test_integration.py
index d9b8743..b177bec 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -8,6 +8,7 @@ from xml.etree import ElementTree as ET
 
 import django
 from django.contrib.staticfiles.testing import StaticLiveServerTestCase
+from django.core import signing
 from django.core.checks import Error, run_checks
 from django.template.loader import get_template
 from django.test import RequestFactory, TestCase
@@ -143,7 +144,7 @@ class DebugToolbarIntegrationTestCase(TestCase):
         url = '/__debug__/template_source/'
         data = {
             'template': template.template.name,
-            'template_origin': template.template.origin.name
+            'template_origin': signing.dumps(template.template.origin.name)
         }
 
         response = self.client.get(url, data)
@@ -219,6 +220,24 @@ class DebugToolbarIntegrationTestCase(TestCase):
             response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
             self.assertEqual(response.status_code, 404)
 
+    @override_settings(DEBUG_TOOLBAR_CONFIG={'RENDER_PANELS': True})
+    def test_data_store_id_not_rendered_when_none(self):
+        url = '/regular/basic/'
+        response = self.client.get(url)
+        self.assertIn(b'id="djDebug"', response.content)
+        self.assertNotIn(b'data-store-id', response.content)
+
+    def test_view_returns_template_response(self):
+        response = self.client.get('/template_response/basic/')
+        self.assertEqual(response.status_code, 200)
+
+    @override_settings(DEBUG_TOOLBAR_CONFIG={'DISABLE_PANELS': set()})
+    def test_incercept_redirects(self):
+        response = self.client.get('/redirect/')
+        self.assertEqual(response.status_code, 200)
+        # Link to LOCATION header.
+        self.assertIn(b'href="/regular/redirect/"', response.content)
+
 
 @unittest.skipIf(webdriver is None, "selenium isn't installed")
 @unittest.skipUnless('DJANGO_SELENIUM_TESTS' in os.environ, "selenium tests not requested")
@@ -368,7 +387,7 @@ class DebugToolbarSystemChecksTestCase(BaseTestCase):
     def test_middleware_factory_functions_supported(self):
         messages = run_checks()
 
-        if django.VERSION[:2] < (1, 10):
+        if django.VERSION[:2] < (1, 10) or django.VERSION[:2] >= (2, 0):
             self.assertEqual(messages, [])
         else:
             self.assertEqual(messages[0].id, '1_10.W001')
diff --git a/tests/urls.py b/tests/urls.py
index 4c377c9..9cee7e5 100644
--- a/tests/urls.py
+++ b/tests/urls.py
@@ -14,10 +14,12 @@ urlpatterns = [
     url(r'^resolving2/(?P<arg1>.+)/(?P<arg2>.+)/$', views.resolving_view),
     url(r'^resolving3/(.+)/$', views.resolving_view, {'arg2': 'default'}),
     url(r'^regular/(?P<title>.*)/$', views.regular_view),
+    url(r'^template_response/(?P<title>.*)/$', views.template_response_view),
     url(r'^regular_jinja/(?P<title>.*)/$', views.regular_jinjia_view),
     url(r'^non_ascii_request/$', views.regular_view, {'title': NonAsciiRepr()}),
     url(r'^new_user/$', views.new_user),
     url(r'^execute_sql/$', views.execute_sql),
     url(r'^cached_view/$', views.cached_view),
+    url(r'^redirect/$', views.redirect_view),
     url(r'^__debug__/', include(debug_toolbar.urls)),
 ]
diff --git a/tests/views.py b/tests/views.py
index bc2ebce..1811394 100644
--- a/tests/views.py
+++ b/tests/views.py
@@ -3,8 +3,9 @@
 from __future__ import absolute_import, unicode_literals
 
 from django.contrib.auth.models import User
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import render
+from django.template.response import TemplateResponse
 from django.views.decorators.cache import cache_page
 
 
@@ -17,6 +18,10 @@ def regular_view(request, title):
     return render(request, 'basic.html', {'title': title})
 
 
+def template_response_view(request, title):
+    return TemplateResponse(request, 'basic.html', {'title': title})
+
+
 def new_user(request, username='joe'):
     User.objects.create_user(username=username)
     return render(request, 'basic.html', {'title': 'new user'})
@@ -34,3 +39,12 @@ def cached_view(request):
 
 def regular_jinjia_view(request, title):
     return render(request, 'jinja2/basic.jinja', {'title': title})
+
+
+def listcomp_view(request):
+    lst = [i for i in range(50000) if i % 2 == 0]
+    return render(request, 'basic.html', {'title': 'List comprehension', 'lst': lst})
+
+
+def redirect_view(request):
+    return HttpResponseRedirect('/regular/redirect/')
diff --git a/tox.ini b/tox.ini
index d24a67d..4fce4a6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,48 +3,41 @@ envlist =
     py{27,33,34}-dj18,
     py{27,34,35}-dj{19,110,111},
     py36-dj111
+    py{35,36}-dj20
     flake8,
     isort,
     readme
 
 [testenv]
-basepython =
-    py27: python2.7
-    py33: python3.3
-    py34: python3.4
-    py35: python3.5
-    py36: python3.6
 deps =
     dj18: Django>=1.8,<1.9
     dj19: Django>=1.9,<1.10
     dj110: Django>=1.10,<1.11
     dj111: Django>=1.11,<2.0
+    dj20: Django==2.0b1
     coverage
+    django_jinja
+    html5lib
     selenium<4.0
     sqlparse
-    django_jinja
 setenv =
     PYTHONPATH = {toxinidir}
 whitelist_externals = make
 pip_pre = True
 usedevelop = true
-commands = make coverage
+commands = make coverage TEST_ARGS='{posargs:tests}'
 
 [testenv:flake8]
-basepython = python2.7
 commands = make flake8
 deps = flake8
 
 [testenv:isort]
-basepython = python2.7
 commands = make isort_check_only
 deps = isort
 
 [testenv:jshint]
-basepython = python2.7
 commands = make jshint
 
 [testenv:readme]
-basepython = python2.7
 commands = python setup.py check -r -s
 deps = readme_renderer

-- 
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