Skip to content

Instantly share code, notes, and snippets.

@apit
Created January 23, 2010 17:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save apit/284705 to your computer and use it in GitHub Desktop.
Save apit/284705 to your computer and use it in GitHub Desktop.
"""
Usage:
import debug
class BaseHandler(tornado.web.RequestHandler):
def get_error_html(self, status_code, **kwargs):
if self.application.settings['debug']:
return debug.display(self, kwargs['exception'])
"""
import datetime
import pprint
import re
import sys
import traceback
import tornado.template
from tornado.web import _O, RequestHandler
# Taken from django/views/debug.py
class ExceptionReporter:
def __init__(self, handler, exc_type, exc_value, tb):
self.exc_type = exc_type
self.exc_value = exc_value
self.tb = tb
self.handler = handler
self.template_path = handler.application.settings.get("template_path")
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:
vars = tb.tb_frame.f_locals.items()
for v in vars:
if v[0] == 'formatted_code':
vars = [v]
break
frames.append(_O({
'tb': tb,
'filename': filename,
'function': function,
'lineno': lineno + 1,
'vars': vars,
'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 = [_O({
'filename': '<unknown>',
'function': '?',
'lineno': '?',
'context_line': '???',
})]
return frames
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:
if filename.endswith('.html'):
t = RequestHandler._templates[self.template_path].load(filename)
source = t.code.rstrip().splitlines()
else:
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 display(handler, e):
attrs = ("protocol", "host", "method", "uri", "version", "remote_ip",
"remote_ip", "body")
req = dict([(n, getattr(handler.request, n)) for n in attrs])
args = dict([(k, handler.get_argument(k)) \
for k,_v in handler.request.arguments.iteritems() \
if not k.startswith('_')])
headers = dict(handler.request.headers)
files = dict([(filename, (f[0]['filename'], f[0]['content_type'])) \
for filename, f in handler.request.files])
frames = ExceptionReporter(handler, *sys.exc_info()).get_traceback_frames()
t = tornado.template.Template(TECHNICAL_500_TEMPLATE)
return t.generate(handler=handler,
e=e,
frames=frames,
sys_executable=sys.executable,
sys_version_info='%d.%d.%d' % sys.version_info[0:3],
server_time=datetime.datetime.now(),
sys_path=sys.path,
req=req,
args=args,
files=files,
headers=headers,
pprint=pprint)
TECHNICAL_500_TEMPLATE = """
<head>
<title>{{ e }}</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; }
h1, h2 { margin-bottom:.8em; }
h2 span, h1 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%; white-space: pre;}
table td.code div { overflow:hidden; }
table.source th { color:#666; }
table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
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; cursor:pointer; }
div.context ol.context-line li { color:black; background-color:#ccc; }
div.context ol.context-line li span { float: right; }
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; }
#template, #template-not-exist { background:#f6f6f6; }
#template-not-exist ul { margin: 0 0 0 20px; }
#unicode-hint { background:#eee; }
#traceback { background:#eee; }
#requestinfo { background:#f6f6f6;}
#summary table { border:none; background:transparent; }
.error { background: #ffc; }
.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">
//<!--
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'));
hideAll(getElementsByClassName(document, 'div', 'pastebin'));
}
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(0x25b6);
var darr = String.fromCharCode(0x25bc);
s.innerHTML = s.innerHTML == uarr ? darr : uarr;
return false;
}
function switchPastebinFriendly(link) {
s1 = "Switch to copy-and-paste view";
s2 = "Switch back to interactive view";
link.innerHTML = link.innerHTML == s1 ? s2 : s1;
toggle('browserTraceback', 'pastebinTraceback');
return false;
}
//-->
</script>
</head>
<body>
<div id="summary">
<h1>{{ e }} <span>at {{ frames[-1:][0].filename }}</span></h1>
<table class="meta">
<tr>
<th>Python Executable:</th>
<td>{{ escape(sys_executable) }}</td>
</tr>
<tr>
<th>Python Version:</th>
<td>{{ sys_version_info }}</td>
</tr>
<!-- <tr>
<th>Python Path:</th>
<td>{{ sys_path }}</td>
</tr>-->
<tr>
<th>Server time:</th>
<td>{{server_time}}</td>
</tr>
</table>
</div>
<div id="traceback">
<h2>Traceback <span class="commands"><a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></span></h2>
<div id="browserTraceback">
<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 onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ escape(line) }}</li>
{% end %}</ol>
{% end %}
<ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line}} <span>...</span></li></ol>
{% if frame.post_context %}
<ol start='{{ frame.lineno }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ escape(line) }}</li>{% end %}</ol>
{% end %}
</div>
{% end %}
{% if frame.vars %}
<div class="commands">
<a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>&#x25b6;</span> Local vars</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 %}
<tr>
<td>{{ var[0]}}</td>
{% if var[0] == 'formatted_code' %}
<td class="code"><div>{{ escape(var[1])}}</div></td>
{% else %}
<td class="code"><div>{{ escape(pprint.pformat(var[1]))}}</div></td>
{% end %}
</tr>
{% end %}
</tbody>
</table>
{% end %}
</li>
{% end %}
</ul>
</div>
<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="{{ e }} at {{ handler.request.uri }}">
<input type="hidden" name="source" value="Tornado Dpaste Agent">
<input type="hidden" name="poster" value="Tornado">
<textarea name="content" id="traceback_area" cols="140" rows="15">
Traceback:
{% for frame in frames %}File "{{ escape(frame.filename) }}" in {{ escape(frame.function) }}
{% if frame.context_line %} {{ frame.lineno }}. {{ escape(frame.context_line) }}{% end %}
{% end %}
</textarea>
<br><br>
<input type="submit" value="Share this traceback on a public Web site">
</div>
</form>
</div>
<div id="requestinfo">
<h2>Request Info</h2>
<table class="req">
<thead>
<tr><th width="200">Header</th><th>Value</th></tr>
</thead>
<tbody>
{% for k, v in req.iteritems() %}
<tr><td>{{ k }}</td><td>{{ escape(v) }}</td></tr>
{% end %}
<tr><td>&nbsp;</td><td>&nbsp;</td></tr>
{% for k, v in args.iteritems() %}
<tr><td>{{ k }}</td><td>{{ escape(pprint.pformat(v)) }}</td></tr>
{% end %}
</tbody>
</table>
</div>
<div id="headers">
<h2>Headers</h2>
<table class="req">
<thead>
<tr><th width="200">Header</th><th>Value</th></tr>
</thead>
<tbody>
{% for k, v in headers.iteritems() %}
<tr><td>{{ k }}</td><td>{{ v }}</td></tr>
{% end %}
</tbody>
</table>
</div>
<div id="explanation">
<p>
You're seeing this error because you have <code>DEBUG = True</code> in your
settings file. Change that to <code>False</code>, and Tornado will
display a standard 500 page.
</p>
</div>
</body>
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment