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