~ubuntu-branches/ubuntu/trusty/python3.4/trusty-proposed

« back to all changes in this revision

Viewing changes to Lib/cgitb.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-11-25 09:44:27 UTC
  • Revision ID: package-import@ubuntu.com-20131125094427-lzxj8ap5w01lmo7f
Tags: upstream-3.4~b1
ImportĀ upstreamĀ versionĀ 3.4~b1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""More comprehensive traceback formatting for Python scripts.
 
2
 
 
3
To enable this module, do:
 
4
 
 
5
    import cgitb; cgitb.enable()
 
6
 
 
7
at the top of your script.  The optional arguments to enable() are:
 
8
 
 
9
    display     - if true, tracebacks are displayed in the web browser
 
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
 
12
    format      - 'text' or 'html' controls the output format
 
13
 
 
14
By default, tracebacks are displayed but not saved, the context is 5 lines
 
15
and the output format is 'html' (for backwards compatibility with the
 
16
original use of this module)
 
17
 
 
18
Alternatively, if you have caught an exception and want cgitb to display it
 
19
for you, call cgitb.handler().  The optional argument to handler() is a
 
20
3-item tuple (etype, evalue, etb) just like the value of sys.exc_info().
 
21
The default handler displays output as HTML.
 
22
 
 
23
"""
 
24
import inspect
 
25
import keyword
 
26
import linecache
 
27
import os
 
28
import pydoc
 
29
import sys
 
30
import tempfile
 
31
import time
 
32
import tokenize
 
33
import traceback
 
34
 
 
35
def reset():
 
36
    """Return a string that resets the CGI and browser to a known state."""
 
37
    return '''<!--: spam
 
38
Content-Type: text/html
 
39
 
 
40
<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
 
41
<body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
 
42
</font> </font> </font> </script> </object> </blockquote> </pre>
 
43
</table> </table> </table> </table> </table> </font> </font> </font>'''
 
44
 
 
45
__UNDEF__ = []                          # a special sentinel object
 
46
def small(text):
 
47
    if text:
 
48
        return '<small>' + text + '</small>'
 
49
    else:
 
50
        return ''
 
51
 
 
52
def strong(text):
 
53
    if text:
 
54
        return '<strong>' + text + '</strong>'
 
55
    else:
 
56
        return ''
 
57
 
 
58
def grey(text):
 
59
    if text:
 
60
        return '<font color="#909090">' + text + '</font>'
 
61
    else:
 
62
        return ''
 
63
 
 
64
def lookup(name, frame, locals):
 
65
    """Find the value for a given name in the given environment."""
 
66
    if name in locals:
 
67
        return 'local', locals[name]
 
68
    if name in frame.f_globals:
 
69
        return 'global', frame.f_globals[name]
 
70
    if '__builtins__' in frame.f_globals:
 
71
        builtins = frame.f_globals['__builtins__']
 
72
        if type(builtins) is type({}):
 
73
            if name in builtins:
 
74
                return 'builtin', builtins[name]
 
75
        else:
 
76
            if hasattr(builtins, name):
 
77
                return 'builtin', getattr(builtins, name)
 
78
    return None, __UNDEF__
 
79
 
 
80
def scanvars(reader, frame, locals):
 
81
    """Scan one logical line of Python and look up values of variables used."""
 
82
    vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__
 
83
    for ttype, token, start, end, line in tokenize.generate_tokens(reader):
 
84
        if ttype == tokenize.NEWLINE: break
 
85
        if ttype == tokenize.NAME and token not in keyword.kwlist:
 
86
            if lasttoken == '.':
 
87
                if parent is not __UNDEF__:
 
88
                    value = getattr(parent, token, __UNDEF__)
 
89
                    vars.append((prefix + token, prefix, value))
 
90
            else:
 
91
                where, value = lookup(token, frame, locals)
 
92
                vars.append((token, where, value))
 
93
        elif token == '.':
 
94
            prefix += lasttoken + '.'
 
95
            parent = value
 
96
        else:
 
97
            parent, prefix = None, ''
 
98
        lasttoken = token
 
99
    return vars
 
100
 
 
101
def html(einfo, context=5):
 
102
    """Return a nice HTML document describing a given traceback."""
 
103
    etype, evalue, etb = einfo
 
104
    if isinstance(etype, type):
 
105
        etype = etype.__name__
 
106
    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
 
107
    date = time.ctime(time.time())
 
108
    head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
 
109
        '<big><big>%s</big></big>' %
 
110
        strong(pydoc.html.escape(str(etype))),
 
111
        '#ffffff', '#6622aa', pyver + '<br>' + date) + '''
 
112
<p>A problem occurred in a Python script.  Here is the sequence of
 
113
function calls leading up to the error, in the order they occurred.</p>'''
 
114
 
 
115
    indent = '<tt>' + small('&nbsp;' * 5) + '&nbsp;</tt>'
 
116
    frames = []
 
117
    records = inspect.getinnerframes(etb, context)
 
118
    for frame, file, lnum, func, lines, index in records:
 
119
        if file:
 
120
            file = os.path.abspath(file)
 
121
            link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
 
122
        else:
 
123
            file = link = '?'
 
124
        args, varargs, varkw, locals = inspect.getargvalues(frame)
 
125
        call = ''
 
126
        if func != '?':
 
127
            call = 'in ' + strong(func) + \
 
128
                inspect.formatargvalues(args, varargs, varkw, locals,
 
129
                    formatvalue=lambda value: '=' + pydoc.html.repr(value))
 
130
 
 
131
        highlight = {}
 
132
        def reader(lnum=[lnum]):
 
133
            highlight[lnum[0]] = 1
 
134
            try: return linecache.getline(file, lnum[0])
 
135
            finally: lnum[0] += 1
 
136
        vars = scanvars(reader, frame, locals)
 
137
 
 
138
        rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
 
139
                ('<big>&nbsp;</big>', link, call)]
 
140
        if index is not None:
 
141
            i = lnum - index
 
142
            for line in lines:
 
143
                num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;'
 
144
                if i in highlight:
 
145
                    line = '<tt>=&gt;%s%s</tt>' % (num, pydoc.html.preformat(line))
 
146
                    rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
 
147
                else:
 
148
                    line = '<tt>&nbsp;&nbsp;%s%s</tt>' % (num, pydoc.html.preformat(line))
 
149
                    rows.append('<tr><td>%s</td></tr>' % grey(line))
 
150
                i += 1
 
151
 
 
152
        done, dump = {}, []
 
153
        for name, where, value in vars:
 
154
            if name in done: continue
 
155
            done[name] = 1
 
156
            if value is not __UNDEF__:
 
157
                if where in ('global', 'builtin'):
 
158
                    name = ('<em>%s</em> ' % where) + strong(name)
 
159
                elif where == 'local':
 
160
                    name = strong(name)
 
161
                else:
 
162
                    name = where + strong(name.split('.')[-1])
 
163
                dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))
 
164
            else:
 
165
                dump.append(name + ' <em>undefined</em>')
 
166
 
 
167
        rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
 
168
        frames.append('''
 
169
<table width="100%%" cellspacing=0 cellpadding=0 border=0>
 
170
%s</table>''' % '\n'.join(rows))
 
171
 
 
172
    exception = ['<p>%s: %s' % (strong(pydoc.html.escape(str(etype))),
 
173
                                pydoc.html.escape(str(evalue)))]
 
174
    for name in dir(evalue):
 
175
        if name[:1] == '_': continue
 
176
        value = pydoc.html.repr(getattr(evalue, name))
 
177
        exception.append('\n<br>%s%s&nbsp;=\n%s' % (indent, name, value))
 
178
 
 
179
    return head + ''.join(frames) + ''.join(exception) + '''
 
180
 
 
181
 
 
182
<!-- The above is a description of an error in a Python program, formatted
 
183
     for a Web browser because the 'cgitb' module was enabled.  In case you
 
184
     are not reading this in a Web browser, here is the original traceback:
 
185
 
 
186
%s
 
187
-->
 
188
''' % pydoc.html.escape(
 
189
          ''.join(traceback.format_exception(etype, evalue, etb)))
 
190
 
 
191
def text(einfo, context=5):
 
192
    """Return a plain text document describing a given traceback."""
 
193
    etype, evalue, etb = einfo
 
194
    if isinstance(etype, type):
 
195
        etype = etype.__name__
 
196
    pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
 
197
    date = time.ctime(time.time())
 
198
    head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + '''
 
199
A problem occurred in a Python script.  Here is the sequence of
 
200
function calls leading up to the error, in the order they occurred.
 
201
'''
 
202
 
 
203
    frames = []
 
204
    records = inspect.getinnerframes(etb, context)
 
205
    for frame, file, lnum, func, lines, index in records:
 
206
        file = file and os.path.abspath(file) or '?'
 
207
        args, varargs, varkw, locals = inspect.getargvalues(frame)
 
208
        call = ''
 
209
        if func != '?':
 
210
            call = 'in ' + func + \
 
211
                inspect.formatargvalues(args, varargs, varkw, locals,
 
212
                    formatvalue=lambda value: '=' + pydoc.text.repr(value))
 
213
 
 
214
        highlight = {}
 
215
        def reader(lnum=[lnum]):
 
216
            highlight[lnum[0]] = 1
 
217
            try: return linecache.getline(file, lnum[0])
 
218
            finally: lnum[0] += 1
 
219
        vars = scanvars(reader, frame, locals)
 
220
 
 
221
        rows = [' %s %s' % (file, call)]
 
222
        if index is not None:
 
223
            i = lnum - index
 
224
            for line in lines:
 
225
                num = '%5d ' % i
 
226
                rows.append(num+line.rstrip())
 
227
                i += 1
 
228
 
 
229
        done, dump = {}, []
 
230
        for name, where, value in vars:
 
231
            if name in done: continue
 
232
            done[name] = 1
 
233
            if value is not __UNDEF__:
 
234
                if where == 'global': name = 'global ' + name
 
235
                elif where != 'local': name = where + name.split('.')[-1]
 
236
                dump.append('%s = %s' % (name, pydoc.text.repr(value)))
 
237
            else:
 
238
                dump.append(name + ' undefined')
 
239
 
 
240
        rows.append('\n'.join(dump))
 
241
        frames.append('\n%s\n' % '\n'.join(rows))
 
242
 
 
243
    exception = ['%s: %s' % (str(etype), str(evalue))]
 
244
    for name in dir(evalue):
 
245
        value = pydoc.text.repr(getattr(evalue, name))
 
246
        exception.append('\n%s%s = %s' % (" "*4, name, value))
 
247
 
 
248
    return head + ''.join(frames) + ''.join(exception) + '''
 
249
 
 
250
The above is a description of an error in a Python program.  Here is
 
251
the original traceback:
 
252
 
 
253
%s
 
254
''' % ''.join(traceback.format_exception(etype, evalue, etb))
 
255
 
 
256
class Hook:
 
257
    """A hook to replace sys.excepthook that shows tracebacks in HTML."""
 
258
 
 
259
    def __init__(self, display=1, logdir=None, context=5, file=None,
 
260
                 format="html"):
 
261
        self.display = display          # send tracebacks to browser if true
 
262
        self.logdir = logdir            # log tracebacks to files if not None
 
263
        self.context = context          # number of source code lines per frame
 
264
        self.file = file or sys.stdout  # place to send the output
 
265
        self.format = format
 
266
 
 
267
    def __call__(self, etype, evalue, etb):
 
268
        self.handle((etype, evalue, etb))
 
269
 
 
270
    def handle(self, info=None):
 
271
        info = info or sys.exc_info()
 
272
        if self.format == "html":
 
273
            self.file.write(reset())
 
274
 
 
275
        formatter = (self.format=="html") and html or text
 
276
        plain = False
 
277
        try:
 
278
            doc = formatter(info, self.context)
 
279
        except:                         # just in case something goes wrong
 
280
            doc = ''.join(traceback.format_exception(*info))
 
281
            plain = True
 
282
 
 
283
        if self.display:
 
284
            if plain:
 
285
                doc = doc.replace('&', '&amp;').replace('<', '&lt;')
 
286
                self.file.write('<pre>' + doc + '</pre>\n')
 
287
            else:
 
288
                self.file.write(doc + '\n')
 
289
        else:
 
290
            self.file.write('<p>A problem occurred in a Python script.\n')
 
291
 
 
292
        if self.logdir is not None:
 
293
            suffix = ['.txt', '.html'][self.format=="html"]
 
294
            (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
 
295
 
 
296
            try:
 
297
                file = os.fdopen(fd, 'w')
 
298
                file.write(doc)
 
299
                file.close()
 
300
                msg = '%s contains the description of this error.' % path
 
301
            except:
 
302
                msg = 'Tried to save traceback to %s, but failed.' % path
 
303
 
 
304
            if self.format == 'html':
 
305
                self.file.write('<p>%s</p>\n' % msg)
 
306
            else:
 
307
                self.file.write(msg + '\n')
 
308
        try:
 
309
            self.file.flush()
 
310
        except: pass
 
311
 
 
312
handler = Hook().handle
 
313
def enable(display=1, logdir=None, context=5, format="html"):
 
314
    """Install an exception handler that formats tracebacks as HTML.
 
315
 
 
316
    The optional argument 'display' can be set to 0 to suppress sending the
 
317
    traceback to the browser, and 'logdir' can be set to a directory to cause
 
318
    tracebacks to be written to files there."""
 
319
    sys.excepthook = Hook(display=display, logdir=logdir,
 
320
                          context=context, format=format)