OpenSecurity/install/web.py-0.37/build/lib/web/debugerror.py
author om
Mon, 02 Dec 2013 14:02:05 +0100
changeset 3 65432e6c6042
permissions -rwxr-xr-x
initial deployment and project layout commit
     1 """
     2 pretty debug errors
     3 (part of web.py)
     4 
     5 portions adapted from Django <djangoproject.com> 
     6 Copyright (c) 2005, the Lawrence Journal-World
     7 Used under the modified BSD license:
     8 http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
     9 """
    10 
    11 __all__ = ["debugerror", "djangoerror", "emailerrors"]
    12 
    13 import sys, urlparse, pprint, traceback
    14 from template import Template
    15 from net import websafe
    16 from utils import sendmail, safestr
    17 import webapi as web
    18 
    19 import os, os.path
    20 whereami = os.path.join(os.getcwd(), __file__)
    21 whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1])
    22 djangoerror_t = """\
    23 $def with (exception_type, exception_value, frames)
    24 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    25 <html lang="en">
    26 <head>
    27   <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    28   <meta name="robots" content="NONE,NOARCHIVE" />
    29   <title>$exception_type at $ctx.path</title>
    30   <style type="text/css">
    31     html * { padding:0; margin:0; }
    32     body * { padding:10px 20px; }
    33     body * * { padding:0; }
    34     body { font:small sans-serif; }
    35     body>div { border-bottom:1px solid #ddd; }
    36     h1 { font-weight:normal; }
    37     h2 { margin-bottom:.8em; }
    38     h2 span { font-size:80%; color:#666; font-weight:normal; }
    39     h3 { margin:1em 0 .5em 0; }
    40     h4 { margin:0 0 .5em 0; font-weight: normal; }
    41     table { 
    42         border:1px solid #ccc; border-collapse: collapse; background:white; }
    43     tbody td, tbody th { vertical-align:top; padding:2px 3px; }
    44     thead th { 
    45         padding:1px 6px 1px 3px; background:#fefefe; text-align:left; 
    46         font-weight:normal; font-size:11px; border:1px solid #ddd; }
    47     tbody th { text-align:right; color:#666; padding-right:.5em; }
    48     table.vars { margin:5px 0 2px 40px; }
    49     table.vars td, table.req td { font-family:monospace; }
    50     table td.code { width:100%;}
    51     table td.code div { overflow:hidden; }
    52     table.source th { color:#666; }
    53     table.source td { 
    54         font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
    55     ul.traceback { list-style-type:none; }
    56     ul.traceback li.frame { margin-bottom:1em; }
    57     div.context { margin: 10px 0; }
    58     div.context ol { 
    59         padding-left:30px; margin:0 10px; list-style-position: inside; }
    60     div.context ol li { 
    61         font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
    62     div.context ol.context-line li { color:black; background-color:#ccc; }
    63     div.context ol.context-line li span { float: right; }
    64     div.commands { margin-left: 40px; }
    65     div.commands a { color:black; text-decoration:none; }
    66     #summary { background: #ffc; }
    67     #summary h2 { font-weight: normal; color: #666; }
    68     #explanation { background:#eee; }
    69     #template, #template-not-exist { background:#f6f6f6; }
    70     #template-not-exist ul { margin: 0 0 0 20px; }
    71     #traceback { background:#eee; }
    72     #requestinfo { background:#f6f6f6; padding-left:120px; }
    73     #summary table { border:none; background:transparent; }
    74     #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
    75     #requestinfo h3 { margin-bottom:-1em; }
    76     .error { background: #ffc; }
    77     .specific { color:#cc3300; font-weight:bold; }
    78   </style>
    79   <script type="text/javascript">
    80   //<!--
    81     function getElementsByClassName(oElm, strTagName, strClassName){
    82         // Written by Jonathan Snook, http://www.snook.ca/jon; 
    83         // Add-ons by Robert Nyman, http://www.robertnyman.com
    84         var arrElements = (strTagName == "*" && document.all)? document.all :
    85         oElm.getElementsByTagName(strTagName);
    86         var arrReturnElements = new Array();
    87         strClassName = strClassName.replace(/\-/g, "\\-");
    88         var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
    89         var oElement;
    90         for(var i=0; i<arrElements.length; i++){
    91             oElement = arrElements[i];
    92             if(oRegExp.test(oElement.className)){
    93                 arrReturnElements.push(oElement);
    94             }
    95         }
    96         return (arrReturnElements)
    97     }
    98     function hideAll(elems) {
    99       for (var e = 0; e < elems.length; e++) {
   100         elems[e].style.display = 'none';
   101       }
   102     }
   103     window.onload = function() {
   104       hideAll(getElementsByClassName(document, 'table', 'vars'));
   105       hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
   106       hideAll(getElementsByClassName(document, 'ol', 'post-context'));
   107     }
   108     function toggle() {
   109       for (var i = 0; i < arguments.length; i++) {
   110         var e = document.getElementById(arguments[i]);
   111         if (e) {
   112           e.style.display = e.style.display == 'none' ? 'block' : 'none';
   113         }
   114       }
   115       return false;
   116     }
   117     function varToggle(link, id) {
   118       toggle('v' + id);
   119       var s = link.getElementsByTagName('span')[0];
   120       var uarr = String.fromCharCode(0x25b6);
   121       var darr = String.fromCharCode(0x25bc);
   122       s.innerHTML = s.innerHTML == uarr ? darr : uarr;
   123       return false;
   124     }
   125     //-->
   126   </script>
   127 </head>
   128 <body>
   129 
   130 $def dicttable (d, kls='req', id=None):
   131     $ items = d and d.items() or []
   132     $items.sort()
   133     $:dicttable_items(items, kls, id)
   134         
   135 $def dicttable_items(items, kls='req', id=None):
   136     $if items:
   137         <table class="$kls"
   138         $if id: id="$id"
   139         ><thead><tr><th>Variable</th><th>Value</th></tr></thead>
   140         <tbody>
   141         $for k, v in items:
   142             <tr><td>$k</td><td class="code"><div>$prettify(v)</div></td></tr>
   143         </tbody>
   144         </table>
   145     $else:
   146         <p>No data.</p>
   147 
   148 <div id="summary">
   149   <h1>$exception_type at $ctx.path</h1>
   150   <h2>$exception_value</h2>
   151   <table><tr>
   152     <th>Python</th>
   153     <td>$frames[0].filename in $frames[0].function, line $frames[0].lineno</td>
   154   </tr><tr>
   155     <th>Web</th>
   156     <td>$ctx.method $ctx.home$ctx.path</td>
   157   </tr></table>
   158 </div>
   159 <div id="traceback">
   160 <h2>Traceback <span>(innermost first)</span></h2>
   161 <ul class="traceback">
   162 $for frame in frames:
   163     <li class="frame">
   164     <code>$frame.filename</code> in <code>$frame.function</code>
   165     $if frame.context_line is not None:
   166         <div class="context" id="c$frame.id">
   167         $if frame.pre_context:
   168             <ol start="$frame.pre_context_lineno" class="pre-context" id="pre$frame.id">
   169             $for line in frame.pre_context:
   170                 <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
   171             </ol>
   172             <ol start="$frame.lineno" class="context-line"><li onclick="toggle('pre$frame.id', 'post$frame.id')">$frame.context_line <span>...</span></li></ol>
   173         $if frame.post_context:
   174             <ol start='${frame.lineno + 1}' class="post-context" id="post$frame.id">
   175             $for line in frame.post_context:
   176                 <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
   177             </ol>
   178       </div>
   179     
   180     $if frame.vars:
   181         <div class="commands">
   182         <a href='#' onclick="return varToggle(this, '$frame.id')"><span>&#x25b6;</span> Local vars</a>
   183         $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame))
   184         </div>
   185         $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id)))
   186       </li>
   187   </ul>
   188 </div>
   189 
   190 <div id="requestinfo">
   191 $if ctx.output or ctx.headers:
   192     <h2>Response so far</h2>
   193     <h3>HEADERS</h3>
   194     $:dicttable_items(ctx.headers)
   195 
   196     <h3>BODY</h3>
   197     <p class="req" style="padding-bottom: 2em"><code>
   198     $ctx.output
   199     </code></p>
   200   
   201 <h2>Request information</h2>
   202 
   203 <h3>INPUT</h3>
   204 $:dicttable(web.input(_unicode=False))
   205 
   206 <h3 id="cookie-info">COOKIES</h3>
   207 $:dicttable(web.cookies())
   208 
   209 <h3 id="meta-info">META</h3>
   210 $ newctx = [(k, v) for (k, v) in ctx.iteritems() if not k.startswith('_') and not isinstance(v, dict)]
   211 $:dicttable(dict(newctx))
   212 
   213 <h3 id="meta-info">ENVIRONMENT</h3>
   214 $:dicttable(ctx.env)
   215 </div>
   216 
   217 <div id="explanation">
   218   <p>
   219     You're seeing this error because you have <code>web.config.debug</code>
   220     set to <code>True</code>. Set that to <code>False</code> if you don't want to see this.
   221   </p>
   222 </div>
   223 
   224 </body>
   225 </html>
   226 """
   227 
   228 djangoerror_r = None
   229 
   230 def djangoerror():
   231     def _get_lines_from_file(filename, lineno, context_lines):
   232         """
   233         Returns context_lines before and after lineno from file.
   234         Returns (pre_context_lineno, pre_context, context_line, post_context).
   235         """
   236         try:
   237             source = open(filename).readlines()
   238             lower_bound = max(0, lineno - context_lines)
   239             upper_bound = lineno + context_lines
   240 
   241             pre_context = \
   242                 [line.strip('\n') for line in source[lower_bound:lineno]]
   243             context_line = source[lineno].strip('\n')
   244             post_context = \
   245                 [line.strip('\n') for line in source[lineno + 1:upper_bound]]
   246 
   247             return lower_bound, pre_context, context_line, post_context
   248         except (OSError, IOError, IndexError):
   249             return None, [], None, []    
   250     
   251     exception_type, exception_value, tback = sys.exc_info()
   252     frames = []
   253     while tback is not None:
   254         filename = tback.tb_frame.f_code.co_filename
   255         function = tback.tb_frame.f_code.co_name
   256         lineno = tback.tb_lineno - 1
   257 
   258         # hack to get correct line number for templates
   259         lineno += tback.tb_frame.f_locals.get("__lineoffset__", 0)
   260         
   261         pre_context_lineno, pre_context, context_line, post_context = \
   262             _get_lines_from_file(filename, lineno, 7)
   263 
   264         if '__hidetraceback__' not in tback.tb_frame.f_locals:
   265             frames.append(web.storage({
   266                 'tback': tback,
   267                 'filename': filename,
   268                 'function': function,
   269                 'lineno': lineno,
   270                 'vars': tback.tb_frame.f_locals,
   271                 'id': id(tback),
   272                 'pre_context': pre_context,
   273                 'context_line': context_line,
   274                 'post_context': post_context,
   275                 'pre_context_lineno': pre_context_lineno,
   276             }))
   277         tback = tback.tb_next
   278     frames.reverse()
   279     urljoin = urlparse.urljoin
   280     def prettify(x):
   281         try: 
   282             out = pprint.pformat(x)
   283         except Exception, e: 
   284             out = '[could not display: <' + e.__class__.__name__ + \
   285                   ': '+str(e)+'>]'
   286         return out
   287         
   288     global djangoerror_r
   289     if djangoerror_r is None:
   290         djangoerror_r = Template(djangoerror_t, filename=__file__, filter=websafe)
   291         
   292     t = djangoerror_r
   293     globals = {'ctx': web.ctx, 'web':web, 'dict':dict, 'str':str, 'prettify': prettify}
   294     t.t.func_globals.update(globals)
   295     return t(exception_type, exception_value, frames)
   296 
   297 def debugerror():
   298     """
   299     A replacement for `internalerror` that presents a nice page with lots
   300     of debug information for the programmer.
   301 
   302     (Based on the beautiful 500 page from [Django](http://djangoproject.com/), 
   303     designed by [Wilson Miner](http://wilsonminer.com/).)
   304     """
   305     return web._InternalError(djangoerror())
   306 
   307 def emailerrors(to_address, olderror, from_address=None):
   308     """
   309     Wraps the old `internalerror` handler (pass as `olderror`) to 
   310     additionally email all errors to `to_address`, to aid in
   311     debugging production websites.
   312     
   313     Emails contain a normal text traceback as well as an
   314     attachment containing the nice `debugerror` page.
   315     """
   316     from_address = from_address or to_address
   317 
   318     def emailerrors_internal():
   319         error = olderror()
   320         tb = sys.exc_info()
   321         error_name = tb[0]
   322         error_value = tb[1]
   323         tb_txt = ''.join(traceback.format_exception(*tb))
   324         path = web.ctx.path
   325         request = web.ctx.method + ' ' + web.ctx.home + web.ctx.fullpath
   326         
   327         message = "\n%s\n\n%s\n\n" % (request, tb_txt)
   328         
   329         sendmail(
   330             "your buggy site <%s>" % from_address,
   331             "the bugfixer <%s>" % to_address,
   332             "bug: %(error_name)s: %(error_value)s (%(path)s)" % locals(),
   333             message,
   334             attachments=[
   335                 dict(filename="bug.html", content=safestr(djangoerror()))
   336             ],
   337         )
   338         return error
   339     
   340     return emailerrors_internal
   341 
   342 if __name__ == "__main__":
   343     urls = (
   344         '/', 'index'
   345     )
   346     from application import application
   347     app = application(urls, globals())
   348     app.internalerror = debugerror
   349     
   350     class index:
   351         def GET(self):
   352             thisdoesnotexist
   353 
   354     app.run()