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 |