From 61a8de6f4f547518d217a5ff959cbd57bddf4bb0 Mon Sep 17 00:00:00 2001 From: Mathijs de Bruin Date: Sun, 24 Feb 2013 16:41:10 +0100 Subject: [PATCH] Fixed #6412 -- More details if a template file cannot be loaded Report more details about template files in loader postmortem. --- django/views/debug.py | 23 +++++++++++---- tests/view_tests/tests/test_debug.py | 43 ++++++++++++++++++++++++---- tests/view_tests/urls.py | 2 +- tests/view_tests/views.py | 4 +-- 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/django/views/debug.py b/django/views/debug.py index 10c07e8f78..209cdce8c9 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -218,6 +218,15 @@ class ExceptionReporter(object): self.exc_value = Exception('Deprecated String Exception: %r' % self.exc_type) 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): "Return a Context instance containing traceback information." @@ -230,8 +239,10 @@ class ExceptionReporter(object): source_list_func = loader.get_template_sources # NOTE: This assumes exc_value is the name of the template that # the loader attempted to load. - template_list = [{'name': t, 'exists': os.path.exists(t)} \ - for t in source_list_func(str(self.exc_value))] + template_list = [{ + 'name': t, + 'status': self.format_path_status(t), + } for t in source_list_func(str(self.exc_value))] except AttributeError: template_list = [] loader_name = loader.__module__ + '.' + loader.__class__.__name__ @@ -650,7 +661,9 @@ TECHNICAL_500_TEMPLATE = """ @@ -753,7 +766,7 @@ Installed Middleware: {% if template_does_not_exist %}Template Loader Error: {% if loader_debug_info %}Django tried loading these templates, in this order: {% 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 %} {% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty! {% endif %} @@ -943,7 +956,7 @@ Installed Middleware: {% if template_does_not_exist %}Template loader Error: {% if loader_debug_info %}Django tried loading these templates, in this order: {% 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 %} {% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty! {% endif %} diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index a84b41959c..5f48d7a2b8 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -5,8 +5,9 @@ from __future__ import absolute_import, unicode_literals import inspect import os +import shutil import sys -import tempfile +from tempfile import NamedTemporaryFile, mkdtemp, mkstemp from django.core import mail 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, custom_exception_reporter_filter_view, sensitive_method_view, sensitive_args_function_caller, sensitive_kwargs_function_caller) +from django.utils.unittest import skipIf @override_settings(DEBUG=True, TEMPLATE_DEBUG=True) @@ -78,9 +80,38 @@ class DebugViewTests(TestCase): raising_loc) def test_template_loader_postmortem(self): - response = self.client.get(reverse('raises_template_does_not_exist')) - template_path = os.path.join('templates', 'i_dont_exist.html') - self.assertContains(response, template_path, status_code=500) + """Tests for not existing file""" + template_name = "notfound.html" + 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): @@ -128,8 +159,8 @@ class ExceptionReporterTests(TestCase): LINES = list('print %d' % i for i in range(1, 6)) reporter = ExceptionReporter(None, None, None, None) - for newline in ['\n','\r\n','\r']: - fd,filename = tempfile.mkstemp(text = False) + for newline in ['\n', '\r\n', '\r']: + fd, filename = mkstemp(text=False) os.write(fd, force_bytes(newline.join(LINES)+newline)) os.close(fd) diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py index 52e2eb474e..d792e47ddf 100644 --- a/tests/view_tests/urls.py +++ b/tests/view_tests/urls.py @@ -66,5 +66,5 @@ urlpatterns = patterns('', urlpatterns += patterns('view_tests.views', url(r'view_exception/(?P\d+)/$', 'view_exception', name='view_exception'), url(r'template_exception/(?P\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.+)$', 'raises_template_does_not_exist', name='raises_template_does_not_exist'), ) diff --git a/tests/view_tests/views.py b/tests/view_tests/views.py index 50ad98ac2d..1cfafa4333 100644 --- a/tests/view_tests/views.py +++ b/tests/view_tests/views.py @@ -112,11 +112,11 @@ def render_view_with_current_app_conflict(request): 'bar': 'BAR', }, 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 # the test client ignores it, so we send it explicitly. try: - return render_to_response('i_dont_exist.html') + return render_to_response(path) except TemplateDoesNotExist: return technical_500_response(request, *sys.exc_info())