web.web.debugerror'

Package web :: Package web :: Module debugerror' |
[hide private]
[frames] | no frames]

# Source Code for Module web.web.debugerror'

      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 net import websafe
     15  from template import Template
     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 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()
    355