/web/debugerror.py
Python | 355 lines | 343 code | 3 blank | 9 comment | 0 complexity | b1c3d68cf5a600854c15e6aab7e56c5f MD5 | raw file
- """
- pretty debug errors
- (part of web.py)
- portions adapted from Django <djangoproject.com>
- Copyright (c) 2005, the Lawrence Journal-World
- Used under the modified BSD license:
- http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
- """
- __all__ = ["debugerror", "djangoerror", "emailerrors"]
- import sys, urlparse, pprint, traceback
- from net import websafe
- from template import Template
- from utils import sendmail
- import webapi as web
- import os, os.path
- whereami = os.path.join(os.getcwd(), __file__)
- whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1])
- djangoerror_t = """\
- $def with (exception_type, exception_value, frames)
- <!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" />
- <meta name="robots" content="NONE,NOARCHIVE" />
- <title>$exception_type at $ctx.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; 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 { 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; }
- 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; }
- #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; }
- .error { background: #ffc; }
- .specific { color:#cc3300; font-weight:bold; }
- </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(0x25b6);
- var darr = String.fromCharCode(0x25bc);
- s.innerHTML = s.innerHTML == uarr ? darr : uarr;
- return false;
- }
- //-->
- </script>
- </head>
- <body>
- $def dicttable (d, kls='req', id=None):
- $ items = d and d.items() or []
- $items.sort()
- $:dicttable_items(items, kls, id)
-
- $def dicttable_items(items, kls='req', id=None):
- $if items:
- <table class="$kls"
- $if id: id="$id"
- ><thead><tr><th>Variable</th><th>Value</th></tr></thead>
- <tbody>
- $for k, v in items:
- <tr><td>$k</td><td class="code"><div>$prettify(v)</div></td></tr>
- </tbody>
- </table>
- $else:
- <p>No data.</p>
- <div id="summary">
- <h1>$exception_type at $ctx.path</h1>
- <h2>$exception_value</h2>
- <table><tr>
- <th>Python</th>
- <td>$frames[0].filename in $frames[0].function, line $frames[0].lineno</td>
- </tr><tr>
- <th>Web</th>
- <td>$ctx.method $ctx.home$ctx.path</td>
- </tr></table>
- </div>
- <div id="traceback">
- <h2>Traceback <span>(innermost first)</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 onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
- </ol>
- <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 + 1}' class="post-context" id="post$frame.id">
- $for line in frame.post_context:
- <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
- </ol>
- </div>
-
- $if frame.vars:
- <div class="commands">
- <a href='#' onclick="return varToggle(this, '$frame.id')"><span>▶</span> Local vars</a>
- $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame))
- </div>
- $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id)))
- </li>
- </ul>
- </div>
- <div id="requestinfo">
- $if ctx.output or ctx.headers:
- <h2>Response so far</h2>
- <h3>HEADERS</h3>
- $:dicttable_items(ctx.headers)
- <h3>BODY</h3>
- <p class="req" style="padding-bottom: 2em"><code>
- $ctx.output
- </code></p>
-
- <h2>Request information</h2>
- <h3>INPUT</h3>
- $:dicttable(web.input())
- <h3 id="cookie-info">COOKIES</h3>
- $:dicttable(web.cookies())
- <h3 id="meta-info">META</h3>
- $ newctx = [(k, v) for (k, v) in ctx.iteritems() if not k.startswith('_') and not isinstance(v, dict)]
- $:dicttable(dict(newctx))
- <h3 id="meta-info">ENVIRONMENT</h3>
- $:dicttable(ctx.env)
- </div>
- <div id="explanation">
- <p>
- You're seeing this error because you have <code>web.config.debug</code>
- set to <code>True</code>. Set that to <code>False</code> if you don't to see this.
- </p>
- </div>
- </body>
- </html>
- """
- djangoerror_r = None
- def djangoerror():
- 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, []
-
- exception_type, exception_value, tback = sys.exc_info()
- frames = []
- while tback is not None:
- filename = tback.tb_frame.f_code.co_filename
- function = tback.tb_frame.f_code.co_name
- lineno = tback.tb_lineno - 1
- pre_context_lineno, pre_context, context_line, post_context = \
- _get_lines_from_file(filename, lineno, 7)
- frames.append(web.storage({
- 'tback': tback,
- 'filename': filename,
- 'function': function,
- 'lineno': lineno,
- 'vars': tback.tb_frame.f_locals,
- 'id': id(tback),
- 'pre_context': pre_context,
- 'context_line': context_line,
- 'post_context': post_context,
- 'pre_context_lineno': pre_context_lineno,
- }))
- tback = tback.tb_next
- frames.reverse()
- urljoin = urlparse.urljoin
- def prettify(x):
- try:
- out = pprint.pformat(x)
- except Exception, e:
- out = '[could not display: <' + e.__class__.__name__ + \
- ': '+str(e)+'>]'
- return out
-
- global djangoerror_r
- if djangoerror_r is None:
- djangoerror_r = Template(djangoerror_t, filename=__file__, filter=websafe)
-
- t = djangoerror_r
- globals = {'ctx': web.ctx, 'web':web, 'dict':dict, 'str':str, 'prettify': prettify}
- t.t.func_globals.update(globals)
- return t(exception_type, exception_value, frames)
- def debugerror():
- """
- A replacement for `internalerror` that presents a nice page with lots
- of debug information for the programmer.
- (Based on the beautiful 500 page from [Django](http://djangoproject.com/),
- designed by [Wilson Miner](http://wilsonminer.com/).)
- """
- return web._InternalError(djangoerror())
- def emailerrors(email_address, olderror):
- """
- Wraps the old `internalerror` handler (pass as `olderror`) to
- additionally email all errors to `email_address`, to aid in
- debugging production websites.
-
- Emails contain a normal text traceback as well as an
- attachment containing the nice `debugerror` page.
- """
- def emailerrors_internal():
- error = olderror()
- tb = sys.exc_info()
- error_name = tb[0]
- error_value = tb[1]
- tb_txt = ''.join(traceback.format_exception(*tb))
- path = web.ctx.path
- request = web.ctx.method+' '+web.ctx.home+web.ctx.fullpath
- eaddr = email_address
- text = ("""\
- ------here----
- Content-Type: text/plain
- Content-Disposition: inline
- %(request)s
- %(tb_txt)s
- ------here----
- Content-Type: text/html; name="bug.html"
- Content-Disposition: attachment; filename="bug.html"
- """ % locals()) + str(djangoerror())
- sendmail(
- "your buggy site <%s>" % eaddr,
- "the bugfixer <%s>" % eaddr,
- "bug: %(error_name)s: %(error_value)s (%(path)s)" % locals(),
- text,
- headers={'Content-Type': 'multipart/mixed; boundary="----here----"'})
- return error
-
- return emailerrors_internal
- if __name__ == "__main__":
- urls = (
- '/', 'index'
- )
- from application import application
- app = application(urls, globals())
- app.internalerror = debugerror
-
- class index:
- def GET(self):
- thisdoesnotexist
- app.run()