PageRenderTime 88ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/web/debugerror.py

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