Skip to content

Commit 6b5113e

Browse files
committed
Made debug views not crash when there isn't a default template engine.
1 parent 79deb6a commit 6b5113e

File tree

2 files changed

+64
-19
lines changed

2 files changed

+64
-19
lines changed

django/views/debug.py

+34-19
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import types
88

99
from django.conf import settings
10+
from django.core.exceptions import ImproperlyConfigured
1011
from django.core.urlresolvers import resolve, Resolver404
1112
from django.http import (HttpResponse, HttpResponseNotFound, HttpRequest,
1213
build_request_repr)
13-
from django.template import Template, Context, TemplateDoesNotExist
14+
from django.template import Context, Engine, TemplateDoesNotExist
1415
from django.template.defaultfilters import force_escape, pprint
15-
from django.template.engine import Engine
1616
from django.utils.datastructures import MultiValueDict
1717
from django.utils.html import escape
1818
from django.utils.encoding import force_bytes, smart_text
@@ -21,6 +21,11 @@
2121
from django.utils import six
2222
from django.utils.translation import ugettext as _
2323

24+
25+
# Minimal Django templates engine to render the error templates
26+
# regardless of the project's TEMPLATES setting.
27+
DEBUG_ENGINE = Engine(debug=True)
28+
2429
HIDDEN_SETTINGS = re.compile('API|TOKEN|KEY|SECRET|PASS|SIGNATURE')
2530

2631
CLEANSED_SUBSTITUTE = '********************'
@@ -275,19 +280,27 @@ def format_path_status(self, path):
275280

276281
def get_traceback_data(self):
277282
"""Return a dictionary containing traceback information."""
283+
try:
284+
default_template_engine = Engine.get_default()
285+
except ImproperlyConfigured:
286+
default_template_engine = None
278287

279-
# TODO: handle multiple template engines.
280-
template_engine = Engine.get_default()
281-
288+
# TODO: add support for multiple template engines (#24120).
289+
# TemplateDoesNotExist should carry all the information.
290+
# Replaying the search process isn't a good design.
282291
if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
283-
self.template_does_not_exist = True
284-
self.loader_debug_info = []
285-
# If Django fails in get_template_loaders, provide an empty list
286-
# for the following loop to not fail.
287-
try:
288-
template_loaders = template_engine.template_loaders
289-
except Exception:
292+
if default_template_engine is None:
290293
template_loaders = []
294+
else:
295+
self.template_does_not_exist = True
296+
self.loader_debug_info = []
297+
# If Django fails in get_template_loaders, provide an empty list
298+
# for the following loop to not fail.
299+
try:
300+
template_loaders = default_template_engine.template_loaders
301+
except Exception:
302+
template_loaders = []
303+
291304
for loader in template_loaders:
292305
try:
293306
source_list_func = loader.get_template_sources
@@ -304,8 +317,11 @@ def get_traceback_data(self):
304317
'loader': loader_name,
305318
'templates': template_list,
306319
})
307-
if (template_engine.debug and
308-
hasattr(self.exc_value, 'django_template_source')):
320+
321+
# TODO: add support for multiple template engines (#24119).
322+
if (default_template_engine is not None
323+
and default_template_engine.debug
324+
and hasattr(self.exc_value, 'django_template_source')):
309325
self.get_template_exception_info()
310326

311327
frames = self.get_traceback_frames()
@@ -362,13 +378,13 @@ def get_traceback_data(self):
362378

363379
def get_traceback_html(self):
364380
"Return HTML version of debug 500 HTTP error page."
365-
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
381+
t = DEBUG_ENGINE.from_string(TECHNICAL_500_TEMPLATE)
366382
c = Context(self.get_traceback_data(), use_l10n=False)
367383
return t.render(c)
368384

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

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

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

562578
def default_urlconf(request):
563579
"Create an empty URLconf 404 error response."
564-
t = Template(DEFAULT_URLCONF_TEMPLATE, name='Default URLconf template')
565-
580+
t = DEBUG_ENGINE.from_string(DEFAULT_URLCONF_TEMPLATE)
566581
c = Context({
567582
"title": _("Welcome to Django"),
568583
"heading": _("It worked!"),

tests/view_tests/tests/test_debug.py

+30
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,36 @@ def test_regression_21530(self):
219219
)
220220

221221

222+
@override_settings(
223+
DEBUG=True,
224+
ROOT_URLCONF="view_tests.urls",
225+
# No template directories are configured, so no templates will be found.
226+
TEMPLATES=[{
227+
'BACKEND': 'django.template.backends.dummy.TemplateStrings',
228+
}],
229+
)
230+
class NonDjangoTemplatesDebugViewTests(TestCase):
231+
232+
def test_400(self):
233+
# Ensure that when DEBUG=True, technical_500_template() is called.
234+
response = self.client.get('/raises400/')
235+
self.assertContains(response, '<div class="context" id="', status_code=400)
236+
237+
def test_403(self):
238+
response = self.client.get('/raises403/')
239+
self.assertContains(response, '<h1>403 Forbidden</h1>', status_code=403)
240+
241+
def test_404(self):
242+
response = self.client.get('/raises404/')
243+
self.assertEqual(response.status_code, 404)
244+
245+
def test_template_not_found_error(self):
246+
# Raises a TemplateDoesNotExist exception and shows the debug view.
247+
url = reverse('raises_template_does_not_exist', kwargs={"path": "notfound.html"})
248+
response = self.client.get(url)
249+
self.assertContains(response, '<div class="context" id="', status_code=500)
250+
251+
222252
class ExceptionReporterTests(TestCase):
223253
rf = RequestFactory()
224254

0 commit comments

Comments
 (0)