8
8
9
9
display - if true, tracebacks are displayed in the web browser
10
10
logdir - if set, tracebacks are written to files in this directory
11
+ context - number of lines of source code to show for each stack frame
11
12
12
- By default, tracebacks are displayed but not written to files .
13
+ By default, tracebacks are displayed but not saved, and context is 5 .
13
14
14
15
Alternatively, if you have caught an exception and want cgitb to display it
15
16
for you, call cgitb.handle(). The optional argument to handle() is a 3-item
@@ -23,160 +24,177 @@ def reset():
23
24
return '''<!--: spam
24
25
Content-Type: text/html
25
26
26
- <body bgcolor="#f0f0ff "><font color="#f0f0ff " size="-5"> -->
27
- <body bgcolor="#f0f0ff "><font color="#f0f0ff " size="-5"> --> -->
27
+ <body bgcolor="#f0f0f8 "><font color="#f0f0f8 " size="-5"> -->
28
+ <body bgcolor="#f0f0f8 "><font color="#f0f0f8 " size="-5"> --> -->
28
29
</font> </font> </font> </script> </object> </blockquote> </pre>
29
30
</table> </table> </table> </table> </table> </font> </font> </font>'''
30
31
31
- def html (etype , evalue , etb , context = 5 ):
32
- """Return a nice HTML document describing the traceback."""
33
- import sys , os , types , time , traceback
34
- import keyword , tokenize , linecache , inspect , pydoc
32
+ __UNDEF__ = [] # a special sentinel object
33
+ def small (text ): return '<small>' + text + '</small>'
34
+ def strong (text ): return '<strong>' + text + '</strong>'
35
+ def grey (text ): return '<font color="#909090">' + text + '</font>'
36
+
37
+ def lookup (name , frame , locals ):
38
+ """Find the value for a given name in the given environment."""
39
+ if name in locals :
40
+ return 'local' , locals [name ]
41
+ if name in frame .f_globals :
42
+ return 'global' , frame .f_globals [name ]
43
+ return None , __UNDEF__
44
+
45
+ def scanvars (reader , frame , locals ):
46
+ """Scan one logical line of Python and look up values of variables used."""
47
+ import tokenize , keyword
48
+ vars , lasttoken , parent , prefix = [], None , None , ''
49
+ for ttype , token , start , end , line in tokenize .generate_tokens (reader ):
50
+ if ttype == tokenize .NEWLINE : break
51
+ if ttype == tokenize .NAME and token not in keyword .kwlist :
52
+ if lasttoken == '.' :
53
+ if parent is not __UNDEF__ :
54
+ value = getattr (parent , token , __UNDEF__ )
55
+ vars .append ((prefix + token , prefix , value ))
56
+ else :
57
+ where , value = lookup (token , frame , locals )
58
+ vars .append ((token , where , value ))
59
+ elif token == '.' :
60
+ prefix += lasttoken + '.'
61
+ parent = value
62
+ else :
63
+ parent , prefix = None , ''
64
+ lasttoken = token
65
+ return vars
66
+
67
+ def html ((etype , evalue , etb ), context = 5 ):
68
+ """Return a nice HTML document describing a given traceback."""
69
+ import sys , os , types , time , traceback , linecache , inspect , pydoc
35
70
36
71
if type (etype ) is types .ClassType :
37
72
etype = etype .__name__
38
73
pyver = 'Python ' + sys .version .split ()[0 ] + ': ' + sys .executable
39
74
date = time .ctime (time .time ())
40
- head = '<body bgcolor="#f0f0ff ">' + pydoc .html .heading (
75
+ head = '<body bgcolor="#f0f0f8 ">' + pydoc .html .heading (
41
76
'<big><big><strong>%s</strong></big></big>' % str (etype ),
42
- '#ffffff' , '#aa55cc' , pyver + '<br>' + date ) + '''
43
- <p>A problem occurred in a Python script.
44
- Here is the sequence of function calls leading up to
45
- the error, with the most recent (innermost) call last.'''
77
+ '#ffffff' , '#6622aa' , pyver + '<br>' + date ) + '''
78
+ <p>A problem occurred in a Python script. Here is the sequence of
79
+ function calls leading up to the error, in the order they occurred.'''
46
80
47
- indent = '<tt><small> ' + ' ' * 5 + '</small> </tt>'
81
+ indent = '<tt>' + small ( ' ' * 5 ) + ' </tt>'
48
82
frames = []
49
83
records = inspect .getinnerframes (etb , context )
50
84
for frame , file , lnum , func , lines , index in records :
51
85
file = file and os .path .abspath (file ) or '?'
52
- link = '<a href="file:%s">%s</a>' % (file , pydoc .html .escape (file ))
86
+ link = '<a href="file:// %s">%s</a>' % (file , pydoc .html .escape (file ))
53
87
args , varargs , varkw , locals = inspect .getargvalues (frame )
54
- if func == '?' :
55
- call = ''
56
- else :
57
- def eqrepr (value ): return '=' + pydoc .html .repr (value )
58
- call = 'in <strong>%s</strong>' % func + inspect .formatargvalues (
59
- args , varargs , varkw , locals , formatvalue = eqrepr )
60
-
61
- names = []
62
- def tokeneater (type , token , start , end , line ):
63
- if type == tokenize .NAME and token not in keyword .kwlist :
64
- if token not in names : names .append (token )
65
- if type == tokenize .NEWLINE : raise IndexError
66
- def linereader (lnum = [lnum ]):
67
- line = linecache .getline (file , lnum [0 ])
68
- lnum [0 ] += 1
69
- return line
70
-
71
- try :
72
- tokenize .tokenize (linereader , tokeneater )
73
- except IndexError : pass
74
- lvals = []
75
- for name in names :
76
- if name in frame .f_code .co_varnames :
77
- if locals .has_key (name ):
78
- value = pydoc .html .repr (locals [name ])
79
- else :
80
- value = '<em>undefined</em>'
81
- name = '<strong>%s</strong>' % name
82
- else :
83
- if frame .f_globals .has_key (name ):
84
- value = pydoc .html .repr (frame .f_globals [name ])
85
- else :
86
- value = '<em>undefined</em>'
87
- name = '<em>global</em> <strong>%s</strong>' % name
88
- lvals .append ('%s = %s' % (name , value ))
89
- if lvals :
90
- lvals = indent + '''
91
- <small><font color="#909090">%s</font></small><br>''' % (', ' .join (lvals ))
92
- else :
93
- lvals = ''
94
-
95
- level = '''
96
- <table width="100%%" bgcolor="#d8bbff" cellspacing=0 cellpadding=2 border=0>
97
- <tr><td>%s %s</td></tr></table>\n ''' % (link , call )
98
- excerpt = []
88
+ call = ''
89
+ if func != '?' :
90
+ call = 'in ' + strong (func ) + \
91
+ inspect .formatargvalues (args , varargs , varkw , locals ,
92
+ formatvalue = lambda value : '=' + pydoc .html .repr (value ))
93
+
94
+ highlight = {}
95
+ def reader (lnum = [lnum ]):
96
+ highlight [lnum [0 ]] = 1
97
+ try : return linecache .getline (file , lnum [0 ])
98
+ finally : lnum [0 ] += 1
99
+ vars = scanvars (reader , frame , locals )
100
+
101
+ rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
102
+ ('<big> </big>' , link , call )]
99
103
if index is not None :
100
104
i = lnum - index
101
105
for line in lines :
102
- num = '<small><font color="#909090">%s</font></small>' % (
103
- ' ' * (5 - len (str (i ))) + str (i ))
104
- line = '<tt>%s %s</tt>' % (num , pydoc .html .preformat (line ))
105
- if i == lnum :
106
- line = '''
107
- <table width="100%%" bgcolor="#ffccee" cellspacing=0 cellpadding=0 border=0>
108
- <tr><td>%s</td></tr></table>\n ''' % line
109
- excerpt .append ('\n ' + line )
110
- if i == lnum :
111
- excerpt .append (lvals )
112
- i = i + 1
113
- frames .append ('<p>' + level + '\n ' .join (excerpt ))
114
-
115
- exception = ['<p><strong>%s</strong>: %s' % (str (etype ), str (evalue ))]
106
+ num = small (' ' * (5 - len (str (i ))) + str (i )) + ' '
107
+ line = '<tt>%s%s</tt>' % (num , pydoc .html .preformat (line ))
108
+ if i in highlight :
109
+ rows .append ('<tr><td bgcolor="#ffccee">%s</td></tr>' % line )
110
+ else :
111
+ rows .append ('<tr><td>%s</td></tr>' % grey (line ))
112
+ i += 1
113
+
114
+ done , dump = {}, []
115
+ for name , where , value in vars :
116
+ if name in done : continue
117
+ done [name ] = 1
118
+ if value is not __UNDEF__ :
119
+ if where == 'global' : name = '<em>global</em> ' + strong (name )
120
+ elif where == 'local' : name = strong (name )
121
+ else : name = where + strong (name .split ('.' )[- 1 ])
122
+ dump .append ('%s = %s' % (name , pydoc .html .repr (value )))
123
+ else :
124
+ dump .append (name + ' <em>undefined</em>' )
125
+
126
+ rows .append ('<tr><td>%s</td></tr>' % small (grey (', ' .join (dump ))))
127
+ frames .append ('''<p>
128
+ <table width="100%%" cellspacing=0 cellpadding=0 border=0>
129
+ %s</table>''' % '\n ' .join (rows ))
130
+
131
+ exception = ['<p>%s: %s' % (strong (str (etype )), str (evalue ))]
116
132
if type (evalue ) is types .InstanceType :
117
133
for name in dir (evalue ):
118
134
value = pydoc .html .repr (getattr (evalue , name ))
119
135
exception .append ('\n <br>%s%s =\n %s' % (indent , name , value ))
120
136
121
137
import traceback
122
- plaintrace = '' .join (traceback .format_exception (etype , evalue , etb ))
123
-
124
138
return head + '' .join (frames ) + '' .join (exception ) + '''
125
139
126
140
127
- <!-- The above is a description of an error that occurred in a Python program.
128
- It is formatted for display in a Web browser because it appears that
129
- we are running in a CGI environment. In case you are viewing this
130
- message outside of a Web browser, here is the original error traceback:
141
+ <!-- The above is a description of an error in a Python program, formatted
142
+ for a Web browser because the 'cgitb' module was enabled. In case you
143
+ are not reading this in a Web browser, here is the original traceback:
131
144
132
145
%s
133
146
-->
134
- ''' % plaintrace
147
+ ''' % '' . join ( traceback . format_exception ( etype , evalue , etb ))
135
148
136
149
class Hook :
137
- def __init__ (self , display = 1 , logdir = None ):
150
+ """A hook to replace sys.excepthook that shows tracebacks in HTML."""
151
+
152
+ def __init__ (self , display = 1 , logdir = None , context = 5 ):
138
153
self .display = display # send tracebacks to browser if true
139
154
self .logdir = logdir # log tracebacks to files if not None
155
+ self .context = context # number of source code lines per frame
140
156
141
157
def __call__ (self , etype , evalue , etb ):
142
- """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
143
158
self .handle ((etype , evalue , etb ))
144
159
145
160
def handle (self , info = None ):
146
- import sys , os
161
+ import sys
147
162
info = info or sys .exc_info ()
148
- text = 0
149
163
print reset ()
150
164
151
165
try :
152
- doc = html (* info )
166
+ text , doc = 0 , html (info , self . context )
153
167
except : # just in case something goes wrong
154
168
import traceback
155
- doc = '' .join (traceback .format_exception (* info ))
156
- text = 1
169
+ text , doc = 1 , '' .join (traceback .format_exception (* info ))
157
170
158
171
if self .display :
159
172
if text :
160
173
doc = doc .replace ('&' , '&' ).replace ('<' , '<' )
161
- print '<pre>' , doc , '</pre>'
174
+ print '<pre>' + doc + '</pre>'
162
175
else :
163
176
print doc
164
177
else :
165
178
print '<p>A problem occurred in a Python script.'
166
179
167
180
if self .logdir is not None :
168
- import tempfile
181
+ import os , tempfile
169
182
name = tempfile .mktemp (['.html' , '.txt' ][text ])
170
183
path = os .path .join (self .logdir , os .path .basename (name ))
171
184
try :
172
185
file = open (path , 'w' )
173
186
file .write (doc )
174
187
file .close ()
175
- print '<p>%s contains the description of this error.' % path
188
+ print '<p> %s contains the description of this error.' % path
176
189
except :
177
- print '<p>Tried to write to %s, but failed.' % path
190
+ print '<p> Tried to save traceback to %s, but failed.' % path
178
191
179
192
handler = Hook ().handle
180
- def enable (display = 1 , logdir = None ):
193
+ def enable (display = 1 , logdir = None , context = 5 ):
194
+ """Install an exception handler that formats tracebacks as HTML.
195
+
196
+ The optional argument 'display' can be set to 0 to suppress sending the
197
+ traceback to the browser, and 'logdir' can be set to a directory to cause
198
+ tracebacks to be written to files there."""
181
199
import sys
182
- sys .excepthook = Hook (display , logdir )
200
+ sys .excepthook = Hook (display , logdir , context )
0 commit comments