17
17
By default, tracebacks are displayed but not saved, the context is 5 lines
18
18
and the output format is 'html' (for backwards compatibility with the
19
original use of this module)
19
original use of this module).
21
21
Alternatively, if you have caught an exception and want cgitb to display it
22
22
for you, call cgitb.handler(). The optional argument to handler() is a
30
30
- Refactor html and text functions to View class, HTMLFormatter and
31
31
TextFormatter. No more duplicate formating code.
32
32
- Layout is done with minimal html and css, in a way it can't be
33
effected by souranding code.
34
- Built to be easy to subclass and modify without duplicating code
33
affected by surrounding code.
34
- Built to be easy to subclass and modify without duplicating code.
35
35
- Change layout, important details come first.
36
- Factor frame analaizing and formatting into separate class
36
- Factor frame analyzing and formatting into separate class.
37
37
- Add debug argument, can be used to change error display e.g. user
38
error view, developer error view
39
- Add viewClass argument, make it easy to customize the traceback view
40
- Easy to customize system details and application details
38
error view, developer error view.
39
- Add viewClass argument, make it easy to customize the traceback view.
40
- Easy to customize system details and application details.
42
42
The main goal of this rewrite was to have a traceback that can render
43
few tracebacks combined. Its needed when you wrap an expection and want
43
few tracebacks combined. It's needed when you wrap an expection and want
44
44
to print both the traceback up to the wrapper exception, and the
45
original traceback. There is no code to support this here, but its easy
45
original traceback. There is no code to support this here, but it's easy
46
46
to add by using your own View sub class.
58
58
Return a string that resets the CGI and browser to a known state.
59
59
TODO: probably some of this is not needed any more.
62
62
Content-Type: text/html
64
64
<body><font style="color: white; font-size: 1px"> -->
65
65
<body><font style="color: white; font-size: 1px"> --> -->
66
66
</font> </font> </font> </script> </object> </blockquote> </pre>
67
67
</table> </table> </table> </table> </table> </font> </font> </font>
70
70
__UNDEF__ = [] # a special sentinel object
76
HiddenObject = HiddenObject()
73
78
class HTMLFormatter:
74
79
""" Minimal html formatter """
76
81
def attributes(self, attributes=None):
78
result = [' %s="%s"' % (k, v) for k, v in attributes.items()]
83
result = [' %s="%s"' % (k, v) for k, v in attributes.items()]
79
84
return ''.join(result)
82
87
def tag(self, name, text, attributes=None):
83
return '<%s%s>%s</%s>\n' % (name, self.attributes(attributes),
88
return '<%s%s>%s</%s>\n' % (name, self.attributes(attributes), text, name)
86
90
def section(self, text, attributes=None):
87
91
return self.tag('div', text, attributes)
109
113
if isinstance(items, (list, tuple)):
110
114
items = '\n' + ''.join([self.listItem(i) for i in items])
111
115
return self.tag(name, items, attributes)
113
117
def listItem(self, text, attributes=None):
114
return self.tag('li', text, attributes)
118
return self.tag('li', text, attributes)
116
120
def link(self, href, text, attributes=None):
117
121
if attributes is None:
120
124
return self.tag('a', text, attributes)
122
126
def strong(self, text, attributes=None):
123
return self.tag('strong', text, attributes)
127
return self.tag('strong', text, attributes)
125
129
def em(self, text, attributes=None):
126
return self.tag('em', text, attributes)
130
return self.tag('em', text, attributes)
128
132
def repr(self, object):
129
133
return pydoc.html.repr(object)
132
136
class TextFormatter:
133
137
""" Plain text formatter """
159
163
def orderedList(self, items, attributes=None):
160
164
if isinstance(items, (list, tuple)):
162
for i in rage(items):
166
for i in range(items):
163
167
result.append(' %d. %s\n' % (i, items[i]))
164
168
return ''.join(result) + '\n'
167
171
def listItem(self, text, attributes=None):
168
return ' * %s\n' % text
172
return ' * %s\n' % text
170
174
def link(self, href, text, attributes=None):
171
175
return '[[%s]]' % text
173
177
def strong(self, text, attributes=None):
176
180
def em(self, text, attributes=None):
179
183
def repr(self, object):
180
184
return repr(object)
184
188
""" Analyze and format single frame in a traceback """
203
207
# -----------------------------------------------------------------
204
208
# Private - formatting
206
210
def formatCall(self):
207
211
call = '%s in %s%s' % (self.formatFile(),
208
212
self.formatter.strong(self.func),
209
213
self.formatArguments(),)
210
214
return self.formatter.paragraph(call, {'class': 'call'})
212
216
def formatFile(self):
213
217
""" Return formatted file link """
214
218
if not self.file:
216
220
file = pydoc.html.escape(os.path.abspath(self.file))
217
return self.formatter.link('file://' + file, file)
221
return self.formatter.link('file://' + file, file)
219
223
def formatArguments(self):
220
224
""" Return formated arguments list """
245
249
return self.formatter.orderedList(context, {'class': 'context'})
247
251
def formatVariables(self, vars):
248
""" Return formatted variables """
252
""" Return formatted variables """
251
255
for name, where, value in vars:
255
259
if value is __UNDEF__:
276
280
""" Scan frame for vars while setting highlight line """
279
283
def reader(lnum=[self.lnum]):
280
284
highlight[lnum[0]] = 1
282
286
return linecache.getline(self.file, lnum[0])
286
290
vars = self.scanVariables(reader)
290
294
""" Lookup variables in one logical Python line """
291
295
vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__
292
296
for ttype, token, start, end, line in tokenize.generate_tokens(reader):
293
if ttype == tokenize.NEWLINE:
297
if ttype == tokenize.NEWLINE:
295
299
if ttype == tokenize.NAME and token not in keyword.kwlist:
296
300
if lasttoken == '.':
297
301
if parent is not __UNDEF__:
298
value = getattr(parent, token, __UNDEF__)
302
if self.unsafe_name(token):
305
value = getattr(parent, token, __UNDEF__)
299
306
vars.append((prefix + token, prefix, value))
301
308
where, value = self.lookup(token)
324
331
value = builtins.get(name, __UNDEF__)
326
333
value = getattr(builtins, name, __UNDEF__)
334
if self.unsafe_name(name):
327
336
return scope, value
338
def unsafe_name(self, name):
339
return name in self.frame.f_globals.get("unsafe_names", ())
331
342
""" Traceback view """
333
344
frameClass = Frame # analyze and format a frame
335
346
def __init__(self, info=None, debug=0):
336
347
""" Save starting info or current exception info """
337
348
self.info = info or sys.exc_info()
338
349
self.debug = debug
340
351
def format(self, formatter, context=5):
341
352
self.formatter = formatter
342
353
self.context = context
400
411
# -----------------------------------------------------------------
403
414
def formatTitle(self):
404
415
return self.formatter.title(self.exceptionTitle(self.info))
406
417
def formatMessage(self):
407
418
return self.formatter.paragraph(self.exceptionMessage(self.info))
409
420
# -----------------------------------------------------------------
412
423
def formatTraceback(self):
413
424
""" Return formatted traceback """
414
425
return self.formatOneTraceback(self.info)
416
427
def formatOneTraceback(self, info):
417
428
""" Format one traceback
423
434
self.formatter.orderedList(self.tracebackFrames(info),
424
435
{'class': 'frames'}),
425
436
self.formatter.section(self.formatException(info),
426
{'class': 'exception'}),]
437
{'class': 'exception'}), ]
427
438
return self.formatter.section(''.join(output), {'class': 'traceback'})
429
440
def tracebackFrames(self, info):
446
457
def formatException(self, info):
447
458
items = [self.formatExceptionTitle(info),
448
459
self.formatExceptionMessage(info),
449
self.formatExceptionAttributes(info),]
460
self.formatExceptionAttributes(info), ]
450
461
return ''.join(items)
452
463
def formatExceptionTitle(self, info):
453
464
return self.formatter.subSubTitle(self.exceptionTitle(info))
455
466
def formatExceptionMessage(self, info):
456
467
return self.formatter.paragraph(self.exceptionMessage(info))
460
471
for name, value in self.exceptionAttributes(info):
461
472
value = self.formatter.repr(value)
462
attribtues.append('%s = %s' % (name, value))
473
attribtues.append('%s = %s' % (name, value))
463
474
return self.formatter.list(attribtues)
465
476
def exceptionAttributes(self, info):
476
487
def exceptionTitle(self, info):
478
489
return getattr(type, '__name__', str(type))
480
491
def exceptionMessage(self, info):
481
492
instance = info[1]
482
493
return pydoc.html.escape(str(instance))
488
499
def formatSystemDetails(self):
489
500
details = ['Date: %s' % self.date(),
490
501
'Platform: %s' % self.platform(),
491
'Python: %s' % self.python(),]
502
'Python: %s' % self.python(), ]
492
503
details += self.applicationDetails()
493
504
return (self.formatter.subTitle('System Details') +
494
505
self.formatter.list(details, {'class': 'system'}))
498
rfc2822Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",
509
rfc2822Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
500
510
return rfc2822Date
502
512
def platform(self):
580
590
self.file.write('<p>A problem occurred in a Python script.\n')
582
592
if self.logdir is not None:
584
suffix = ['.txt', '.html'][self.format=="html"]
594
suffix = ['.txt', '.html'][self.format == "html"]
585
595
(fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
587
597
file = os.fdopen(fd, 'w')
599
609
handler = Hook().handle
601
def enable(display=1, logdir=None, context=5, format="html",
602
viewClass=View, debug=0):
611
def enable(display=1, logdir=None, context=5, format="html", viewClass=View, debug=0):
603
612
"""Install an exception handler that formats tracebacks as HTML.
605
614
The optional argument 'display' can be set to 0 to suppress sending the
607
616
tracebacks to be written to files there."""
608
617
sys.excepthook = Hook(display=display, logdir=logdir, context=context,
609
618
format=format, viewClass=viewClass, debug=debug)