Fixed #6412 -- More details if a template file cannot be loaded

Report more details about template files in loader postmortem.
This commit is contained in:
Mathijs de Bruin 2013-02-24 16:41:10 +01:00 committed by Aymeric Augustin
parent fd961941cc
commit 61a8de6f4f
4 changed files with 58 additions and 14 deletions

View File

@ -218,6 +218,15 @@ class ExceptionReporter(object):
self.exc_value = Exception('Deprecated String Exception: %r' % self.exc_type) self.exc_value = Exception('Deprecated String Exception: %r' % self.exc_type)
self.exc_type = type(self.exc_value) self.exc_type = type(self.exc_value)
def format_path_status(self, path):
if not os.path.exists(path):
return "File does not exist"
if not os.path.isfile(path):
return "Not a file"
if not os.access(path, os.R_OK):
return "File is not readable"
return "File exists"
def get_traceback_data(self): def get_traceback_data(self):
"Return a Context instance containing traceback information." "Return a Context instance containing traceback information."
@ -230,8 +239,10 @@ class ExceptionReporter(object):
source_list_func = loader.get_template_sources source_list_func = loader.get_template_sources
# NOTE: This assumes exc_value is the name of the template that # NOTE: This assumes exc_value is the name of the template that
# the loader attempted to load. # the loader attempted to load.
template_list = [{'name': t, 'exists': os.path.exists(t)} \ template_list = [{
for t in source_list_func(str(self.exc_value))] 'name': t,
'status': self.format_path_status(t),
} for t in source_list_func(str(self.exc_value))]
except AttributeError: except AttributeError:
template_list = [] template_list = []
loader_name = loader.__module__ + '.' + loader.__class__.__name__ loader_name = loader.__module__ + '.' + loader.__class__.__name__
@ -650,7 +661,9 @@ TECHNICAL_500_TEMPLATE = """
<ul> <ul>
{% for loader in loader_debug_info %} {% for loader in loader_debug_info %}
<li>Using loader <code>{{ loader.loader }}</code>: <li>Using loader <code>{{ loader.loader }}</code>:
<ul>{% for t in loader.templates %}<li><code>{{ t.name }}</code> (File {% if t.exists %}exists{% else %}does not exist{% endif %})</li>{% endfor %}</ul> <ul>
{% for t in loader.templates %}<li><code>{{ t.name }}</code> ({{ t.status }})</li>{% endfor %}
</ul>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@ -753,7 +766,7 @@ Installed Middleware:
{% if template_does_not_exist %}Template Loader Error: {% if template_does_not_exist %}Template Loader Error:
{% if loader_debug_info %}Django tried loading these templates, in this order: {% if loader_debug_info %}Django tried loading these templates, in this order:
{% for loader in loader_debug_info %}Using loader {{ loader.loader }}: {% for loader in loader_debug_info %}Using loader {{ loader.loader }}:
{% for t in loader.templates %}{{ t.name }} (File {% if t.exists %}exists{% else %}does not exist{% endif %}) {% for t in loader.templates %}{{ t.name }} ({{ t.status }})
{% endfor %}{% endfor %} {% endfor %}{% endfor %}
{% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty! {% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty!
{% endif %} {% endif %}
@ -943,7 +956,7 @@ Installed Middleware:
{% if template_does_not_exist %}Template loader Error: {% if template_does_not_exist %}Template loader Error:
{% if loader_debug_info %}Django tried loading these templates, in this order: {% if loader_debug_info %}Django tried loading these templates, in this order:
{% for loader in loader_debug_info %}Using loader {{ loader.loader }}: {% for loader in loader_debug_info %}Using loader {{ loader.loader }}:
{% for t in loader.templates %}{{ t.name }} (File {% if t.exists %}exists{% else %}does not exist{% endif %}) {% for t in loader.templates %}{{ t.name }} ({{ t.status }})
{% endfor %}{% endfor %} {% endfor %}{% endfor %}
{% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty! {% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty!
{% endif %} {% endif %}

View File

@ -5,8 +5,9 @@ from __future__ import absolute_import, unicode_literals
import inspect import inspect
import os import os
import shutil
import sys import sys
import tempfile from tempfile import NamedTemporaryFile, mkdtemp, mkstemp
from django.core import mail from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
@ -21,6 +22,7 @@ from .. import BrokenException, except_args
from ..views import (sensitive_view, non_sensitive_view, paranoid_view, from ..views import (sensitive_view, non_sensitive_view, paranoid_view,
custom_exception_reporter_filter_view, sensitive_method_view, custom_exception_reporter_filter_view, sensitive_method_view,
sensitive_args_function_caller, sensitive_kwargs_function_caller) sensitive_args_function_caller, sensitive_kwargs_function_caller)
from django.utils.unittest import skipIf
@override_settings(DEBUG=True, TEMPLATE_DEBUG=True) @override_settings(DEBUG=True, TEMPLATE_DEBUG=True)
@ -78,9 +80,38 @@ class DebugViewTests(TestCase):
raising_loc) raising_loc)
def test_template_loader_postmortem(self): def test_template_loader_postmortem(self):
response = self.client.get(reverse('raises_template_does_not_exist')) """Tests for not existing file"""
template_path = os.path.join('templates', 'i_dont_exist.html') template_name = "notfound.html"
self.assertContains(response, template_path, status_code=500) with NamedTemporaryFile(prefix=template_name) as tempfile:
tempdir = os.path.dirname(tempfile.name)
template_path = os.path.join(tempdir, template_name)
with override_settings(TEMPLATE_DIRS=(tempdir,)):
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
self.assertContains(response, "%s (File does not exist)" % template_path, status_code=500, count=1)
@skipIf(sys.platform == "win32", "Python on Windows doesn't have working os.chmod() and os.access().")
def test_template_loader_postmortem_notreadable(self):
"""Tests for not readable file"""
with NamedTemporaryFile() as tempfile:
template_name = tempfile.name
tempdir = os.path.dirname(tempfile.name)
template_path = os.path.join(tempdir, template_name)
os.chmod(template_path, 0o0222)
with override_settings(TEMPLATE_DIRS=(tempdir,)):
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
self.assertContains(response, "%s (File is not readable)" % template_path, status_code=500, count=1)
def test_template_loader_postmortem_notafile(self):
"""Tests for not being a file"""
try:
template_path = mkdtemp()
template_name = os.path.basename(template_path)
tempdir = os.path.dirname(template_path)
with override_settings(TEMPLATE_DIRS=(tempdir,)):
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
self.assertContains(response, "%s (Not a file)" % template_path, status_code=500, count=1)
finally:
shutil.rmtree(template_path)
class ExceptionReporterTests(TestCase): class ExceptionReporterTests(TestCase):
@ -128,8 +159,8 @@ class ExceptionReporterTests(TestCase):
LINES = list('print %d' % i for i in range(1, 6)) LINES = list('print %d' % i for i in range(1, 6))
reporter = ExceptionReporter(None, None, None, None) reporter = ExceptionReporter(None, None, None, None)
for newline in ['\n','\r\n','\r']: for newline in ['\n', '\r\n', '\r']:
fd,filename = tempfile.mkstemp(text = False) fd, filename = mkstemp(text=False)
os.write(fd, force_bytes(newline.join(LINES)+newline)) os.write(fd, force_bytes(newline.join(LINES)+newline))
os.close(fd) os.close(fd)

View File

@ -66,5 +66,5 @@ urlpatterns = patterns('',
urlpatterns += patterns('view_tests.views', urlpatterns += patterns('view_tests.views',
url(r'view_exception/(?P<n>\d+)/$', 'view_exception', name='view_exception'), url(r'view_exception/(?P<n>\d+)/$', 'view_exception', name='view_exception'),
url(r'template_exception/(?P<n>\d+)/$', 'template_exception', name='template_exception'), url(r'template_exception/(?P<n>\d+)/$', 'template_exception', name='template_exception'),
url(r'^raises_template_does_not_exist/$', 'raises_template_does_not_exist', name='raises_template_does_not_exist'), url(r'^raises_template_does_not_exist/(?P<path>.+)$', 'raises_template_does_not_exist', name='raises_template_does_not_exist'),
) )

View File

@ -112,11 +112,11 @@ def render_view_with_current_app_conflict(request):
'bar': 'BAR', 'bar': 'BAR',
}, current_app="foobar_app", context_instance=RequestContext(request)) }, current_app="foobar_app", context_instance=RequestContext(request))
def raises_template_does_not_exist(request): def raises_template_does_not_exist(request, path='i_dont_exist.html'):
# We need to inspect the HTML generated by the fancy 500 debug view but # We need to inspect the HTML generated by the fancy 500 debug view but
# the test client ignores it, so we send it explicitly. # the test client ignores it, so we send it explicitly.
try: try:
return render_to_response('i_dont_exist.html') return render_to_response(path)
except TemplateDoesNotExist: except TemplateDoesNotExist:
return technical_500_response(request, *sys.exc_info()) return technical_500_response(request, *sys.exc_info())