root/galaxy-central/eggs/Paste-1.6-py2.6.egg/paste/exceptions/formatter.py

リビジョン 3, 19.0 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3
4"""
5Formatters for the exception data that comes from ExceptionCollector.
6"""
7# @@: TODO:
8# Use this: http://www.zope.org/Members/tino/VisualTraceback/VisualTracebackNews
9
10import cgi
11import re
12from paste.util import PySourceColor
13
14def html_quote(s):
15    return cgi.escape(str(s), True)
16
17class AbstractFormatter(object):
18
19    general_data_order = ['object', 'source_url']
20
21    def __init__(self, show_hidden_frames=False,
22                 include_reusable=True,
23                 show_extra_data=True,
24                 trim_source_paths=()):
25        self.show_hidden_frames = show_hidden_frames
26        self.trim_source_paths = trim_source_paths
27        self.include_reusable = include_reusable
28        self.show_extra_data = show_extra_data
29
30    def format_collected_data(self, exc_data):
31        general_data = {}
32        if self.show_extra_data:
33            for name, value_list in exc_data.extra_data.items():
34                if isinstance(name, tuple):
35                    importance, title = name
36                else:
37                    importance, title = 'normal', name
38                for value in value_list:
39                    general_data[(importance, name)] = self.format_extra_data(
40                        importance, title, value)
41        lines = []
42        frames = self.filter_frames(exc_data.frames)
43        for frame in frames:
44            sup = frame.supplement
45            if sup:
46                if sup.object:
47                    general_data[('important', 'object')] = self.format_sup_object(
48                        sup.object)
49                if sup.source_url:
50                    general_data[('important', 'source_url')] = self.format_sup_url(
51                        sup.source_url)
52                if sup.line:
53                    lines.append(self.format_sup_line_pos(sup.line, sup.column))
54                if sup.expression:
55                    lines.append(self.format_sup_expression(sup.expression))
56                if sup.warnings:
57                    for warning in sup.warnings:
58                        lines.append(self.format_sup_warning(warning))
59                if sup.info:
60                    lines.extend(self.format_sup_info(sup.info))
61            if frame.supplement_exception:
62                lines.append('Exception in supplement:')
63                lines.append(self.quote_long(frame.supplement_exception))
64            if frame.traceback_info:
65                lines.append(self.format_traceback_info(frame.traceback_info))
66            filename = frame.filename
67            if filename and self.trim_source_paths:
68                for path, repl in self.trim_source_paths:
69                    if filename.startswith(path):
70                        filename = repl + filename[len(path):]
71                        break
72            lines.append(self.format_source_line(filename or '?', frame))
73            source = frame.get_source_line()
74            long_source = frame.get_source_line(2)
75            if source:
76                lines.append(self.format_long_source(
77                    source, long_source))
78        etype = exc_data.exception_type
79        if not isinstance(etype, basestring):
80            etype = etype.__name__
81        exc_info = self.format_exception_info(
82            etype,
83            exc_data.exception_value)
84        data_by_importance = {'important': [], 'normal': [],
85                              'supplemental': [], 'extra': []}
86        for (importance, name), value in general_data.items():
87            data_by_importance[importance].append(
88                (name, value))
89        for value in data_by_importance.values():
90            value.sort()
91        return self.format_combine(data_by_importance, lines, exc_info)
92
93    def filter_frames(self, frames):
94        """
95        Removes any frames that should be hidden, according to the
96        values of traceback_hide, self.show_hidden_frames, and the
97        hidden status of the final frame.
98        """
99        if self.show_hidden_frames:
100            return frames
101        new_frames = []
102        hidden = False
103        for frame in frames:
104            hide = frame.traceback_hide
105            # @@: It would be nice to signal a warning if an unknown
106            # hide string was used, but I'm not sure where to put
107            # that warning.
108            if hide == 'before':
109                new_frames = []
110                hidden = False
111            elif hide == 'before_and_this':
112                new_frames = []
113                hidden = False
114                continue
115            elif hide == 'reset':
116                hidden = False
117            elif hide == 'reset_and_this':
118                hidden = False
119                continue
120            elif hide == 'after':
121                hidden = True
122            elif hide == 'after_and_this':
123                hidden = True
124                continue
125            elif hide:
126                continue
127            elif hidden:
128                continue
129            new_frames.append(frame)
130        if frames[-1] not in new_frames:
131            # We must include the last frame; that we don't indicates
132            # that the error happened where something was "hidden",
133            # so we just have to show everything
134            return frames
135        return new_frames
136
137    def pretty_string_repr(self, s):
138        """
139        Formats the string as a triple-quoted string when it contains
140        newlines.
141        """
142        if '\n' in s:
143            s = repr(s)
144            s = s[0]*3 + s[1:-1] + s[-1]*3
145            s = s.replace('\\n', '\n')
146            return s
147        else:
148            return repr(s)
149
150    def long_item_list(self, lst):
151        """
152        Returns true if the list contains items that are long, and should
153        be more nicely formatted.
154        """
155        how_many = 0
156        for item in lst:
157            if len(repr(item)) > 40:
158                how_many += 1
159                if how_many >= 3:
160                    return True
161        return False
162
163class TextFormatter(AbstractFormatter):
164
165    def quote(self, s):
166        return s
167    def quote_long(self, s):
168        return s
169    def emphasize(self, s):
170        return s
171    def format_sup_object(self, obj):
172        return 'In object: %s' % self.emphasize(self.quote(repr(obj)))
173    def format_sup_url(self, url):
174        return 'URL: %s' % self.quote(url)
175    def format_sup_line_pos(self, line, column):
176        if column:
177            return self.emphasize('Line %i, Column %i' % (line, column))
178        else:
179            return self.emphasize('Line %i' % line)
180    def format_sup_expression(self, expr):
181        return self.emphasize('In expression: %s' % self.quote(expr))
182    def format_sup_warning(self, warning):
183        return 'Warning: %s' % self.quote(warning)
184    def format_sup_info(self, info):
185        return [self.quote_long(info)]
186    def format_source_line(self, filename, frame):
187        return 'File %r, line %s in %s' % (
188            filename, frame.lineno or '?', frame.name or '?')
189    def format_long_source(self, source, long_source):
190        return self.format_source(source)
191    def format_source(self, source_line):
192        return '  ' + self.quote(source_line.strip())
193    def format_exception_info(self, etype, evalue):
194        return self.emphasize(
195            '%s: %s' % (self.quote(etype), self.quote(evalue)))
196    def format_traceback_info(self, info):
197        return info
198       
199    def format_combine(self, data_by_importance, lines, exc_info):
200        lines[:0] = [value for n, value in data_by_importance['important']]
201        lines.append(exc_info)
202        for name in 'normal', 'supplemental', 'extra':
203            lines.extend([value for n, value in data_by_importance[name]])
204        return self.format_combine_lines(lines)
205
206    def format_combine_lines(self, lines):
207        return '\n'.join(lines)
208
209    def format_extra_data(self, importance, title, value):
210        if isinstance(value, str):
211            s = self.pretty_string_repr(value)
212            if '\n' in s:
213                return '%s:\n%s' % (title, s)
214            else:
215                return '%s: %s' % (title, s)
216        elif isinstance(value, dict):
217            lines = ['\n', title, '-'*len(title)]
218            items = value.items()
219            items.sort()
220            for n, v in items:
221                try:
222                    v = repr(v)
223                except Exception, e:
224                    v = 'Cannot display: %s' % e
225                v = truncate(v)
226                lines.append('  %s: %s' % (n, v))
227            return '\n'.join(lines)
228        elif (isinstance(value, (list, tuple))
229              and self.long_item_list(value)):
230            parts = [truncate(repr(v)) for v in value]
231            return '%s: [\n    %s]' % (
232                title, ',\n    '.join(parts))
233        else:
234            return '%s: %s' % (title, truncate(repr(value)))
235
236class HTMLFormatter(TextFormatter):
237
238    def quote(self, s):
239        return html_quote(s)
240    def quote_long(self, s):
241        return '<pre>%s</pre>' % self.quote(s)
242    def emphasize(self, s):
243        return '<b>%s</b>' % s
244    def format_sup_url(self, url):
245        return 'URL: <a href="%s">%s</a>' % (url, url)
246    def format_combine_lines(self, lines):
247        return '<br>\n'.join(lines)
248    def format_source_line(self, filename, frame):
249        name = self.quote(frame.name or '?')
250        return 'Module <span class="module" title="%s">%s</span>:<b>%s</b> in <code>%s</code>' % (
251            filename, frame.modname or '?', frame.lineno or '?',
252            name)
253        return 'File %r, line %s in <tt>%s</tt>' % (
254            filename, frame.lineno, name)
255    def format_long_source(self, source, long_source):
256        q_long_source = str2html(long_source, False, 4, True)
257        q_source = str2html(source, True, 0, False)
258        return ('<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, \'long\')" href="#">&lt;&lt;&nbsp; </a>%s</code>'
259                '<code class="source" source-type="short"><a onclick="return switch_source(this, \'short\')" class="switch_source" href="#">&gt;&gt;&nbsp; </a>%s</code>'
260                % (q_long_source,
261                   q_source))
262    def format_source(self, source_line):
263        return '&nbsp;&nbsp;<code class="source">%s</code>' % self.quote(source_line.strip())
264    def format_traceback_info(self, info):
265        return '<pre>%s</pre>' % self.quote(info)
266
267    def format_extra_data(self, importance, title, value):
268        if isinstance(value, str):
269            s = self.pretty_string_repr(value)
270            if '\n' in s:
271                return '%s:<br><pre>%s</pre>' % (title, self.quote(s))
272            else:
273                return '%s: <tt>%s</tt>' % (title, self.quote(s))
274        elif isinstance(value, dict):
275            return self.zebra_table(title, value)
276        elif (isinstance(value, (list, tuple))
277              and self.long_item_list(value)):
278            return '%s: <tt>[<br>\n&nbsp; &nbsp; %s]</tt>' % (
279                title, ',<br>&nbsp; &nbsp; '.join(map(self.quote, map(repr, value))))
280        else:
281            return '%s: <tt>%s</tt>' % (title, self.quote(repr(value)))
282
283    def format_combine(self, data_by_importance, lines, exc_info):
284        lines[:0] = [value for n, value in data_by_importance['important']]
285        lines.append(exc_info)
286        for name in 'normal', 'supplemental':
287            lines.extend([value for n, value in data_by_importance[name]])
288        if data_by_importance['extra']:
289            lines.append(
290                '<script type="text/javascript">\nshow_button(\'extra_data\', \'extra data\');\n</script>\n' +
291                '<div id="extra_data" class="hidden-data">\n')
292            lines.extend([value for n, value in data_by_importance['extra']])
293            lines.append('</div>')
294        text = self.format_combine_lines(lines)
295        if self.include_reusable:
296            return error_css + hide_display_js + text
297        else:
298            # Usually because another error is already on this page,
299            # and so the js & CSS are unneeded
300            return text
301
302    def zebra_table(self, title, rows, table_class="variables"):
303        if isinstance(rows, dict):
304            rows = rows.items()
305            rows.sort()
306        table = ['<table class="%s">' % table_class,
307                 '<tr class="header"><th colspan="2">%s</th></tr>'
308                 % self.quote(title)]
309        odd = False
310        for name, value in rows:
311            try:
312                value = repr(value)
313            except Exception, e:
314                value = 'Cannot print: %s' % e
315            odd = not odd
316            table.append(
317                '<tr class="%s"><td>%s</td>'
318                % (odd and 'odd' or 'even', self.quote(name)))
319            table.append(
320                '<td><tt>%s</tt></td></tr>'
321                % make_wrappable(self.quote(truncate(value))))
322        table.append('</table>')
323        return '\n'.join(table)
324
325hide_display_js = r'''
326<script type="text/javascript">
327function hide_display(id) {
328    var el = document.getElementById(id);
329    if (el.className == "hidden-data") {
330        el.className = "";
331        return true;
332    } else {
333        el.className = "hidden-data";
334        return false;
335    }
336}
337document.write('<style type="text/css">\n');
338document.write('.hidden-data {display: none}\n');
339document.write('</style>\n');
340function show_button(toggle_id, name) {
341    document.write('<a href="#' + toggle_id
342        + '" onclick="javascript:hide_display(\'' + toggle_id
343        + '\')" class="button">' + name + '</a><br>');
344}
345
346function switch_source(el, hide_type) {
347    while (el) {
348        if (el.getAttribute &&
349            el.getAttribute('source-type') == hide_type) {
350            break;
351        }
352        el = el.parentNode;
353    }
354    if (! el) {
355        return false;
356    }
357    el.style.display = 'none';
358    if (hide_type == 'long') {
359        while (el) {
360            if (el.getAttribute &&
361                el.getAttribute('source-type') == 'short') {
362                break;
363            }
364            el = el.nextSibling;
365        }
366    } else {
367        while (el) {
368            if (el.getAttribute &&
369                el.getAttribute('source-type') == 'long') {
370                break;
371            }
372            el = el.previousSibling;
373        }
374    }
375    if (el) {
376        el.style.display = '';
377    }
378    return false;
379}
380
381</script>'''
382   
383
384error_css = """
385<style type="text/css">
386body {
387  font-family: Helvetica, sans-serif;
388}
389
390table {
391  width: 100%;
392}
393
394tr.header {
395  background-color: #006;
396  color: #fff;
397}
398
399tr.even {
400  background-color: #ddd;
401}
402
403table.variables td {
404  vertical-align: top;
405  overflow: auto;
406}
407
408a.button {
409  background-color: #ccc;
410  border: 2px outset #aaa;
411  color: #000;
412  text-decoration: none;
413}
414
415a.button:hover {
416  background-color: #ddd;
417}
418
419code.source {
420  color: #006;
421}
422
423a.switch_source {
424  color: #090;
425  text-decoration: none;
426}
427
428a.switch_source:hover {
429  background-color: #ddd;
430}
431
432.source-highlight {
433  background-color: #ff9;
434}
435
436</style>
437"""
438
439def format_html(exc_data, include_hidden_frames=False, **ops):
440    if not include_hidden_frames:
441        return HTMLFormatter(**ops).format_collected_data(exc_data)
442    short_er = format_html(exc_data, show_hidden_frames=False, **ops)
443    # @@: This should have a way of seeing if the previous traceback
444    # was actually trimmed at all
445    ops['include_reusable'] = False
446    ops['show_extra_data'] = False
447    long_er = format_html(exc_data, show_hidden_frames=True, **ops)
448    text_er = format_text(exc_data, show_hidden_frames=True, **ops)
449    return """
450    %s
451    <br>
452    <script type="text/javascript">
453    show_button('full_traceback', 'full traceback')
454    </script>
455    <div id="full_traceback" class="hidden-data">
456    %s
457    </div>
458    <br>
459    <script type="text/javascript">
460    show_button('text_version', 'text version')
461    </script>
462    <div id="text_version" class="hidden-data">
463    <textarea style="width: 100%%" rows=10 cols=60>%s</textarea>
464    </div>
465    """ % (short_er, long_er, cgi.escape(text_er))
466       
467def format_text(exc_data, **ops):
468    return TextFormatter(**ops).format_collected_data(exc_data)
469
470whitespace_re = re.compile(r'  +')
471pre_re = re.compile(r'</?pre.*?>')
472error_re = re.compile(r'<h3>ERROR: .*?</h3>')
473
474def str2html(src, strip=False, indent_subsequent=0,
475             highlight_inner=False):
476    """
477    Convert a string to HTML.  Try to be really safe about it,
478    returning a quoted version of the string if nothing else works.
479    """
480    try:
481        return _str2html(src, strip=strip,
482                         indent_subsequent=indent_subsequent,
483                         highlight_inner=highlight_inner)
484    except:
485        return html_quote(src)
486
487def _str2html(src, strip=False, indent_subsequent=0,
488              highlight_inner=False):
489    if strip:
490        src = src.strip()
491    orig_src = src
492    try:
493        src = PySourceColor.str2html(src, form='snip')
494        src = error_re.sub('', src)
495        src = pre_re.sub('', src)
496        src = re.sub(r'^[\n\r]{0,1}', '', src)
497        src = re.sub(r'[\n\r]{0,1}$', '', src)
498    except:
499        src = html_quote(orig_src)
500    lines = src.splitlines()
501    if len(lines) == 1:
502        return lines[0]
503    indent = ' '*indent_subsequent
504    for i in range(1, len(lines)):
505        lines[i] = indent+lines[i]
506        if highlight_inner and i == len(lines)/2:
507            lines[i] = '<span class="source-highlight">%s</span>' % lines[i]
508    src = '<br>\n'.join(lines)
509    src = whitespace_re.sub(
510        lambda m: '&nbsp;'*(len(m.group(0))-1) + ' ', src)
511    return src
512
513def truncate(string, limit=1000):
514    """
515    Truncate the string to the limit number of
516    characters
517    """
518    if len(string) > limit:
519        return string[:limit-20]+'...'+string[-17:]
520    else:
521        return string
522
523def make_wrappable(html, wrap_limit=60,
524                   split_on=';?&@!$#-/\\"\''):
525    # Currently using <wbr>, maybe should use &#8203;
526    #   http://www.cs.tut.fi/~jkorpela/html/nobr.html
527    if len(html) <= wrap_limit:
528        return html
529    words = html.split()
530    new_words = []
531    for word in words:
532        wrapped_word = ''
533        while len(word) > wrap_limit:
534            for char in split_on:
535                if char in word:
536                    first, rest = word.split(char, 1)
537                    wrapped_word += first+char+'<wbr>'
538                    word = rest
539                    break
540            else:
541                for i in range(0, len(word), wrap_limit):
542                    wrapped_word += word[i:i+wrap_limit]+'<wbr>'
543                word = ''
544        wrapped_word += word
545        new_words.append(wrapped_word)
546    return ' '.join(new_words)
547
548def make_pre_wrappable(html, wrap_limit=60,
549                       split_on=';?&@!$#-/\\"\''):
550    """
551    Like ``make_wrappable()`` but intended for text that will
552    go in a ``<pre>`` block, so wrap on a line-by-line basis.
553    """
554    lines = html.splitlines()
555    new_lines = []
556    for line in lines:
557        if len(line) > wrap_limit:
558            for char in split_on:
559                if char in line:
560                    parts = line.split(char)
561                    line = '<wbr>'.join(parts)
562                    break
563        new_lines.append(line)
564    return '\n'.join(lines)
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。