--- a/app/django/views/debug.py Tue Oct 14 12:36:55 2008 +0000
+++ b/app/django/views/debug.py Tue Oct 14 16:00:59 2008 +0000
@@ -7,7 +7,7 @@
from django.template import Template, Context, TemplateDoesNotExist
from django.utils.html import escape
from django.http import HttpResponse, HttpResponseServerError, HttpResponseNotFound
-from django.utils.encoding import smart_unicode
+from django.utils.encoding import smart_unicode, smart_str
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST')
@@ -19,42 +19,6 @@
p = template_source.find('\n', p+1)
yield len(template_source) + 1
-def get_template_exception_info(exc_type, exc_value, tb):
- origin, (start, end) = exc_value.source
- template_source = origin.reload()
- context_lines = 10
- line = 0
- upto = 0
- source_lines = []
- before = during = after = ""
- for num, next in enumerate(linebreak_iter(template_source)):
- if start >= upto and end <= next:
- line = num
- before = escape(template_source[upto:start])
- during = escape(template_source[start:end])
- after = escape(template_source[end:next])
- source_lines.append( (num, escape(template_source[upto:next])) )
- upto = next
- total = len(source_lines)
-
- top = max(1, line - context_lines)
- bottom = min(total, line + 1 + context_lines)
-
- template_info = {
- 'message': exc_value.args[0],
- 'source_lines': source_lines[top:bottom],
- 'before': before,
- 'during': during,
- 'after': after,
- 'top': top,
- 'bottom': bottom,
- 'total': total,
- 'line': line,
- 'name': origin.name,
- }
- exc_info = hasattr(exc_value, 'exc_info') and exc_value.exc_info or (exc_type, exc_value, tb)
- return exc_info + (template_info,)
-
def get_safe_settings():
"Returns a dictionary of the settings module, with sensitive settings blurred out."
settings_dict = {}
@@ -71,102 +35,210 @@
Create a technical server error response. The last three arguments are
the values returned from sys.exc_info() and friends.
"""
- html = get_traceback_html(request, exc_type, exc_value, tb)
+ reporter = ExceptionReporter(request, exc_type, exc_value, tb)
+ html = reporter.get_traceback_html()
return HttpResponseServerError(html, mimetype='text/html')
-def get_traceback_html(request, exc_type, exc_value, tb):
- "Return HTML code for traceback."
- template_info = None
- template_does_not_exist = False
- loader_debug_info = None
+class ExceptionReporter:
+ """
+ A class to organize and coordinate reporting on exceptions.
+ """
+ def __init__(self, request, exc_type, exc_value, tb):
+ self.request = request
+ self.exc_type = exc_type
+ self.exc_value = exc_value
+ self.tb = tb
+
+ self.template_info = None
+ self.template_does_not_exist = False
+ self.loader_debug_info = None
+
+ # Handle deprecated string exceptions
+ if isinstance(self.exc_type, basestring):
+ self.exc_value = Exception('Deprecated String Exception: %r' % self.exc_type)
+ self.exc_type = type(self.exc_value)
+
+ def get_traceback_html(self):
+ "Return HTML code for traceback."
+
+ if issubclass(self.exc_type, TemplateDoesNotExist):
+ from django.template.loader import template_source_loaders
+ self.template_does_not_exist = True
+ self.loader_debug_info = []
+ for loader in template_source_loaders:
+ try:
+ source_list_func = getattr(__import__(loader.__module__, {}, {}, ['get_template_sources']), '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))]
+ except (ImportError, AttributeError):
+ template_list = []
+ self.loader_debug_info.append({
+ 'loader': loader.__module__ + '.' + loader.__name__,
+ 'templates': template_list,
+ })
+ if settings.TEMPLATE_DEBUG and hasattr(self.exc_value, 'source'):
+ self.get_template_exception_info()
+
+ frames = self.get_traceback_frames()
- # Handle deprecated string exceptions
- if isinstance(exc_type, basestring):
- exc_value = Exception('Deprecated String Exception: %r' % exc_type)
- exc_type = type(exc_value)
+ unicode_hint = ''
+ if issubclass(self.exc_type, UnicodeError):
+ start = getattr(self.exc_value, 'start', None)
+ end = getattr(self.exc_value, 'end', None)
+ if start is not None and end is not None:
+ unicode_str = self.exc_value.args[1]
+ unicode_hint = smart_unicode(unicode_str[max(start-5, 0):min(end+5, len(unicode_str))], 'ascii', errors='replace')
+ from django import get_version
+ t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
+ c = Context({
+ 'exception_type': self.exc_type.__name__,
+ 'exception_value': smart_unicode(self.exc_value, errors='replace'),
+ 'unicode_hint': unicode_hint,
+ 'frames': frames,
+ 'lastframe': frames[-1],
+ 'request': self.request,
+ 'request_protocol': self.request.is_secure() and "https" or "http",
+ 'settings': get_safe_settings(),
+ 'sys_executable': sys.executable,
+ 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
+ 'server_time': datetime.datetime.now(),
+ 'django_version_info': get_version(),
+ 'sys_path' : sys.path,
+ 'template_info': self.template_info,
+ 'template_does_not_exist': self.template_does_not_exist,
+ 'loader_debug_info': self.loader_debug_info,
+ })
+ return t.render(c)
- if issubclass(exc_type, TemplateDoesNotExist):
- from django.template.loader import template_source_loaders
- template_does_not_exist = True
- loader_debug_info = []
- for loader in template_source_loaders:
+ def get_template_exception_info(self):
+ origin, (start, end) = self.exc_value.source
+ template_source = origin.reload()
+ context_lines = 10
+ line = 0
+ upto = 0
+ source_lines = []
+ before = during = after = ""
+ for num, next in enumerate(linebreak_iter(template_source)):
+ if start >= upto and end <= next:
+ line = num
+ before = escape(template_source[upto:start])
+ during = escape(template_source[start:end])
+ after = escape(template_source[end:next])
+ source_lines.append( (num, escape(template_source[upto:next])) )
+ upto = next
+ total = len(source_lines)
+
+ top = max(1, line - context_lines)
+ bottom = min(total, line + 1 + context_lines)
+
+ self.template_info = {
+ 'message': self.exc_value.args[0],
+ 'source_lines': source_lines[top:bottom],
+ 'before': before,
+ 'during': during,
+ 'after': after,
+ 'top': top,
+ 'bottom': bottom,
+ 'total': total,
+ 'line': line,
+ 'name': origin.name,
+ }
+
+ def _get_lines_from_file(self, filename, lineno, context_lines, loader=None, module_name=None):
+ """
+ Returns context_lines before and after lineno from file.
+ Returns (pre_context_lineno, pre_context, context_line, post_context).
+ """
+ source = None
+ if loader is not None and hasattr(loader, "get_source"):
+ source = loader.get_source(module_name)
+ if source is not None:
+ source = source.splitlines()
+ if source is None:
try:
- source_list_func = getattr(__import__(loader.__module__, {}, {}, ['get_template_sources']), '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(exc_value))]
- except (ImportError, AttributeError):
- template_list = []
- loader_debug_info.append({
- 'loader': loader.__module__ + '.' + loader.__name__,
- 'templates': template_list,
- })
- if settings.TEMPLATE_DEBUG and hasattr(exc_value, 'source'):
- exc_type, exc_value, tb, template_info = get_template_exception_info(exc_type, exc_value, tb)
- frames = []
- while tb is not None:
- # support for __traceback_hide__ which is used by a few libraries
- # to hide internal frames.
- if tb.tb_frame.f_locals.get('__traceback_hide__'):
+ f = open(filename)
+ try:
+ source = f.readlines()
+ finally:
+ f.close()
+ except (OSError, IOError):
+ pass
+ if source is None:
+ return None, [], None, []
+
+ encoding = 'ascii'
+ for line in source[:2]:
+ # File coding may be specified. Match pattern from PEP-263
+ # (http://www.python.org/dev/peps/pep-0263/)
+ match = re.search(r'coding[:=]\s*([-\w.]+)', line)
+ if match:
+ encoding = match.group(1)
+ break
+ source = [unicode(sline, encoding, 'replace') for sline in source]
+
+ 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
+
+ def get_traceback_frames(self):
+ frames = []
+ tb = self.tb
+ while tb is not None:
+ # support for __traceback_hide__ which is used by a few libraries
+ # to hide internal frames.
+ if tb.tb_frame.f_locals.get('__traceback_hide__'):
+ tb = tb.tb_next
+ continue
+ filename = tb.tb_frame.f_code.co_filename
+ function = tb.tb_frame.f_code.co_name
+ lineno = tb.tb_lineno - 1
+ loader = tb.tb_frame.f_globals.get('__loader__')
+ module_name = tb.tb_frame.f_globals.get('__name__')
+ pre_context_lineno, pre_context, context_line, post_context = self._get_lines_from_file(filename, lineno, 7, loader, module_name)
+ if pre_context_lineno is not None:
+ frames.append({
+ 'tb': tb,
+ 'filename': filename,
+ 'function': function,
+ 'lineno': lineno + 1,
+ 'vars': tb.tb_frame.f_locals.items(),
+ 'id': id(tb),
+ 'pre_context': pre_context,
+ 'context_line': context_line,
+ 'post_context': post_context,
+ 'pre_context_lineno': pre_context_lineno + 1,
+ })
tb = tb.tb_next
- continue
- filename = tb.tb_frame.f_code.co_filename
- function = tb.tb_frame.f_code.co_name
- lineno = tb.tb_lineno - 1
- loader = tb.tb_frame.f_globals.get('__loader__')
- module_name = tb.tb_frame.f_globals.get('__name__')
- pre_context_lineno, pre_context, context_line, post_context = _get_lines_from_file(filename, lineno, 7, loader, module_name)
- if pre_context_lineno is not None:
- frames.append({
- 'tb': tb,
- 'filename': filename,
- 'function': function,
- 'lineno': lineno + 1,
- 'vars': tb.tb_frame.f_locals.items(),
- 'id': id(tb),
- 'pre_context': pre_context,
- 'context_line': context_line,
- 'post_context': post_context,
- 'pre_context_lineno': pre_context_lineno + 1,
- })
- tb = tb.tb_next
- if not frames:
- frames = [{
- 'filename': '<unknown>',
- 'function': '?',
- 'lineno': '?',
- }]
+ if not frames:
+ frames = [{
+ 'filename': '<unknown>',
+ 'function': '?',
+ 'lineno': '?',
+ 'context_line': '???',
+ }]
+
+ return frames
- unicode_hint = ''
- if issubclass(exc_type, UnicodeError):
- start = getattr(exc_value, 'start', None)
- end = getattr(exc_value, 'end', None)
- if start is not None and end is not None:
- unicode_str = exc_value.args[1]
- unicode_hint = smart_unicode(unicode_str[max(start-5, 0):min(end+5, len(unicode_str))], 'ascii', errors='replace')
- from django import get_version
- t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
- c = Context({
- 'exception_type': exc_type.__name__,
- 'exception_value': smart_unicode(exc_value, errors='replace'),
- 'unicode_hint': unicode_hint,
- 'frames': frames,
- 'lastframe': frames[-1],
- 'request': request,
- 'request_protocol': request.is_secure() and "https" or "http",
- 'settings': get_safe_settings(),
- 'sys_executable': sys.executable,
- 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
- 'server_time': datetime.datetime.now(),
- 'django_version_info': get_version(),
- 'sys_path' : sys.path,
- 'template_info': template_info,
- 'template_does_not_exist': template_does_not_exist,
- 'loader_debug_info': loader_debug_info,
- })
- return t.render(c)
+ def format_exception(self):
+ """
+ Return the same data as from traceback.format_exception.
+ """
+ import traceback
+ frames = self.get_traceback_frames()
+ tb = [ (f['filename'], f['lineno'], f['function'], f['context_line']) for f in frames ]
+ list = ['Traceback (most recent call last):\n']
+ list += traceback.format_list(tb)
+ list += traceback.format_exception_only(self.exc_type, self.exc_value)
+ return list
+
def technical_404_response(request, exception):
"Create a technical 404 error response. The exception should be the Http404."
@@ -182,9 +254,9 @@
t = Template(TECHNICAL_404_TEMPLATE, name='Technical 404 template')
c = Context({
'root_urlconf': settings.ROOT_URLCONF,
- 'request_path': request.path[1:], # Trim leading slash
+ 'request_path': request.path_info[1:], # Trim leading slash
'urlpatterns': tried,
- 'reason': str(exception),
+ 'reason': smart_str(exception, errors='replace'),
'request': request,
'request_protocol': request.is_secure() and "https" or "http",
'settings': get_safe_settings(),
@@ -199,47 +271,6 @@
})
return HttpResponse(t.render(c), mimetype='text/html')
-def _get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None):
- """
- Returns context_lines before and after lineno from file.
- Returns (pre_context_lineno, pre_context, context_line, post_context).
- """
- source = None
- if loader is not None and hasattr(loader, "get_source"):
- source = loader.get_source(module_name)
- if source is not None:
- source = source.splitlines()
- if source is None:
- try:
- f = open(filename)
- try:
- source = f.readlines()
- finally:
- f.close()
- except (OSError, IOError):
- pass
- if source is None:
- return None, [], None, []
-
- encoding = 'ascii'
- for line in source[:2]:
- # File coding may be specified. Match pattern from PEP-263
- # (http://www.python.org/dev/peps/pep-0263/)
- match = re.search(r'coding[:=]\s*([-\w.]+)', line)
- if match:
- encoding = match.group(1)
- break
- source = [unicode(sline, encoding, 'replace') for sline in source]
-
- 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
-
#
# Templates are embedded in the file so that we know the error handler will
# always work even if the template loader is broken.
@@ -251,7 +282,7 @@
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="robots" content="NONE,NOARCHIVE">
- <title>{{ exception_type }} at {{ request.path|escape }}</title>
+ <title>{{ exception_type }} at {{ request.path_info|escape }}</title>
<style type="text/css">
html * { padding:0; margin:0; }
body * { padding:10px 20px; }
@@ -297,6 +328,7 @@
.specific { color:#cc3300; font-weight:bold; }
h2 span.commands { font-size:.7em;}
span.commands a:link {color:#5E5694;}
+ pre.exception_value { font-family: sans-serif; color: #666; font-size: 1.5em; margin: 10px 0 10px 0; }
</style>
<script type="text/javascript">
//<!--
@@ -356,8 +388,8 @@
</head>
<body>
<div id="summary">
- <h1>{{ exception_type }} at {{ request.path|escape }}</h1>
- <h2>{{ exception_value|escape }}</h2>
+ <h1>{{ exception_type }} at {{ request.path_info|escape }}</h1>
+ <pre class="exception_value">{{ exception_value|escape }}</pre>
<table class="meta">
<tr>
<th>Request Method:</th>
@@ -365,7 +397,7 @@
</tr>
<tr>
<th>Request URL:</th>
- <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }}</td>
+ <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path_info|escape }}</td>
</tr>
<tr>
<th>Exception Type:</th>
@@ -373,7 +405,7 @@
</tr>
<tr>
<th>Exception Value:</th>
- <td>{{ exception_value|escape }}</td>
+ <td><pre>{{ exception_value|escape }}<pre></td>
</tr>
<tr>
<th>Exception Location:</th>
@@ -450,11 +482,11 @@
{% 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 %}{% if line %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endif %}{% endfor %}</ol>
+ <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ 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 }} <span>...</span></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 %}{% if line %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endif %}{% endfor %}</ol>
+ <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol>
{% endif %}
</div>
{% endif %}
@@ -488,14 +520,14 @@
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
<div id="pastebinTraceback" class="pastebin">
<input type="hidden" name="language" value="PythonConsole">
- <input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path|escape }}">
+ <input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path_info|escape }}">
<input type="hidden" name="source" value="Django Dpaste Agent">
<input type="hidden" name="poster" value="Django">
<textarea name="content" id="traceback_area" cols="140" rows="25">
Environment:
Request Method: {{ request.META.REQUEST_METHOD }}
-Request URL: {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }}
+Request URL: {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path_info|escape }}
Django Version: {{ django_version_info }}
Python Version: {{ sys_version_info }}
Installed Applications:
@@ -522,7 +554,7 @@
{% for frame in frames %}File "{{ frame.filename|escape }}" in {{ frame.function|escape }}
{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %}
{% endfor %}
-Exception Type: {{ exception_type|escape }} at {{ request.path|escape }}
+Exception Type: {{ exception_type|escape }} at {{ request.path_info|escape }}
Exception Value: {{ exception_value|escape }}
</textarea>
<br><br>
@@ -655,7 +687,7 @@
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
- <title>Page not found at {{ request.path|escape }}</title>
+ <title>Page not found at {{ request.path_info|escape }}</title>
<meta name="robots" content="NONE,NOARCHIVE">
<style type="text/css">
html * { padding:0; margin:0; }
@@ -685,7 +717,7 @@
</tr>
<tr>
<th>Request URL:</th>
- <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }}</td>
+ <td>{{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path_info|escape }}</td>
</tr>
</table>
</div>