Commit | Line | Data |
---|---|---|
c638d827 CR |
1 | # |
2 | # This module was written by Ka-Ping Yee, <ping@lfw.org>. | |
3 | # | |
4 | # $Id: cgitb.py,v 1.12 2004-07-13 10:18:00 a1s Exp $ | |
5 | ||
6 | """Extended CGI traceback handler by Ka-Ping Yee, <ping@lfw.org>. | |
7 | """ | |
8 | __docformat__ = 'restructuredtext' | |
9 | ||
10 | import sys, os, types, string, keyword, linecache, tokenize, inspect, cgi | |
11 | import pydoc, traceback | |
12 | ||
13 | from roundup.cgi import templating, TranslationService | |
14 | ||
15 | def get_translator(i18n=None): | |
16 | """Return message translation function (gettext) | |
17 | ||
18 | Parameters: | |
19 | i18n - translation service, such as roundup.i18n module | |
20 | or TranslationService object. | |
21 | ||
22 | Return ``gettext`` attribute of the ``i18n`` object, if available | |
23 | (must be a message translation function with one argument). | |
24 | If ``gettext`` cannot be obtained from ``i18n``, take default | |
25 | TranslationService. | |
26 | ||
27 | """ | |
28 | try: | |
29 | return i18n.gettext | |
30 | except: | |
31 | return TranslationService.get_translation().gettext | |
32 | ||
33 | def breaker(): | |
34 | return ('<body bgcolor="white">' + | |
35 | '<font color="white" size="-5"> > </font> ' + | |
36 | '</table>' * 5) | |
37 | ||
38 | def niceDict(indent, dict): | |
39 | l = [] | |
40 | for k in sorted(dict): | |
41 | v = dict[k] | |
42 | l.append('<tr><td><strong>%s</strong></td><td>%s</td></tr>'%(k, | |
43 | cgi.escape(repr(v)))) | |
44 | return '\n'.join(l) | |
45 | ||
46 | def pt_html(context=5, i18n=None): | |
47 | _ = get_translator(i18n) | |
48 | esc = cgi.escape | |
49 | exc_info = [esc(str(value)) for value in sys.exc_info()[:2]] | |
50 | l = [_('<h1>Templating Error</h1>\n' | |
51 | '<p><b>%(exc_type)s</b>: %(exc_value)s</p>\n' | |
52 | '<p class="help">Debugging information follows</p>' | |
53 | ) % {'exc_type': exc_info[0], 'exc_value': exc_info[1]}, | |
54 | '<ol>',] | |
55 | from roundup.cgi.PageTemplates.Expressions import TraversalError | |
56 | t = inspect.trace(context) | |
57 | t.reverse() | |
58 | for frame, file, lnum, func, lines, index in t: | |
59 | args, varargs, varkw, locals = inspect.getargvalues(frame) | |
60 | if '__traceback_info__' in locals: | |
61 | ti = locals['__traceback_info__'] | |
62 | if isinstance(ti, TraversalError): | |
63 | s = [] | |
64 | for name, info in ti.path: | |
65 | s.append(_('<li>"%(name)s" (%(info)s)</li>') | |
66 | % {'name': name, 'info': esc(repr(info))}) | |
67 | s = '\n'.join(s) | |
68 | l.append(_('<li>Looking for "%(name)s", ' | |
69 | 'current path:<ol>%(path)s</ol></li>' | |
70 | ) % {'name': ti.name, 'path': s}) | |
71 | else: | |
72 | l.append(_('<li>In %s</li>') % esc(str(ti))) | |
73 | if '__traceback_supplement__' in locals: | |
74 | ts = locals['__traceback_supplement__'] | |
75 | if len(ts) == 2: | |
76 | supp, context = ts | |
77 | s = _('A problem occurred in your template "%s".') \ | |
78 | % str(context.id) | |
79 | if context._v_errors: | |
80 | s = s + '<br>' + '<br>'.join( | |
81 | [esc(x) for x in context._v_errors]) | |
82 | l.append('<li>%s</li>'%s) | |
83 | elif len(ts) == 3: | |
84 | supp, context, info = ts | |
85 | l.append(_(''' | |
86 | <li>While evaluating the %(info)r expression on line %(line)d | |
87 | <table class="otherinfo" style="font-size: 90%%"> | |
88 | <tr><th colspan="2" class="header">Current variables:</th></tr> | |
89 | %(globals)s | |
90 | %(locals)s | |
91 | </table></li> | |
92 | ''') % { | |
93 | 'info': info, | |
94 | 'line': context.position[0], | |
95 | 'globals': niceDict(' ', context.global_vars), | |
96 | 'locals': niceDict(' ', context.local_vars) | |
97 | }) | |
98 | ||
99 | l.append(''' | |
100 | </ol> | |
101 | <table style="font-size: 80%%; color: gray"> | |
102 | <tr><th class="header" align="left">%s</th></tr> | |
103 | <tr><td><pre>%s</pre></td></tr> | |
104 | </table>''' % (_('Full traceback:'), cgi.escape(''.join( | |
105 | traceback.format_exception(*sys.exc_info()) | |
106 | )))) | |
107 | l.append('<p> </p>') | |
108 | return '\n'.join(l) | |
109 | ||
110 | def html(context=5, i18n=None): | |
111 | _ = get_translator(i18n) | |
112 | etype, evalue = sys.exc_info()[0], sys.exc_info()[1] | |
113 | if type(etype) is type: | |
114 | etype = etype.__name__ | |
115 | pyver = 'Python ' + string.split(sys.version)[0] + '<br>' + sys.executable | |
116 | head = pydoc.html.heading( | |
117 | _('<font size=+1><strong>%(exc_type)s</strong>: %(exc_value)s</font>') | |
118 | % {'exc_type': etype, 'exc_value': evalue}, | |
119 | '#ffffff', '#777777', pyver) | |
120 | ||
121 | head = head + (_('<p>A problem occurred while running a Python script. ' | |
122 | 'Here is the sequence of function calls leading up to ' | |
123 | 'the error, with the most recent (innermost) call first. ' | |
124 | 'The exception attributes are:')) | |
125 | ||
126 | indent = '<tt><small>%s</small> </tt>' % (' ' * 5) | |
127 | traceback = [] | |
128 | for frame, file, lnum, func, lines, index in inspect.trace(context): | |
129 | if file is None: | |
130 | link = _("<file is None - probably inside <tt>eval</tt> " | |
131 | "or <tt>exec</tt>>") | |
132 | else: | |
133 | file = os.path.abspath(file) | |
134 | link = '<a href="file:%s">%s</a>' % (file, pydoc.html.escape(file)) | |
135 | args, varargs, varkw, locals = inspect.getargvalues(frame) | |
136 | if func == '?': | |
137 | call = '' | |
138 | else: | |
139 | call = _('in <strong>%s</strong>') % func + inspect.formatargvalues( | |
140 | args, varargs, varkw, locals, | |
141 | formatvalue=lambda value: '=' + pydoc.html.repr(value)) | |
142 | ||
143 | level = ''' | |
144 | <table width="100%%" bgcolor="#dddddd" cellspacing=0 cellpadding=2 border=0> | |
145 | <tr><td>%s %s</td></tr></table>''' % (link, call) | |
146 | ||
147 | if index is None or file is None: | |
148 | traceback.append('<p>' + level) | |
149 | continue | |
150 | ||
151 | # do a file inspection | |
152 | names = [] | |
153 | def tokeneater(type, token, start, end, line, names=names): | |
154 | if type == tokenize.NAME and token not in keyword.kwlist: | |
155 | if token not in names: | |
156 | names.append(token) | |
157 | if type == tokenize.NEWLINE: raise IndexError | |
158 | def linereader(file=file, lnum=[lnum]): | |
159 | line = linecache.getline(file, lnum[0]) | |
160 | lnum[0] = lnum[0] + 1 | |
161 | return line | |
162 | ||
163 | try: | |
164 | tokenize.tokenize(linereader, tokeneater) | |
165 | except IndexError: | |
166 | pass | |
167 | lvals = [] | |
168 | for name in names: | |
169 | if name in frame.f_code.co_varnames: | |
170 | if name in locals: | |
171 | value = pydoc.html.repr(locals[name]) | |
172 | else: | |
173 | value = _('<em>undefined</em>') | |
174 | name = '<strong>%s</strong>' % name | |
175 | else: | |
176 | if name in frame.f_globals: | |
177 | value = pydoc.html.repr(frame.f_globals[name]) | |
178 | else: | |
179 | value = _('<em>undefined</em>') | |
180 | name = '<em>global</em> <strong>%s</strong>' % name | |
181 | lvals.append('%s = %s'%(name, value)) | |
182 | if lvals: | |
183 | lvals = string.join(lvals, ', ') | |
184 | lvals = indent + '<small><font color="#909090">%s'\ | |
185 | '</font></small><br>'%lvals | |
186 | else: | |
187 | lvals = '' | |
188 | ||
189 | excerpt = [] | |
190 | i = lnum - index | |
191 | for line in lines: | |
192 | number = ' ' * (5-len(str(i))) + str(i) | |
193 | number = '<small><font color="#909090">%s</font></small>' % number | |
194 | line = '<tt>%s %s</tt>' % (number, pydoc.html.preformat(line)) | |
195 | if i == lnum: | |
196 | line = ''' | |
197 | <table width="100%%" bgcolor="white" cellspacing=0 cellpadding=0 border=0> | |
198 | <tr><td>%s</td></tr></table>''' % line | |
199 | excerpt.append('\n' + line) | |
200 | if i == lnum: | |
201 | excerpt.append(lvals) | |
202 | i = i + 1 | |
203 | traceback.append('<p>' + level + string.join(excerpt, '\n')) | |
204 | ||
205 | traceback.reverse() | |
206 | ||
207 | exception = '<p><strong>%s</strong>: %s' % (str(etype), str(evalue)) | |
208 | attribs = [] | |
209 | if type(evalue) is types.InstanceType: | |
210 | for name in dir(evalue): | |
211 | value = pydoc.html.repr(getattr(evalue, name)) | |
212 | attribs.append('<br>%s%s = %s' % (indent, name, value)) | |
213 | ||
214 | return head + string.join(attribs) + string.join(traceback) + '<p> </p>' | |
215 | ||
216 | def handler(): | |
217 | print breaker() | |
218 | print html() | |
219 | ||
220 | # vim: set filetype=python ts=4 sw=4 et si : |