Commit 6b5113ec authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Made debug views not crash when there isn't a default template engine.

parent 79deb6a0
Loading
Loading
Loading
Loading
+34 −19
Original line number Diff line number Diff line
@@ -7,12 +7,12 @@ import sys
import types

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import resolve, Resolver404
from django.http import (HttpResponse, HttpResponseNotFound, HttpRequest,
    build_request_repr)
from django.template import Template, Context, TemplateDoesNotExist
from django.template import Context, Engine, TemplateDoesNotExist
from django.template.defaultfilters import force_escape, pprint
from django.template.engine import Engine
from django.utils.datastructures import MultiValueDict
from django.utils.html import escape
from django.utils.encoding import force_bytes, smart_text
@@ -21,6 +21,11 @@ from django.utils.module_loading import import_string
from django.utils import six
from django.utils.translation import ugettext as _


# Minimal Django templates engine to render the error templates
# regardless of the project's TEMPLATES setting.
DEBUG_ENGINE = Engine(debug=True)

HIDDEN_SETTINGS = re.compile('API|TOKEN|KEY|SECRET|PASS|SIGNATURE')

CLEANSED_SUBSTITUTE = '********************'
@@ -275,19 +280,27 @@ class ExceptionReporter(object):

    def get_traceback_data(self):
        """Return a dictionary containing traceback information."""
        try:
            default_template_engine = Engine.get_default()
        except ImproperlyConfigured:
            default_template_engine = None

        # TODO: handle multiple template engines.
        template_engine = Engine.get_default()

        # TODO: add support for multiple template engines (#24120).
        # TemplateDoesNotExist should carry all the information.
        # Replaying the search process isn't a good design.
        if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
            if default_template_engine is None:
                template_loaders = []
            else:
                self.template_does_not_exist = True
                self.loader_debug_info = []
                # If Django fails in get_template_loaders, provide an empty list
                # for the following loop to not fail.
                try:
                template_loaders = template_engine.template_loaders
                    template_loaders = default_template_engine.template_loaders
                except Exception:
                    template_loaders = []

            for loader in template_loaders:
                try:
                    source_list_func = loader.get_template_sources
@@ -304,8 +317,11 @@ class ExceptionReporter(object):
                    'loader': loader_name,
                    'templates': template_list,
                })
        if (template_engine.debug and
                hasattr(self.exc_value, 'django_template_source')):

        # TODO: add support for multiple template engines (#24119).
        if (default_template_engine is not None
                and default_template_engine.debug
                and hasattr(self.exc_value, 'django_template_source')):
            self.get_template_exception_info()

        frames = self.get_traceback_frames()
@@ -362,13 +378,13 @@ class ExceptionReporter(object):

    def get_traceback_html(self):
        "Return HTML version of debug 500 HTTP error page."
        t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
        t = DEBUG_ENGINE.from_string(TECHNICAL_500_TEMPLATE)
        c = Context(self.get_traceback_data(), use_l10n=False)
        return t.render(c)

    def get_traceback_text(self):
        "Return plain text version of debug 500 HTTP error page."
        t = Template(TECHNICAL_500_TEXT_TEMPLATE, name='Technical 500 template')
        t = DEBUG_ENGINE.from_string(TECHNICAL_500_TEXT_TEMPLATE)
        c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
        return t.render(c)

@@ -545,7 +561,7 @@ def technical_404_response(request, exception):
            module = obj.__module__
            caller = '%s.%s' % (module, caller)

    t = Template(TECHNICAL_404_TEMPLATE, name='Technical 404 template')
    t = DEBUG_ENGINE.from_string(TECHNICAL_404_TEMPLATE)
    c = Context({
        'urlconf': urlconf,
        'root_urlconf': settings.ROOT_URLCONF,
@@ -561,8 +577,7 @@ def technical_404_response(request, exception):

def default_urlconf(request):
    "Create an empty URLconf 404 error response."
    t = Template(DEFAULT_URLCONF_TEMPLATE, name='Default URLconf template')

    t = DEBUG_ENGINE.from_string(DEFAULT_URLCONF_TEMPLATE)
    c = Context({
        "title": _("Welcome to Django"),
        "heading": _("It worked!"),
+30 −0
Original line number Diff line number Diff line
@@ -219,6 +219,36 @@ class DebugViewTests(TestCase):
        )


@override_settings(
    DEBUG=True,
    ROOT_URLCONF="view_tests.urls",
    # No template directories are configured, so no templates will be found.
    TEMPLATES=[{
        'BACKEND': 'django.template.backends.dummy.TemplateStrings',
    }],
)
class NonDjangoTemplatesDebugViewTests(TestCase):

    def test_400(self):
        # Ensure that when DEBUG=True, technical_500_template() is called.
        response = self.client.get('/raises400/')
        self.assertContains(response, '<div class="context" id="', status_code=400)

    def test_403(self):
        response = self.client.get('/raises403/')
        self.assertContains(response, '<h1>403 Forbidden</h1>', status_code=403)

    def test_404(self):
        response = self.client.get('/raises404/')
        self.assertEqual(response.status_code, 404)

    def test_template_not_found_error(self):
        # Raises a TemplateDoesNotExist exception and shows the debug view.
        url = reverse('raises_template_does_not_exist', kwargs={"path": "notfound.html"})
        response = self.client.get(url)
        self.assertContains(response, '<div class="context" id="', status_code=500)


class ExceptionReporterTests(TestCase):
    rf = RequestFactory()