Package wtf :: Package app :: Package services :: Module _crash_tb
[hide private]
[frames] | no frames]

Source Code for Module wtf.app.services._crash_tb

  1  # -*- coding: utf-8 -*- 
  2  """ 
  3  Debugging output heavily based on colubrid.debug 
  4  (http://trac.pocoo.org/repos/colubrid/trunk/colubrid/debug.py@2791) 
  5   
  6  colubrid.debug is copyright 2006 by Armin Ronacher, Benjamin Wiegand, 
  7  Georg Brandl and licensed under the `BSD License`_ 
  8   
  9  .. _BSD License: http://www.opensource.org/licenses/bsd-license.php 
 10  """ 
 11  __docformat__ = 'restructuredtext en' 
 12   
 13  try: 
 14      import cStringIO as _string_io 
 15  except ImportError: 
 16      import StringIO as _string_io 
 17  import inspect as _inspect 
 18  import keyword as _keyword 
 19  import os as _os 
 20  import pprint as _pprint 
 21  import re as _re 
 22  import sys as _sys 
 23  import token as _token 
 24  import tokenize as _tokenize 
 25  import traceback as _traceback 
 26  from xml.sax.saxutils import escape as _escape 
 27   
 28   
 29  JAVASCRIPT = r''' 
 30  function toggleBlock(handler) { 
 31      if (handler.nodeName == 'H3') { 
 32          var table = handler; 
 33          do { 
 34              table = table.nextSibling; 
 35              if (typeof table == 'undefined') { 
 36                  return; 
 37              } 
 38          } 
 39          while (table.nodeName != 'TABLE'); 
 40      } 
 41       
 42      else if (handler.nodeName == 'DT') { 
 43          var parent = handler.parentNode; 
 44          var table = parent.getElementsByTagName('TABLE')[0]; 
 45      } 
 46       
 47      var lines = table.getElementsByTagName("TR"); 
 48      for (var i = 0; i < lines.length; i++) { 
 49          var line = lines[i]; 
 50          if (line.className == 'pre' || line.className == 'post' || 
 51                  line.parentNode.parentNode.className == 'vars') { 
 52              line.style.display = (line.style.display == 'none') ? '' : 'none'; 
 53          } 
 54      } 
 55  } 
 56   
 57  function initTB() { 
 58      var tb = document.getElementById('wsgi-traceback'); 
 59      var handlers = tb.getElementsByTagName('H3'); 
 60      for (var i = 0; i < handlers.length; i++) { 
 61          toggleBlock(handlers[i]); 
 62          handlers[i].setAttribute('onclick', 'toggleBlock(this)'); 
 63      } 
 64      handlers = tb.getElementsByTagName('DT'); 
 65      for (var i = 0; i < handlers.length; i++) { 
 66          toggleBlock(handlers[i]); 
 67          handlers[i].setAttribute('onclick', 'toggleBlock(this)'); 
 68      } 
 69  } 
 70   
 71  function change_tb() { 
 72      interactive = document.getElementById('interactive'); 
 73      plain = document.getElementById('plain'); 
 74      interactive.style.display = ((interactive.style.display == 'block') | (interactive.style.display == '')) ? 'none' : 'block'; 
 75      plain.style.display = (plain.style.display == 'block') ? 'none' : 'block'; 
 76  } 
 77  ''' 
 78   
 79  STYLESHEET = ''' 
 80  body { 
 81    font-size:0.9em; 
 82    margin: 0; 
 83    padding: 1.3em; 
 84  } 
 85   
 86  * { 
 87    margin:0; 
 88    padding:0; 
 89  } 
 90   
 91  #wsgi-traceback { 
 92    margin: 1em; 
 93    border: 1px solid #5F9CC4; 
 94    background-color: #F6F6F6; 
 95  } 
 96   
 97  h1 { 
 98    background-color: #3F7CA4; 
 99    font-size:1.2em; 
100    color:#FFFFFF; 
101    padding:0.3em; 
102    margin:0 0 0.2em 0; 
103  } 
104   
105  h2 { 
106    background-color:#5F9CC4; 
107    font-size:1em; 
108    color:#FFFFFF; 
109    padding:0.3em; 
110    margin:0.4em 0 0.2em 0; 
111  } 
112   
113  h2.tb { 
114    cursor:pointer; 
115  } 
116   
117  h3 { 
118    font-size:1em; 
119    cursor:pointer; 
120  } 
121   
122  h3.fn { 
123    margin-top: 0.5em; 
124    padding: 0.3em; 
125  } 
126   
127  h3.fn:hover { 
128    color: #777; 
129  } 
130   
131  h3.indent { 
132    margin:0 0.7em 0 0.7em; 
133    font-weight:normal; 
134  } 
135   
136  p.text { 
137    padding:0.1em 0.5em 0.1em 0.5em; 
138  } 
139   
140  p.errormsg { 
141    padding:0.1em 0.5em 0.1em 0.5em; 
142  } 
143   
144  p.errorline { 
145    padding:0.1em 0.5em 0.1em 2em; 
146    font-size: 0.9em; 
147  } 
148   
149  div.frame { 
150    margin: 0 2em 0 1em; 
151  } 
152   
153  table.code { 
154    margin: 0.4em 0 0 0.5em; 
155    background-color:#E0E0E0; 
156    width:100%; 
157    font-family: monospace; 
158    font-size:13px; 
159    border:1px solid #C9C9C9; 
160    border-collapse:collapse; 
161  } 
162   
163  table.code td.lineno { 
164    width:42px; 
165    text-align:right; 
166    padding:0 5px 0 0; 
167    color:#444444; 
168    font-weight:bold; 
169    border-right:1px solid #888888; 
170  } 
171   
172  table.code td.code { 
173    background-color:#EFEFEF; 
174    padding:1px 0 1px 5px; 
175    white-space:pre; 
176  } 
177   
178  table.code tr.cur td.code { 
179    background-color: #fff; 
180    border-top: 1px solid #ccc; 
181    border-bottom: 1px solid #ccc; 
182    white-space: pre; 
183  } 
184   
185  pre.plain { 
186    margin:0.5em 1em 1em 1em; 
187    padding:0.5em; 
188    border:1px solid #999999; 
189    background-color: #FFFFFF; 
190    font-family: monospace; 
191    font-size: 13px; 
192  } 
193   
194  table.vars { 
195    margin:0 1.5em 0 1.5em; 
196    border-collapse:collapse; 
197    font-size: 0.9em; 
198  } 
199   
200  table.vars td { 
201    font-family: 'Bitstream Vera Sans Mono', 'Courier New', monospace; 
202    padding: 0.3em; 
203    border: 1px solid #ddd; 
204    vertical-align: top; 
205    background-color: white; 
206  } 
207   
208  table.vars .name { 
209    font-style: italic; 
210  } 
211   
212  table.vars .value { 
213    color: #555; 
214  } 
215   
216  table.vars th { 
217    padding: 0.2em; 
218    border: 1px solid #ddd; 
219    background-color: #f2f2f2; 
220    text-align: left; 
221  } 
222   
223  #plain { 
224    display: none; 
225  } 
226   
227  dl dt { 
228    padding: 0.2em 0 0.2em 1em; 
229    font-weight: bold; 
230    cursor: pointer; 
231    background-color: #ddd; 
232  } 
233   
234  dl dt:hover { 
235    background-color: #bbb; color: white; 
236  } 
237   
238  dl dd { 
239    padding: 0 0 0 2em; 
240    background-color: #eee; 
241  } 
242   
243  span.p-kw { 
244    font-weight: bold; 
245    color: #008800; 
246  } 
247   
248  span.p-cmt { 
249    color: #888888; 
250  } 
251   
252  span.p-str { 
253    color: #dd2200; 
254    background-color: #fff0f0; 
255  } 
256   
257  span.p-num { 
258    color: #0000DD; 
259    font-weight: bold; 
260  } 
261   
262  span.p-op { 
263    color: black; 
264  } 
265  ''' 
266   
267   
268 -def get_frame_info(tb, context_lines=7):
269 """ 270 Return a dict of information about a given traceback. 271 """ 272 # line numbers / function / variables 273 lineno = tb.tb_lineno 274 function = tb.tb_frame.f_code.co_name 275 variables = tb.tb_frame.f_locals 276 277 # get filename 278 fn = tb.tb_frame.f_globals.get('__file__') 279 if not fn: 280 fn = _os.path.realpath( 281 _inspect.getsourcefile(tb) or _inspect.getfile(tb) 282 ) 283 if fn[-4:] in ('.pyc', '.pyo'): 284 fn = fn[:-1] 285 286 # module name 287 modname = tb.tb_frame.f_globals.get('__name__') 288 289 # get loader 290 loader = tb.tb_frame.f_globals.get('__loader__') 291 292 # sourcecode 293 try: 294 if not loader is None: 295 source = loader.get_source(modname) 296 else: 297 source = file(fn).read() 298 except (SystemExit, KeyboardInterrupt): 299 raise 300 except: 301 source = '' 302 pre_context, post_context = [], [] 303 context_line, context_lineno = None, None 304 else: 305 parser = PythonParser(source) 306 parser.parse() 307 parsed_source = parser.get_html_output() 308 lbound = max(0, lineno - context_lines - 1) 309 ubound = lineno + context_lines 310 try: 311 context_line = parsed_source[lineno - 1] 312 pre_context = parsed_source[lbound:lineno - 1] 313 post_context = parsed_source[lineno:ubound] 314 except IndexError: 315 context_line = None 316 pre_context = post_context = [], [] 317 context_lineno = lbound 318 319 return { 320 'tb': tb, 321 'filename': fn, 322 'loader': loader, 323 'function': function, 324 'lineno': lineno, 325 'vars': variables, 326 'pre_context': pre_context, 327 'context_line': context_line, 328 'post_context': post_context, 329 'context_lineno': context_lineno, 330 'source': source 331 }
332 333
334 -def debug_context(exc_info):
335 exception_type, exception_value, tb = exc_info 336 # skip first internal frame 337 if tb.tb_next is not None: 338 tb = tb.tb_next 339 plaintb = ''.join(_traceback.format_exception(*exc_info)) 340 341 # load frames 342 frames = [] 343 344 # walk through frames and collect information 345 while tb is not None: 346 frames.append(get_frame_info(tb)) 347 tb = tb.tb_next 348 349 # guard for string exceptions 350 if isinstance(exception_type, str): 351 extypestr = "string exception" 352 exception_value = exception_type 353 elif exception_type.__module__ == "exceptions": 354 extypestr = exception_type.__name__ 355 else: 356 extypestr = str(exception_type) 357 358 return Namespace( 359 exception_type=extypestr, 360 exception_value=str(exception_value), 361 frames=frames, 362 last_frame=frames[-1], 363 plaintb=plaintb, 364 )
365 366
367 -def debug_info(environ, exc_info):
368 """ 369 Return debug info for the request 370 """ 371 context = debug_context(exc_info) 372 context.req_vars = sorted(environ.iteritems()) 373 return DebugRender(context).render()
374 375
376 -class Namespace(object):
377 - def __init__(self, **kwds):
378 self.__dict__.update(kwds)
379 380
381 -class PythonParser(object):
382 """ 383 Simple python sourcecode highlighter. 384 Usage:: 385 386 p = PythonParser(source) 387 p.parse() 388 for line in p.get_html_output(): 389 print line 390 """ 391 392 _KEYWORD = _token.NT_OFFSET + 1 393 _TEXT = _token.NT_OFFSET + 2 394 _classes = { 395 _token.NUMBER: 'num', 396 _token.OP: 'op', 397 _token.STRING: 'str', 398 _tokenize.COMMENT: 'cmt', 399 _token.NAME: 'id', 400 _token.ERRORTOKEN: 'error', 401 _KEYWORD: 'kw', 402 _TEXT: 'txt', 403 } 404
405 - def __init__(self, raw):
406 self.raw = raw.expandtabs(8).strip() 407 self.out = _string_io.StringIO()
408
409 - def parse(self):
410 self.lines = [0, 0] 411 pos = 0 412 while 1: 413 pos = self.raw.find('\n', pos) + 1 414 if not pos: break 415 self.lines.append(pos) 416 self.lines.append(len(self.raw)) 417 418 self.pos = 0 419 text = _string_io.StringIO(self.raw) 420 try: 421 _tokenize.tokenize(text.readline, self) 422 except _tokenize.TokenError: 423 pass
424
425 - def get_html_output(self):
426 """ Return line generator. """ 427 def html_splitlines(lines): 428 # this cool function was taken from trac. 429 # http://projects.edgewall.com/trac/ 430 open_tag_re = _re.compile(r'<(\w+)(\s.*)?[^/]?>') 431 close_tag_re = _re.compile(r'</(\w+)>') 432 open_tags = [] 433 for line in lines: 434 for tag in open_tags: 435 line = tag.group(0) + line 436 open_tags = [] 437 for tag in open_tag_re.finditer(line): 438 open_tags.append(tag) 439 open_tags.reverse() 440 for ctag in close_tag_re.finditer(line): 441 for otag in open_tags: 442 if otag.group(1) == ctag.group(1): 443 open_tags.remove(otag) 444 break 445 for tag in open_tags: 446 line += '</%s>' % tag.group(1) 447 yield line
448 449 return list(html_splitlines(self.out.getvalue().splitlines()))
450
451 - def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
452 oldpos = self.pos 453 newpos = self.lines[srow] + scol 454 self.pos = newpos + len(toktext) 455 456 if toktype in [_token.NEWLINE, _tokenize.NL]: 457 self.out.write('\n') 458 return 459 460 if newpos > oldpos: 461 self.out.write(self.raw[oldpos:newpos]) 462 463 if toktype in [_token.INDENT, _token.DEDENT]: 464 self.pos = newpos 465 return 466 467 if _token.LPAR <= toktype and toktype <= _token.OP: 468 toktype = _token.OP 469 elif toktype == _token.NAME and _keyword.iskeyword(toktext): 470 toktype = self._KEYWORD 471 clsname = self._classes.get(toktype, 'txt') 472 473 self.out.write('<span class="code-item p-%s">' % clsname) 474 self.out.write(_escape(toktext)) 475 self.out.write('</span>')
476 477
478 -class DebugRender(object):
479
480 - def __init__(self, context):
481 self.c = context
482
483 - def render(self):
484 return '\n'.join([ 485 self.header(), 486 self.traceback(), 487 self.request_information(), 488 self.footer() 489 ])
490
491 - def header(self):
492 data = [ 493 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' 494 '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">', 495 '<html xmlns="http://www.w3.org/1999/xhtml"><head>', 496 '<title>Python Traceback</title>', 497 '<script type="text/javascript">%s</script>' % JAVASCRIPT, 498 '<style type="text/css">%s</style>' % STYLESHEET, 499 '</head><body>', 500 '<div id="wsgi-traceback">' 501 ] 502 503 if hasattr(self.c, 'exception_type'): 504 title = _escape(self.c.exception_type) 505 exc = _escape(self.c.exception_value) 506 data += [ 507 '<h1>%s</h1>' % title, 508 '<p class="errormsg">%s</p>' % exc 509 ] 510 511 if hasattr(self.c, 'last_frame'): 512 data += [ 513 '<p class="errorline">%s in %s, line %s</p>' % ( 514 self.c.last_frame['filename'], self.c.last_frame['function'], 515 self.c.last_frame['lineno']) 516 ] 517 518 return '\n'.join(data)
519
520 - def render_code(self, frame):
521 def render_line(mode, lineno, code): 522 return ''.join([ 523 '<tr class="%s">' % mode, 524 '<td class="lineno">%i</td>' % lineno, 525 '<td class="code">%s</td></tr>' % code 526 ])
527 528 tmp = ['<table class="code">'] 529 lineno = frame['context_lineno'] 530 if not lineno is None: 531 lineno += 1 532 for l in frame['pre_context']: 533 tmp.append(render_line('pre', lineno, l)) 534 lineno += 1 535 tmp.append(render_line('cur', lineno, frame['context_line'])) 536 lineno += 1 537 for l in frame['post_context']: 538 tmp.append(render_line('post', lineno, l)) 539 lineno += 1 540 else: 541 tmp.append(render_line('cur', 1, 'Sourcecode not available')) 542 tmp.append('</table>') 543 544 return '\n'.join(tmp)
545
546 - def var_table(self, var):
547 # simple data types 548 if isinstance(var, basestring) or isinstance(var, float)\ 549 or isinstance(var, int) or isinstance(var, long): 550 return ('<table class="vars"><tr><td class="value">%r' 551 '</td></tr></table>' % _escape(repr(var))) 552 553 # dicts 554 if isinstance(var, dict) or hasattr(var, 'items'): 555 items = var.items() 556 items.sort() 557 558 # empty dict 559 if not items: 560 return ('<table class="vars"><tr><th>no data given' 561 '</th></tr></table>') 562 563 result = ['<table class="vars"><tr><th>Name' 564 '</th><th>Value</th></tr>'] 565 for key, value in items: 566 try: 567 val = _escape(_pprint.pformat(value)) 568 except (SystemExit, KeyboardInterrupt): 569 raise 570 except: 571 val = '?' 572 result.append('<tr><td class="name">%s</td><td class="value">%s' 573 '</td></tr>' % (_escape(repr(key)), val)) 574 result.append('</table>') 575 return '\n'.join(result) 576 577 # lists 578 if isinstance(var, list): 579 # empty list 580 if not var: 581 return ('<table class="vars"><tr><th>no data given' 582 '</th></tr></table>') 583 584 result = ['<table class="vars">'] 585 for line in var: 586 try: 587 val = _escape(_pprint.pformat(line)) 588 except (SystemExit, KeyboardInterrupt): 589 raise 590 except: 591 val = '?' 592 result.append('<tr><td class="value">%s</td></tr>' % (val)) 593 result.append('</table>') 594 return '\n'.join(result) 595 596 # unknown things 597 try: 598 value = _escape(repr(var)) 599 except (SystemExit, KeyboardInterrupt): 600 raise 601 except: 602 value = '?' 603 return '<table class="vars"><tr><th>%s</th></tr></table>' % value
604
605 - def traceback(self):
606 if not hasattr(self.c, 'frames'): 607 return '' 608 609 result = ['<h2 onclick="change_tb()" class="tb">Traceback (click to switch to raw view)</h2>'] 610 result.append('<div id="interactive"><p class="text">A problem occurred in your Python WSGI' 611 ' application. Here is the sequence of function calls leading up to' 612 ' the error, in the order they occurred. Click on a header to show' 613 ' context lines.</p>') 614 615 for num, frame in enumerate(self.c.frames): 616 line = [ 617 '<div class="frame" id="frame-%i">' % num, 618 '<h3 class="fn">%s in %s</h3>' % (frame['function'], 619 frame['filename']), 620 self.render_code(frame), 621 ] 622 623 if frame['vars']: 624 line.append('\n'.join([ 625 '<h3 class="indent">▸ local variables</h3>', 626 self.var_table(frame['vars']) 627 ])) 628 629 line.append('</div>') 630 result.append(''.join(line)) 631 result.append('\n'.join([ 632 '</div>', 633 self.plain() 634 ])) 635 return '\n'.join(result)
636
637 - def plain(self):
638 if not hasattr(self.c, 'plaintb'): 639 return '' 640 return ''' 641 <div id="plain"> 642 <p class="text">Here is the plain Python traceback for copy and paste:</p> 643 <pre class="plain">\n%s</pre> 644 </div> 645 ''' % self.c.plaintb
646
647 - def request_information(self):
648 result = [ 649 '<h2>Request Environment</h2>', 650 '<p class="text">The following list contains all environment', 651 'variables. Click on a header to expand the list.</p>' 652 ] 653 654 if not hasattr(self.c, 'frames'): 655 del result[0] 656 657 for key, info in self.c.req_vars: 658 result.append('<dl><dt>%s</dt><dd>%s</dd></dl>' % ( 659 _escape(key), self.var_table(info) 660 )) 661 662 return '\n'.join(result)
663
664 - def footer(self):
665 return '\n'.join([ 666 '<script type="text/javascript">initTB();</script>', 667 '</div>', 668 hasattr(self.c, 'plaintb') 669 and ('<!-- Plain traceback:\n\n%s-->' % self.c.plaintb) 670 or '', 671 '</body></html>', 672 ])
673