| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.web.test.test_web -*- | |
| 2 | |
| 3 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 4 # See LICENSE for details. | |
| 5 | |
| 6 | |
| 7 from cStringIO import StringIO | |
| 8 | |
| 9 from twisted.python import failure | |
| 10 | |
| 11 import html | |
| 12 import resource | |
| 13 | |
| 14 | |
| 15 import linecache | |
| 16 import string, re | |
| 17 import types | |
| 18 | |
| 19 | |
| 20 def redirectTo(URL, request): | |
| 21 request.redirect(URL) | |
| 22 return """ | |
| 23 <html> | |
| 24 <head> | |
| 25 <meta http-equiv=\"refresh\" content=\"0;URL=%(url)s\"> | |
| 26 </head> | |
| 27 <body bgcolor=\"#FFFFFF\" text=\"#000000\"> | |
| 28 <a href=\"%(url)s\">click here</a> | |
| 29 </body> | |
| 30 </html> | |
| 31 """ % {'url': URL} | |
| 32 | |
| 33 class Redirect(resource.Resource): | |
| 34 | |
| 35 isLeaf = 1 | |
| 36 | |
| 37 def __init__(self, url): | |
| 38 resource.Resource.__init__(self) | |
| 39 self.url = url | |
| 40 | |
| 41 def render(self, request): | |
| 42 return redirectTo(self.url, request) | |
| 43 | |
| 44 def getChild(self, name, request): | |
| 45 return self | |
| 46 | |
| 47 class ChildRedirector(Redirect): | |
| 48 isLeaf = 0 | |
| 49 def __init__(self, url): | |
| 50 # XXX is this enough? | |
| 51 if ((url.find('://') == -1) | |
| 52 and (not url.startswith('..')) | |
| 53 and (not url.startswith('/'))): | |
| 54 raise ValueError("It seems you've given me a redirect (%s) that is a
child of myself! That's not good, it'll cause an infinite redirect." % url) | |
| 55 Redirect.__init__(self, url) | |
| 56 | |
| 57 def getChild(self, name, request): | |
| 58 newUrl = self.url | |
| 59 if not newUrl.endswith('/'): | |
| 60 newUrl += '/' | |
| 61 newUrl += name | |
| 62 return ChildRedirector(newUrl) | |
| 63 | |
| 64 | |
| 65 from twisted.python import urlpath | |
| 66 | |
| 67 class ParentRedirect(resource.Resource): | |
| 68 """ | |
| 69 I redirect to URLPath.here(). | |
| 70 """ | |
| 71 isLeaf = 1 | |
| 72 def render(self, request): | |
| 73 return redirectTo(urlpath.URLPath.fromRequest(request).here(), request) | |
| 74 | |
| 75 def getChild(self, request): | |
| 76 return self | |
| 77 | |
| 78 | |
| 79 class DeferredResource(resource.Resource): | |
| 80 """ | |
| 81 I wrap up a Deferred that will eventually result in a Resource | |
| 82 object. | |
| 83 """ | |
| 84 isLeaf = 1 | |
| 85 | |
| 86 def __init__(self, d): | |
| 87 resource.Resource.__init__(self) | |
| 88 self.d = d | |
| 89 | |
| 90 def getChild(self, name, request): | |
| 91 return self | |
| 92 | |
| 93 def render(self, request): | |
| 94 self.d.addCallback(self._cbChild, request).addErrback( | |
| 95 self._ebChild,request) | |
| 96 from twisted.web.server import NOT_DONE_YET | |
| 97 return NOT_DONE_YET | |
| 98 | |
| 99 def _cbChild(self, child, request): | |
| 100 result = resource.getChildForRequest(child, request).render(request) | |
| 101 from twisted.web.server import NOT_DONE_YET | |
| 102 if result == NOT_DONE_YET: | |
| 103 return | |
| 104 else: | |
| 105 request.write(result) | |
| 106 request.finish() | |
| 107 | |
| 108 def _ebChild(self, reason, request): | |
| 109 request.processingFailed(reason) | |
| 110 return reason | |
| 111 | |
| 112 | |
| 113 stylesheet = """ | |
| 114 <style type="text/css"> | |
| 115 p.error { | |
| 116 color: red; | |
| 117 font-family: Verdana, Arial, helvetica, sans-serif; | |
| 118 font-weight: bold; | |
| 119 } | |
| 120 | |
| 121 div { | |
| 122 font-family: Verdana, Arial, helvetica, sans-serif; | |
| 123 } | |
| 124 | |
| 125 div.stackTrace { | |
| 126 } | |
| 127 | |
| 128 div.frame { | |
| 129 padding: 1em; | |
| 130 background: white; | |
| 131 border-bottom: thin black dashed; | |
| 132 } | |
| 133 | |
| 134 div.firstFrame { | |
| 135 padding: 1em; | |
| 136 background: white; | |
| 137 border-top: thin black dashed; | |
| 138 border-bottom: thin black dashed; | |
| 139 } | |
| 140 | |
| 141 div.location { | |
| 142 } | |
| 143 | |
| 144 div.snippet { | |
| 145 margin-bottom: 0.5em; | |
| 146 margin-left: 1em; | |
| 147 background: #FFFFDD; | |
| 148 } | |
| 149 | |
| 150 div.snippetHighlightLine { | |
| 151 color: red; | |
| 152 } | |
| 153 | |
| 154 span.code { | |
| 155 font-family: "Courier New", courier, monotype; | |
| 156 } | |
| 157 | |
| 158 span.function { | |
| 159 font-weight: bold; | |
| 160 font-family: "Courier New", courier, monotype; | |
| 161 } | |
| 162 | |
| 163 table.variables { | |
| 164 border-collapse: collapse; | |
| 165 margin-left: 1em; | |
| 166 } | |
| 167 | |
| 168 td.varName { | |
| 169 vertical-align: top; | |
| 170 font-weight: bold; | |
| 171 padding-left: 0.5em; | |
| 172 padding-right: 0.5em; | |
| 173 } | |
| 174 | |
| 175 td.varValue { | |
| 176 padding-left: 0.5em; | |
| 177 padding-right: 0.5em; | |
| 178 } | |
| 179 | |
| 180 div.variables { | |
| 181 margin-bottom: 0.5em; | |
| 182 } | |
| 183 | |
| 184 span.heading { | |
| 185 font-weight: bold; | |
| 186 } | |
| 187 | |
| 188 div.dict { | |
| 189 background: #cccc99; | |
| 190 padding: 2px; | |
| 191 float: left; | |
| 192 } | |
| 193 | |
| 194 td.dictKey { | |
| 195 background: #ffff99; | |
| 196 font-weight: bold; | |
| 197 } | |
| 198 | |
| 199 td.dictValue { | |
| 200 background: #ffff99; | |
| 201 } | |
| 202 | |
| 203 div.list { | |
| 204 background: #7777cc; | |
| 205 padding: 2px; | |
| 206 float: left; | |
| 207 } | |
| 208 | |
| 209 div.listItem { | |
| 210 background: #9999ff; | |
| 211 } | |
| 212 | |
| 213 div.instance { | |
| 214 background: #cc7777; | |
| 215 padding: 2px; | |
| 216 float: left; | |
| 217 } | |
| 218 | |
| 219 span.instanceName { | |
| 220 font-weight: bold; | |
| 221 display: block; | |
| 222 } | |
| 223 | |
| 224 span.instanceRepr { | |
| 225 background: #ff9999; | |
| 226 font-family: "Courier New", courier, monotype; | |
| 227 } | |
| 228 | |
| 229 div.function { | |
| 230 background: orange; | |
| 231 font-weight: bold; | |
| 232 float: left; | |
| 233 } | |
| 234 </style> | |
| 235 """ | |
| 236 | |
| 237 | |
| 238 def htmlrepr(x): | |
| 239 return htmlReprTypes.get(type(x), htmlUnknown)(x) | |
| 240 | |
| 241 def saferepr(x): | |
| 242 try: | |
| 243 rx = repr(x) | |
| 244 except: | |
| 245 rx = "<repr failed! %s instance at %s>" % (x.__class__, id(x)) | |
| 246 return rx | |
| 247 | |
| 248 def htmlUnknown(x): | |
| 249 return '<code>'+html.escape(saferepr(x))+'</code>' | |
| 250 | |
| 251 def htmlDict(d): | |
| 252 io = StringIO() | |
| 253 w = io.write | |
| 254 w('<div class="dict"><span class="heading">Dictionary instance @ %s</span>'
% hex(id(d))) | |
| 255 w('<table class="dict">') | |
| 256 for k, v in d.items(): | |
| 257 | |
| 258 if k == '__builtins__': | |
| 259 v = 'builtin dictionary' | |
| 260 w('<tr><td class="dictKey">%s</td><td class="dictValue">%s</td></tr>' %
(htmlrepr(k), htmlrepr(v))) | |
| 261 w('</table></div>') | |
| 262 return io.getvalue() | |
| 263 | |
| 264 def htmlList(l): | |
| 265 io = StringIO() | |
| 266 w = io.write | |
| 267 w('<div class="list"><span class="heading">List instance @ %s</span>' % hex(
id(l))) | |
| 268 for i in l: | |
| 269 w('<div class="listItem">%s</div>' % htmlrepr(i)) | |
| 270 w('</div>') | |
| 271 return io.getvalue() | |
| 272 | |
| 273 def htmlInst(i): | |
| 274 if hasattr(i, "__html__"): | |
| 275 s = i.__html__() | |
| 276 else: | |
| 277 s = html.escape(saferepr(i)) | |
| 278 return '''<div class="instance"><span class="instanceName">%s instance @ %s<
/span> | |
| 279 <span class="instanceRepr">%s</span></div> | |
| 280 ''' % (i.__class__, hex(id(i)), s) | |
| 281 | |
| 282 def htmlString(s): | |
| 283 return html.escape(saferepr(s)) | |
| 284 | |
| 285 def htmlFunc(f): | |
| 286 return ('<div class="function">' + | |
| 287 html.escape("function %s in file %s at line %s" % | |
| 288 (f.__name__, f.func_code.co_filename, | |
| 289 f.func_code.co_firstlineno))+ | |
| 290 '</div>') | |
| 291 | |
| 292 htmlReprTypes = {types.DictType: htmlDict, | |
| 293 types.ListType: htmlList, | |
| 294 types.InstanceType: htmlInst, | |
| 295 types.StringType: htmlString, | |
| 296 types.FunctionType: htmlFunc} | |
| 297 | |
| 298 | |
| 299 | |
| 300 def htmlIndent(snippetLine): | |
| 301 ret = string.replace(string.replace(html.escape(string.rstrip(snippetLine)), | |
| 302 ' ', ' '), | |
| 303 '\t', ' ') | |
| 304 return ret | |
| 305 | |
| 306 def formatFailure(myFailure): | |
| 307 | |
| 308 exceptionHTML = """ | |
| 309 <p class="error">%s: %s</p> | |
| 310 """ | |
| 311 | |
| 312 frameHTML = """ | |
| 313 <div class="location">%s, line %s in <span class="function">%s</span></div> | |
| 314 """ | |
| 315 | |
| 316 snippetLineHTML = """ | |
| 317 <div class="snippetLine"><span class="lineno">%s</span><span class="code">%s</sp
an></div> | |
| 318 """ | |
| 319 | |
| 320 snippetHighlightLineHTML = """ | |
| 321 <div class="snippetHighlightLine"><span class="lineno">%s</span><span class="cod
e">%s</span></div> | |
| 322 """ | |
| 323 | |
| 324 variableHTML = """ | |
| 325 <tr class="varRow"><td class="varName">%s</td><td class="varValue">%s</td></tr> | |
| 326 """ | |
| 327 | |
| 328 if not isinstance(myFailure, failure.Failure): | |
| 329 return html.PRE(str(myFailure)) | |
| 330 io = StringIO() | |
| 331 w = io.write | |
| 332 w(stylesheet) | |
| 333 w('<a href="#tbend">') | |
| 334 w(exceptionHTML % (html.escape(str(myFailure.type)), | |
| 335 html.escape(str(myFailure.value)))) | |
| 336 w('</a>') | |
| 337 w('<div class="stackTrace">') | |
| 338 first = 1 | |
| 339 for method, filename, lineno, localVars, globalVars in myFailure.frames: | |
| 340 if filename == '<string>': | |
| 341 continue | |
| 342 if first: | |
| 343 w('<div class="firstFrame">') | |
| 344 first = 0 | |
| 345 else: | |
| 346 w('<div class="frame">') | |
| 347 w(frameHTML % (filename, lineno, method)) | |
| 348 | |
| 349 w('<div class="snippet">') | |
| 350 textSnippet = '' | |
| 351 for snipLineNo in range(lineno-2, lineno+2): | |
| 352 snipLine = linecache.getline(filename, snipLineNo) | |
| 353 textSnippet += snipLine | |
| 354 snipLine = htmlIndent(snipLine) | |
| 355 if snipLineNo == lineno: | |
| 356 w(snippetHighlightLineHTML % (snipLineNo, snipLine)) | |
| 357 else: | |
| 358 w(snippetLineHTML % (snipLineNo, snipLine)) | |
| 359 w('</div>') | |
| 360 | |
| 361 # Instance variables | |
| 362 for name, var in localVars: | |
| 363 if name == 'self' and hasattr(var, '__dict__'): | |
| 364 usedVars = [ (key, value) for (key, value) in var.__dict__.items
() | |
| 365 if re.search(r'\W'+'self.'+key+r'\W', textSnippet)
] | |
| 366 if usedVars: | |
| 367 w('<div class="variables"><b>Self</b>') | |
| 368 w('<table class="variables">') | |
| 369 for key, value in usedVars: | |
| 370 w(variableHTML % (key, htmlrepr(value))) | |
| 371 w('</table></div>') | |
| 372 break | |
| 373 | |
| 374 # Local and global vars | |
| 375 for nm, varList in ('Locals', localVars), ('Globals', globalVars): | |
| 376 usedVars = [ (name, var) for (name, var) in varList | |
| 377 if re.search(r'\W'+name+r'\W', textSnippet) ] | |
| 378 if usedVars: | |
| 379 w('<div class="variables"><b>%s</b><table class="variables">' %
nm) | |
| 380 for name, var in usedVars: | |
| 381 w(variableHTML % (name, htmlrepr(var))) | |
| 382 w('</table></div>') | |
| 383 | |
| 384 w('</div>') # frame | |
| 385 w('</div>') # stacktrace | |
| 386 w('<a name="tbend"> </a>') | |
| 387 w(exceptionHTML % (html.escape(str(myFailure.type)), | |
| 388 html.escape(str(myFailure.value)))) | |
| 389 | |
| 390 return io.getvalue() | |
| OLD | NEW |