Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added "pretty" error pages to be used when DEBUG is True.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@1233 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 0710243ea12f9f88189197703d4c2e9e2e9fe741 1 parent 1775ee7
Jacob Kaplan-Moss jacobian authored
Showing with 446 additions and 26 deletions.
  1. +8 −26 django/core/handlers/base.py
  2. +438 −0 django/views/debug.py
34 django/core/handlers/base.py
View
@@ -86,14 +86,14 @@ def get_response(self, path, request):
return response
except exceptions.Http404, e:
if DEBUG:
- return self.get_technical_error_response(is404=True, exception=e)
+ return self.get_technical_error_response(request, is404=True, exception=e)
else:
callback, param_dict = resolver.resolve404()
return callback(request, **param_dict)
except db.DatabaseError:
db.db.rollback()
if DEBUG:
- return self.get_technical_error_response()
+ return self.get_technical_error_response(request)
else:
subject = 'Database error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in INTERNAL_IPS and 'internal' or 'EXTERNAL'), getattr(request, 'path', ''))
message = "%s\n\n%s" % (self._get_traceback(), request)
@@ -103,7 +103,7 @@ def get_response(self, path, request):
return httpwrappers.HttpResponseForbidden('<h1>Permission denied</h1>')
except: # Handle everything else, including SuspiciousOperation, etc.
if DEBUG:
- return self.get_technical_error_response()
+ return self.get_technical_error_response(request)
else:
subject = 'Coding error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in INTERNAL_IPS and 'internal' or 'EXTERNAL'), getattr(request, 'path', ''))
try:
@@ -123,35 +123,17 @@ def get_friendly_error_response(self, request, resolver):
callback, param_dict = resolver.resolve500()
return callback(request, **param_dict)
- def get_technical_error_response(self, is404=False, exception=None):
+ def get_technical_error_response(self, request, is404=False, exception=None):
"""
Returns an HttpResponse that displays a TECHNICAL error message for a
fundamental database or coding error.
"""
+ import sys
+ from django.views import debug
if is404:
- from django.conf.settings import ROOT_URLCONF
- from django.utils.html import escape
- html = ['<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">']
- html.append('<html><head><title>Error 404</title>')
- # Explicitly tell robots not to archive this, in case this slips
- # onto a production site.
- html.append('<meta name="robots" content="NONE,NOARCHIVE" />')
- html.append('</head><body><h1>Error 404</h1>')
- try:
- tried = exception.args[0]['tried']
- except (IndexError, TypeError):
- if exception.args:
- html.append('<p>%s</p>' % escape(exception.args[0]))
- else:
- html.append('<p>Using the URLconf defined in <code>%s</code>, Django tried these URL patterns, in this order:</p>' % ROOT_URLCONF)
- html.append('<ul>%s</ul>' % ''.join(['<li><code>%s</code></li>' % escape(t).replace(' ', '&nbsp;') for t in tried]))
- html.append("<p>The current URL, <code><strong>%r</strong></code>, didn't match any of these.</p>" % exception.args[0]['path'])
- html.append("<hr /><p>You're seeing this error because you have <code>DEBUG = True</code> in your Django settings file. Change that to <code>False</code>, and Django will display a standard 404 page.</p>")
- html.append('</body></html>')
- return httpwrappers.HttpResponseNotFound('\n'.join(html))
+ return debug.technical_404_response(request, exception)
else:
- output = "There's been an error:\n\n%s" % self._get_traceback()
- return httpwrappers.HttpResponseServerError(output, mimetype='text/plain')
+ return debug.technical_500_response(request, *sys.exc_info())
def _get_traceback(self):
"Helper function to return the traceback as a string"
438 django/views/debug.py
View
@@ -0,0 +1,438 @@
+import os
+import sys
+import inspect
+from django.conf import settings
+from os.path import dirname, join as pathjoin
+from django.core.template import Template, Context
+from django.utils.httpwrappers import HttpResponseServerError, HttpResponseNotFound
+
+def technical_500_response(request, exc_type, exc_value, tb):
+ """
+ Create a technical server error response. The last three arguments are
+ the values returned from sys.exc_info() and friends.
+ """
+ frames = []
+ while tb is not None:
+ filename = tb.tb_frame.f_code.co_filename
+ function = tb.tb_frame.f_code.co_name
+ lineno = tb.tb_lineno - 1
+ pre_context_lineno, pre_context, context_line, post_context = _get_lines_from_file(filename, lineno, 7)
+ frames.append({
+ 'tb' : tb,
+ 'filename' : filename,
+ 'function' : function,
+ 'lineno' : lineno,
+ 'vars' : tb.tb_frame.f_locals,
+ 'id' : id(tb),
+ 'pre_context' : pre_context,
+ 'context_line' : context_line,
+ 'post_context' : post_context,
+ 'pre_context_lineno' : pre_context_lineno,
+ })
+ tb = tb.tb_next
+
+ t = Template(TECHNICAL_500_TEMPLATE)
+ c = Context({
+ 'exception_type' : exc_type.__name__,
+ 'exception_value' : exc_value,
+ 'frames' : frames,
+ 'lastframe' : frames[-1],
+ 'request' : request,
+ 'request_protocol' : os.environ.get("HTTPS") == "on" and "https" or "http",
+ 'settings' : dict([(k, getattr(settings, k)) for k in dir(settings) if k.isupper()]),
+
+ })
+ return HttpResponseServerError(t.render(c))
+
+def technical_404_response(request, exception):
+ """
+ Create a technical 404 error response. The exception should be the Http404
+ exception.
+ """
+ try:
+ tried = exception.args[0]['tried']
+ except (IndexError, TypeError):
+ tried = []
+
+ t = Template(TECHNICAL_404_TEMPLATE)
+ c = Context({
+ 'root_urlconf' : settings.ROOT_URLCONF,
+ 'urlpatterns' : tried,
+ 'reason' : str(exception),
+ 'request' : request,
+ 'request_protocol' : os.environ.get("HTTPS") == "on" and "https" or "http",
+ 'settings' : dict([(k, getattr(settings, k)) for k in dir(settings) if k.isupper()]),
+ })
+ return HttpResponseNotFound(t.render(c))
+
+def _get_lines_from_file(filename, lineno, context_lines):
+ """
+ Returns context_lines before and after lineno from file.
+ Returns (pre_context_lineno, pre_context, context_line, post_context).
+ """
+ try:
+ source = open(filename).readlines()
+ lower_bound = max(0, lineno - context_lines)
+ upper_bound = lineno + context_lines
+
+ pre_context = [line.strip('\n') for line in source[lower_bound:lineno]]
+ context_line = source[lineno].strip('\n')
+ post_context = [line.strip('\n') for line in source[lineno+1:upper_bound]]
+
+ return lower_bound, pre_context, context_line, post_context
+ except (OSError, IOError):
+ return None, [], None, []
+
+#
+# Templates are embedded in the file so that we know the error handler will
+# always work even if the template loader is broken.
+#
+
+TECHNICAL_500_TEMPLATE = """
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>{{ exception_type }} at {{ request.path }}</title>
+ <style type="text/css">
+ html * { padding:0; margin:0; }
+ body * { padding:10px 20px; }
+ body * * { padding:0; }
+ body { font:small sans-serif; }
+ body>div { border-bottom:1px solid #ddd; }
+ h1 { font-weight:normal; }
+ h2 { margin-bottom:.8em; }
+ h2 span { font-size:80%; color:#666; font-weight:normal; }
+ h3 { margin:1em 0 .5em 0; }
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
+ table { border:1px solid #ccc; border-collapse: collapse; width:100%; background:white; }
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
+ thead th { padding:1px 6px 1px 3px; background:#fefefe; text-align:left; font-weight:normal; font-size:11px; border:1px solid #ddd; }
+ tbody th { width:12em; text-align:right; color:#666; padding-right:.5em; }
+ table.vars { margin:5px 0 2px 40px; }
+ table.vars td, table.req td { font-family:monospace; }
+ table td.code { width:100%; }
+ table td.code div { overflow:hidden; }
+ ul.traceback { list-style-type:none; }
+ ul.traceback li.frame { margin-bottom:1em; }
+ div.context { margin: 10px 0; }
+ div.context ol { padding-left:30px; margin:0 10px; list-style-position: inside; }
+ div.context ol li { font-family:monospace; white-space:pre; color:#666; }
+ div.context ol.context-line li { color:black; background-color:#ccc; cursor:pointer; }
+ div.commands { margin-left: 40px; }
+ div.commands a { color:black; text-decoration:none; }
+ #summary { background: #ffc; }
+ #summary h2 { font-weight: normal; color: #666; }
+ #explanation { background:#eee; }
+ #traceback { background:#eee; }
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
+ #summary table { border:none; background:transparent; }
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
+ #requestinfo h3 { margin-bottom:-1em; }
+ </style>
+ <script type="text/javascript">
+ function getElementsByClassName(oElm, strTagName, strClassName){
+ // Written by Jonathan Snook, http://www.snook.ca/jon; Add-ons by Robert Nyman, http://www.robertnyman.com
+ var arrElements = (strTagName == "*" && document.all)? document.all :
+ oElm.getElementsByTagName(strTagName);
+ var arrReturnElements = new Array();
+ strClassName = strClassName.replace(/\-/g, "\\-");
+ var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
+ var oElement;
+ for(var i=0; i<arrElements.length; i++){
+ oElement = arrElements[i];
+ if(oRegExp.test(oElement.className)){
+ arrReturnElements.push(oElement);
+ }
+ }
+ return (arrReturnElements)
+ }
+ function hideAll(elems) {
+ for (var e = 0; e < elems.length; e++) {
+ elems[e].style.display = 'none';
+ }
+ }
+ window.onload = function() {
+ hideAll(getElementsByClassName(document, 'table', 'vars'));
+ hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
+ hideAll(getElementsByClassName(document, 'ol', 'post-context'));
+ }
+ function toggle() {
+ for (var i = 0; i < arguments.length; i++) {
+ var e = document.getElementById(arguments[i]);
+ if (e) {
+ e.style.display = e.style.display == 'none' ? 'block' : 'none';
+ }
+ }
+ return false;
+ }
+ function varToggle(link, id) {
+ toggle('v' + id);
+ var s = link.getElementsByTagName('span')[0];
+ var uarr = String.fromCharCode(0x2191);
+ var darr = String.fromCharCode(0x2193);
+ s.innerHTML = s.innerHTML == uarr ? darr : uarr;
+ return false;
+ }
+ </script>
+</head>
+<body>
+
+<div id="summary">
+ <h1>{{ exception_type }} at {{ request.path }}</h1>
+ <h2>{{ exception_value }}</h2>
+ <table class="meta">
+ <tr>
+ <th>Request Method:</th>
+ <td>{{ request.META.REQUEST_METHOD }}</td>
+ </tr>
+ <tr>
+ <th>Request URL:</th>
+ <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path }}</td>
+ </tr>
+ <tr>
+ <th>Exception Type:</th>
+ <td>{{ exception_type }}</td>
+ </tr>
+ <tr>
+ <th>Exception Value:</th>
+ <td>{{ exception_value }}</td>
+ </tr>
+ <tr>
+ <th>Exception Location:</th>
+ <td>{{ lastframe.filename }} in {{ lastframe.function }}, line {{ lastframe.lineno }}</td>
+ </tr>
+ </table>
+</div>
+
+<div id="traceback">
+ <h2>Traceback <span>(innermost last)</span></h2>
+ <ul class="traceback">
+ {% for frame in frames %}
+ <li class="frame">
+ <code>{{ frame.filename }}</code> in <code>{{ frame.function }}</code>
+
+ {% if frame.context_line %}
+ <div class="context" id="c{{ frame.id }}">
+ {% if frame.pre_context %}
+ <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li>{{ line|escape }}</li>{% endfor %}</ol>
+ {% endif %}
+ <ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line|escape }}</li></ol>
+ {% if frame.post_context %}
+ <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li>{{ line|escape }}</li>{% endfor %}</ol>
+ {% endif %}
+ </div>
+ {% endif %}
+
+ {% if frame.vars %}
+ <div class="commands">
+ <a href="#" onclick="return varToggle(this, '{{ frame.id }}')">Local vars <span>&darr;</span> </a>
+ </div>
+ <table class="vars" id="v{{ frame.id }}">
+ <thead>
+ <tr>
+ <th>Variable</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for var in frame.vars.items|dictsort:"0" %}
+ <tr>
+ <td>{{ var.0 }}</td>
+ <td class="code"><div>{{ var.1|pprint|escape }}</div></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% endif %}
+ </li>
+ {% endfor %}
+ </ul>
+</div>
+
+<div id="requestinfo">
+ <h2>Request information</h2>
+
+ <h3 id="get-info">GET</h3>
+ {% if request.GET %}
+ <table class="req">
+ <thead>
+ <tr>
+ <th>Variable</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ </tbody>
+ {% for var in request.GET.items %}
+ <tr>
+ <td>{{ var.0 }}</td>
+ <td class="code"><div>{{ var.1|pprint|escape }}</div></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <p>No GET data<p>
+ {% endif %}
+
+ <h3 id="post-info">POST</h3>
+ {% if request.POST %}
+ <table class="req">
+ <thead>
+ <tr>
+ <th>Variable</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ </tbody>
+ {% for var in request.POST.items %}
+ <tr>
+ <td>{{ var.0 }}</td>
+ <td class="code"><div>{{ var.1|pprint|escape }}</div></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <p>No POST data<p>
+ {% endif %}
+
+ <h3 id="cookie-info">COOKIES</h3>
+ {% if request.COOKIES %}
+ <table class="req">
+ <thead>
+ <tr>
+ <th>Variable</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ </tbody>
+ {% for var in request.COOKIES.items %}
+ <tr>
+ <td>{{ var.0 }}</td>
+ <td class="code"><div>{{ var.1|pprint|escape }}</div></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <p>No cookie data<p>
+ {% endif %}
+
+ <h3 id="meta-info">META</h3>
+ <table class="req">
+ <thead>
+ <tr>
+ <th>Variable</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ </tbody>
+ {% for var in request.META.items|dictsort:"0" %}
+ <tr>
+ <td>{{ var.0 }}</td>
+ <td class="code"><div>{{ var.1|pprint|escape }}</div></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+ <h3 id="settings-info">Settings</h3>
+ <h4>Using settings module <code>{{ settings.SETTINGS_MODULE }}</code></h4>
+ <table class="req">
+ <thead>
+ <tr>
+ <th>Setting</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ </tbody>
+ {% for var in settings.items|dictsort:"0" %}
+ <tr>
+ <td>{{ var.0 }}</td>
+ <td class="code"><div>{{ var.1|pprint|escape }}</div></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+</div>
+
+<div id="explanation">
+ <p>
+ You're seeing this error because you have <code>DEBUG = True</code> in your
+ Django settings file. Change that to <code>False</code>, and Django will
+ display a standard 500 page.
+ </p>
+</div>
+
+</body>
+</html>
+"""
+
+TECHNICAL_404_TEMPLATE = """
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>Page not found at {{ request.path }}</title>
+ <meta name="robots" content="NONE,NOARCHIVE" />
+ <style type="text/css">
+ html * { padding:0; margin:0; }
+ body * { padding:10px 20px; }
+ body * * { padding:0; }
+ body { font:small sans-serif; background:#eee; }
+ body>div { border-bottom:1px solid #ddd; }
+ h1 { font-weight:normal; margin-bottom:.4em; }
+ h1 span { font-size:60%; color:#666; font-weight:normal; }
+ table { border:none; border-collapse: collapse; width:100%; }
+ td, th { vertical-align:top; padding:2px 3px; }
+ th { width:12em; text-align:right; color:#666; padding-right:.5em; }
+ #info { background:#f6f6f6; }
+ #info ol { margin: 0.5em 4em; }
+ #info ol li { font-family: monospace; }
+ #summary { background: #ffc; }
+ #explanation { background:#eee; border-bottom: 0px none; }
+ </style>
+</head>
+<body>
+ <div id="summary">
+ <h1>Page not found <span>(404)</span></h1>
+ <table class="meta">
+ <tr>
+ <th>Request Method:</th>
+ <td>{{ request.META.REQUEST_METHOD }}</td>
+ </tr>
+ <tr>
+ <th>Request URL:</th>
+ <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path }}</td>
+ </tr>
+ </table>
+ </div>
+ <div id="info">
+ {% if urlpatterns %}
+ <p>
+ Using the URLconf defined in <code>{{ settings.ROOT_URLCONF }}</code>,
+ Django tried these URL patterns, in this order:
+ </p>
+ <ol>
+ {% for pattern in urlpatterns %}
+ <li>{{ pattern|escape }}</li>
+ {% endfor %}
+ </ol>
+ <p>The current URL, <code>{{ request.path }}</code>, didn't match any of these.</p>
+ {% else %}
+ <p>{{ reason|escape }}</p>
+ {% endif %}
+ </div>
+
+ <div id="explanation">
+ <p>
+ You're seeing this error because you have <code>DEBUG = True</code> in
+ your Django settings file. Change that to <code>False</code>, and Django
+ will display a standard 404 page.
+ </p>
+ </div>
+</body>
+</html>
+"""
Please sign in to comment.
Something went wrong with that request. Please try again.