mirror of
https://github.com/django/django.git
synced 2025-03-31 19:46:42 +00:00
Improved display of template loader postmortem on debug page.
This now works for multiple Django engines and recursive loaders. Support for non-Django engines is still pending. Refs #15053.
This commit is contained in:
parent
fc21471526
commit
65a7a0d9ee
@ -1,6 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
@ -10,7 +9,7 @@ from django.core.urlresolvers import Resolver404, resolve
|
|||||||
from django.http import (
|
from django.http import (
|
||||||
HttpRequest, HttpResponse, HttpResponseNotFound, build_request_repr,
|
HttpRequest, HttpResponse, HttpResponseNotFound, build_request_repr,
|
||||||
)
|
)
|
||||||
from django.template import Context, Engine, TemplateDoesNotExist
|
from django.template import Context, Engine, TemplateDoesNotExist, engines
|
||||||
from django.template.defaultfilters import force_escape, pprint
|
from django.template.defaultfilters import force_escape, pprint
|
||||||
from django.utils import lru_cache, six, timezone
|
from django.utils import lru_cache, six, timezone
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
@ -266,61 +265,36 @@ class ExceptionReporter(object):
|
|||||||
|
|
||||||
self.template_info = getattr(self.exc_value, 'template_debug', None)
|
self.template_info = getattr(self.exc_value, 'template_debug', None)
|
||||||
self.template_does_not_exist = False
|
self.template_does_not_exist = False
|
||||||
self.loader_debug_info = None
|
self.postmortem = None
|
||||||
|
|
||||||
# Handle deprecated string exceptions
|
# Handle deprecated string exceptions
|
||||||
if isinstance(self.exc_type, six.string_types):
|
if isinstance(self.exc_type, six.string_types):
|
||||||
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"
|
|
||||||
return "File exists"
|
|
||||||
|
|
||||||
def get_traceback_data(self):
|
def get_traceback_data(self):
|
||||||
"""Return a dictionary containing traceback information."""
|
"""Return a dictionary containing traceback information."""
|
||||||
try:
|
|
||||||
default_template_engine = Engine.get_default()
|
|
||||||
except Exception:
|
|
||||||
# Since the debug view must never crash, catch all exceptions.
|
|
||||||
# If Django can't find a default template engine, get_default()
|
|
||||||
# raises ImproperlyConfigured. If some template engines fail to
|
|
||||||
# load, any exception may be raised.
|
|
||||||
default_template_engine = None
|
|
||||||
|
|
||||||
# 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 self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
|
||||||
if default_template_engine is None:
|
self.template_does_not_exist = True
|
||||||
template_loaders = []
|
postmortem = []
|
||||||
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 = default_template_engine.template_loaders
|
|
||||||
except Exception:
|
|
||||||
template_loaders = []
|
|
||||||
|
|
||||||
for loader in template_loaders:
|
# TODO: add support for multiple template engines (#24120).
|
||||||
try:
|
# TemplateDoesNotExist should carry all the information, including
|
||||||
source_list_func = loader.get_template_sources
|
# the backend, rather than looping through engines.all.
|
||||||
# NOTE: This assumes exc_value is the name of the template that
|
for engine in engines.all():
|
||||||
# the loader attempted to load.
|
if hasattr(engine, 'engine'):
|
||||||
template_list = [{
|
e = engine.engine
|
||||||
'name': t,
|
else:
|
||||||
'status': self.format_path_status(t),
|
e = engine
|
||||||
} for t in source_list_func(str(self.exc_value))]
|
|
||||||
except AttributeError:
|
postmortem.append(dict(
|
||||||
template_list = []
|
engine=engine,
|
||||||
loader_name = loader.__module__ + '.' + loader.__class__.__name__
|
tried=[
|
||||||
self.loader_debug_info.append({
|
entry for entry in self.exc_value.tried if
|
||||||
'loader': loader_name,
|
entry[0].loader.engine == e
|
||||||
'templates': template_list,
|
],
|
||||||
})
|
))
|
||||||
|
self.postmortem = postmortem
|
||||||
|
|
||||||
frames = self.get_traceback_frames()
|
frames = self.get_traceback_frames()
|
||||||
for i, frame in enumerate(frames):
|
for i, frame in enumerate(frames):
|
||||||
@ -363,7 +337,7 @@ class ExceptionReporter(object):
|
|||||||
'sys_path': sys.path,
|
'sys_path': sys.path,
|
||||||
'template_info': self.template_info,
|
'template_info': self.template_info,
|
||||||
'template_does_not_exist': self.template_does_not_exist,
|
'template_does_not_exist': self.template_does_not_exist,
|
||||||
'loader_debug_info': self.loader_debug_info,
|
'postmortem': self.postmortem,
|
||||||
}
|
}
|
||||||
# Check whether exception info is available
|
# Check whether exception info is available
|
||||||
if self.exc_type:
|
if self.exc_type:
|
||||||
@ -634,7 +608,8 @@ TECHNICAL_500_TEMPLATE = ("""
|
|||||||
#summary h2 { font-weight: normal; color: #666; }
|
#summary h2 { font-weight: normal; color: #666; }
|
||||||
#explanation { background:#eee; }
|
#explanation { background:#eee; }
|
||||||
#template, #template-not-exist { background:#f6f6f6; }
|
#template, #template-not-exist { background:#f6f6f6; }
|
||||||
#template-not-exist ul { margin: 0 0 0 20px; }
|
#template-not-exist ul { margin: 0 0 10px 20px; }
|
||||||
|
#template-not-exist .postmortem-section { margin-bottom: 3px; }
|
||||||
#unicode-hint { background:#eee; }
|
#unicode-hint { background:#eee; }
|
||||||
#traceback { background:#eee; }
|
#traceback { background:#eee; }
|
||||||
#requestinfo { background:#f6f6f6; padding-left:120px; }
|
#requestinfo { background:#f6f6f6; padding-left:120px; }
|
||||||
@ -646,6 +621,7 @@ TECHNICAL_500_TEMPLATE = ("""
|
|||||||
h2 span.commands { font-size:.7em;}
|
h2 span.commands { font-size:.7em;}
|
||||||
span.commands a:link {color:#5E5694;}
|
span.commands a:link {color:#5E5694;}
|
||||||
pre.exception_value { font-family: sans-serif; color: #666; font-size: 1.5em; margin: 10px 0 10px 0; }
|
pre.exception_value { font-family: sans-serif; color: #666; font-size: 1.5em; margin: 10px 0 10px 0; }
|
||||||
|
.append-bottom { margin-bottom: 10px; }
|
||||||
</style>
|
</style>
|
||||||
{% if not is_email %}
|
{% if not is_email %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@ -772,19 +748,23 @@ TECHNICAL_500_TEMPLATE = ("""
|
|||||||
{% if template_does_not_exist %}
|
{% if template_does_not_exist %}
|
||||||
<div id="template-not-exist">
|
<div id="template-not-exist">
|
||||||
<h2>Template-loader postmortem</h2>
|
<h2>Template-loader postmortem</h2>
|
||||||
{% if loader_debug_info %}
|
{% if postmortem %}
|
||||||
<p>Django tried loading these templates, in this order:</p>
|
<p class="append-bottom">Django tried loading these templates, in this order:</p>
|
||||||
<ul>
|
{% for entry in postmortem %}
|
||||||
{% for loader in loader_debug_info %}
|
<p class="postmortem-section">Using engine <code>{{ entry.engine.name }}</code>:</p>
|
||||||
<li>Using loader <code>{{ loader.loader }}</code>:
|
<ul>
|
||||||
<ul>
|
{% if entry.tried %}
|
||||||
{% for t in loader.templates %}<li><code>{{ t.name }}</code> ({{ t.status }})</li>{% endfor %}
|
{% for attempt in entry.tried %}
|
||||||
</ul>
|
<li><code>{{ attempt.0.loader_name }}</code>: {{ attempt.0.name }} ({{ attempt.1 }})</li>
|
||||||
</li>
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<li>This engine did not provide a list of tried templates.</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>Django couldn't find any templates because your <code>'loaders'</code> option is empty!</p>
|
<p>No templates were found because your 'TEMPLATES' setting is not configured.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -907,12 +887,14 @@ Installed Applications:
|
|||||||
Installed Middleware:
|
Installed Middleware:
|
||||||
{{ settings.MIDDLEWARE_CLASSES|pprint }}
|
{{ settings.MIDDLEWARE_CLASSES|pprint }}
|
||||||
|
|
||||||
{% if template_does_not_exist %}Template Loader Error:
|
{% if template_does_not_exist %}Template loader postmortem
|
||||||
{% if loader_debug_info %}Django tried loading these templates, in this order:
|
{% if postmortem %}Django tried loading these templates, in this order:
|
||||||
{% for loader in loader_debug_info %}Using loader {{ loader.loader }}:
|
{% for entry in postmortem %}
|
||||||
{% for t in loader.templates %}{{ t.name }} ({{ t.status }})
|
Using engine {{ entry.engine.name }}:
|
||||||
{% endfor %}{% endfor %}
|
{% if entry.tried %}{% for attempt in entry.tried %} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
|
||||||
{% else %}Django couldn't find any templates because your 'loaders' option is empty!
|
{% endfor %}{% else %} This engine did not provide a list of tried templates.
|
||||||
|
{% endif %}{% endfor %}
|
||||||
|
{% else %}No templates were found because your 'TEMPLATES' setting is not configured.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}{% if template_info %}
|
{% endif %}{% if template_info %}
|
||||||
Template error:
|
Template error:
|
||||||
@ -1098,12 +1080,14 @@ Installed Applications:
|
|||||||
{{ settings.INSTALLED_APPS|pprint }}
|
{{ settings.INSTALLED_APPS|pprint }}
|
||||||
Installed Middleware:
|
Installed Middleware:
|
||||||
{{ settings.MIDDLEWARE_CLASSES|pprint }}
|
{{ settings.MIDDLEWARE_CLASSES|pprint }}
|
||||||
{% if template_does_not_exist %}Template loader Error:
|
{% if template_does_not_exist %}Template loader postmortem
|
||||||
{% if loader_debug_info %}Django tried loading these templates, in this order:
|
{% if postmortem %}Django tried loading these templates, in this order:
|
||||||
{% for loader in loader_debug_info %}Using loader {{ loader.loader }}:
|
{% for entry in postmortem %}
|
||||||
{% for t in loader.templates %}{{ t.name }} ({{ t.status }})
|
Using engine {{ entry.engine.name }}:
|
||||||
{% endfor %}{% endfor %}
|
{% if entry.tried %}{% for attempt in entry.tried %} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
|
||||||
{% else %}Django couldn't find any templates because your 'loaders' option is empty!
|
{% endfor %}{% else %} This engine did not provide a list of tried templates.
|
||||||
|
{% endif %}{% endfor %}
|
||||||
|
{% else %}No templates were found because your 'TEMPLATES' setting is not configured.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}{% if template_info %}
|
{% endif %}{% if template_info %}
|
||||||
Template error:
|
Template error:
|
||||||
|
@ -153,7 +153,7 @@ class DebugViewTests(TestCase):
|
|||||||
'DIRS': [tempdir],
|
'DIRS': [tempdir],
|
||||||
}]):
|
}]):
|
||||||
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
|
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)
|
self.assertContains(response, "%s (Source does not exist)" % template_path, status_code=500, count=2)
|
||||||
|
|
||||||
def test_no_template_source_loaders(self):
|
def test_no_template_source_loaders(self):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user