~jonas-drange/ubuntu-start-page/1252899-mobile-friendly

« back to all changes in this revision

Viewing changes to src/mako/exceptions.py

  • Committer: Matthew Nuzum
  • Date: 2008-04-18 01:58:53 UTC
  • Revision ID: matthew.nuzum@canonical.com-20080418015853-2b8rf979z2c2exxl
adding files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# exceptions.py
 
2
# Copyright (C) 2006, 2007 Michael Bayer mike_mp@zzzcomputing.com
 
3
#
 
4
# This module is part of Mako and is released under
 
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
 
6
 
 
7
"""exception classes"""
 
8
 
 
9
import traceback, sys, re
 
10
 
 
11
class MakoException(Exception):
 
12
    pass
 
13
 
 
14
class RuntimeException(MakoException):
 
15
    pass
 
16
 
 
17
def _format_filepos(lineno, pos, filename):
 
18
    if filename is None:
 
19
        return " at line: %d char: %d" % (lineno, pos)
 
20
    else:
 
21
        return " in file '%s' at line: %d char: %d" % (filename, lineno, pos)     
 
22
class CompileException(MakoException):
 
23
    def __init__(self, message, source, lineno, pos, filename):
 
24
        MakoException.__init__(self, message + _format_filepos(lineno, pos, filename))
 
25
        self.lineno =lineno
 
26
        self.pos = pos
 
27
        self.filename = filename
 
28
        self.source = source
 
29
                    
 
30
class SyntaxException(MakoException):
 
31
    def __init__(self, message, source, lineno, pos, filename):
 
32
        MakoException.__init__(self, message + _format_filepos(lineno, pos, filename))
 
33
        self.lineno =lineno
 
34
        self.pos = pos
 
35
        self.filename = filename
 
36
        self.source = source
 
37
        
 
38
class TemplateLookupException(MakoException):
 
39
    pass
 
40
 
 
41
class TopLevelLookupException(TemplateLookupException):
 
42
    pass
 
43
    
 
44
class RichTraceback(object):
 
45
    """pulls the current exception from the sys traceback and extracts Mako-specific 
 
46
    template information.
 
47
    
 
48
    Usage:
 
49
    
 
50
    RichTraceback()
 
51
    
 
52
    Properties:
 
53
    
 
54
    error - the exception instance.  
 
55
    source - source code of the file where the error occured.  if the error occured within a compiled template,
 
56
    this is the template source.
 
57
    lineno - line number where the error occured.  if the error occured within a compiled template, the line number
 
58
    is adjusted to that of the template source
 
59
    records - a list of 8-tuples containing the original python traceback elements, plus the 
 
60
    filename, line number, source line, and full template source for the traceline mapped back to its originating source
 
61
    template, if any for that traceline (else the fields are None).
 
62
    reverse_records - the list of records in reverse
 
63
    traceback - a list of 4-tuples, in the same format as a regular python traceback, with template-corresponding 
 
64
    traceback records replacing the originals
 
65
    reverse_traceback - the traceback list in reverse
 
66
    """
 
67
    def __init__(self):
 
68
        (self.source, self.lineno) = ("", 0)
 
69
        (t, self.error, self.records) = self._init()
 
70
        if self.error is None:
 
71
            self.error = t
 
72
        if isinstance(self.error, CompileException) or isinstance(self.error, SyntaxException):
 
73
            import mako.template
 
74
            self.source = self.error.source
 
75
            self.lineno = self.error.lineno
 
76
            self._has_source = True
 
77
        self.reverse_records = [r for r in self.records]
 
78
        self.reverse_records.reverse()
 
79
    def _get_reformatted_records(self, records):
 
80
        for rec in records:
 
81
            if rec[6] is not None:
 
82
                yield (rec[4], rec[5], rec[2], rec[6])
 
83
            else:
 
84
                yield tuple(rec[0:4])
 
85
    traceback = property(lambda self:self._get_reformatted_records(self.records), doc="""
 
86
        return a list of 4-tuple traceback records (i.e. normal python format)
 
87
        with template-corresponding lines remapped to the originating template
 
88
    """)
 
89
    reverse_traceback = property(lambda self:self._get_reformatted_records(self.reverse_records), doc="""
 
90
        return the same data as traceback, except in reverse order
 
91
    """)
 
92
    def _init(self):
 
93
        """format a traceback from sys.exc_info() into 7-item tuples, containing
 
94
        the regular four traceback tuple items, plus the original template 
 
95
        filename, the line number adjusted relative to the template source, and
 
96
        code line from that line number of the template."""
 
97
        import mako.template
 
98
        mods = {}
 
99
        (type, value, trcback) = sys.exc_info()
 
100
        rawrecords = traceback.extract_tb(trcback)
 
101
        new_trcback = []
 
102
        for filename, lineno, function, line in rawrecords:
 
103
            try:
 
104
                (line_map, template_lines) = mods[filename]
 
105
            except KeyError:
 
106
                try:
 
107
                    info = mako.template._get_module_info(filename)
 
108
                    module_source = info.code
 
109
                    template_source = info.source
 
110
                    template_filename = info.template_filename or filename
 
111
                except KeyError:
 
112
                    new_trcback.append((filename, lineno, function, line, None, None, None, None))
 
113
                    continue
 
114
 
 
115
                template_ln = module_ln = 1
 
116
                line_map = {}
 
117
                for line in module_source.split("\n"):
 
118
                    match = re.match(r'\s*# SOURCE LINE (\d+)', line)
 
119
                    if match:
 
120
                        template_ln = int(match.group(1))
 
121
                    else:
 
122
                        template_ln += 1
 
123
                    module_ln += 1
 
124
                    line_map[module_ln] = template_ln
 
125
                template_lines = [line for line in template_source.split("\n")]
 
126
                mods[filename] = (line_map, template_lines)
 
127
 
 
128
            template_ln = line_map[lineno]
 
129
            if template_ln <= len(template_lines):
 
130
                template_line = template_lines[template_ln - 1]
 
131
            else:
 
132
                template_line = None
 
133
            new_trcback.append((filename, lineno, function, line, template_filename, template_ln, template_line, template_source))
 
134
        if not self.source:
 
135
            for l in range(len(new_trcback)-1, 0, -1):
 
136
                if new_trcback[l][5]:
 
137
                    self.source = new_trcback[l][7]
 
138
                    self.lineno = new_trcback[l][5]
 
139
                    break
 
140
            else:
 
141
                try:
 
142
                    self.source = file(new_trcback[-1][0]).read()
 
143
                except IOError:
 
144
                    self.source = ''
 
145
                self.lineno = new_trcback[-1][1]
 
146
        return (type, value, new_trcback)
 
147
 
 
148
                
 
149
def text_error_template(lookup=None):
 
150
    """provides a template that renders a stack trace in a similar format to the Python interpreter,
 
151
    substituting source template filenames, line numbers and code for that of the originating
 
152
    source template, as applicable."""
 
153
    import mako.template
 
154
    return mako.template.Template(r"""
 
155
<%!
 
156
    from mako.exceptions import RichTraceback
 
157
%>\
 
158
<%
 
159
    tback = RichTraceback()
 
160
%>\
 
161
Traceback (most recent call last):
 
162
% for (filename, lineno, function, line) in tback.traceback:
 
163
  File "${filename}", line ${lineno}, in ${function or '?'}
 
164
    ${line | unicode.strip}
 
165
% endfor
 
166
${str(tback.error.__class__.__name__)}: ${str(tback.error)}
 
167
""")
 
168
 
 
169
def html_error_template():
 
170
    """provides a template that renders a stack trace in an HTML format, providing an excerpt of 
 
171
    code as well as substituting source template filenames, line numbers and code 
 
172
    for that of the originating source template, as applicable.
 
173
 
 
174
    the template's default encoding_errors value is 'htmlentityreplace'. the template has
 
175
    two options:
 
176
 
 
177
    with the full option disabled, only a section of an HTML document is returned.
 
178
    with the css option disabled, the default stylesheet won't be included."""
 
179
    import mako.template
 
180
    return mako.template.Template(r"""
 
181
<%!
 
182
    from mako.exceptions import RichTraceback
 
183
%>
 
184
<%page args="full=True, css=True"/>
 
185
% if full:
 
186
<html>
 
187
<head>
 
188
    <title>Mako Runtime Error</title>
 
189
% endif
 
190
% if css:
 
191
    <style>
 
192
        body { font-family:verdana; margin:10px 30px 10px 30px;}
 
193
        .stacktrace { margin:5px 5px 5px 5px; }
 
194
        .highlight { padding:0px 10px 0px 10px; background-color:#9F9FDF; }
 
195
        .nonhighlight { padding:0px; background-color:#DFDFDF; }
 
196
        .sample { padding:10px; margin:10px 10px 10px 10px; font-family:monospace; }
 
197
        .sampleline { padding:0px 10px 0px 10px; }
 
198
        .sourceline { margin:5px 5px 10px 5px; font-family:monospace;}
 
199
        .location { font-size:80%; }
 
200
    </style>
 
201
% endif
 
202
% if full:
 
203
</head>
 
204
<body>
 
205
% endif
 
206
 
 
207
<h2>Error !</h2>
 
208
<%
 
209
    tback = RichTraceback()
 
210
    src = tback.source
 
211
    line = tback.lineno
 
212
    if src:
 
213
        lines = src.split('\n')
 
214
    else:
 
215
        lines = None
 
216
%>
 
217
<h3>${str(tback.error.__class__.__name__)}: ${str(tback.error)}</h3>
 
218
 
 
219
% if lines:
 
220
    <div class="sample">
 
221
    <div class="nonhighlight">
 
222
% for index in range(max(0, line-4),min(len(lines), line+5)):
 
223
    % if index + 1 == line:
 
224
<div class="highlight">${index + 1} ${lines[index] | h}</div>
 
225
    % else:
 
226
<div class="sampleline">${index + 1} ${lines[index] | h}</div>
 
227
    % endif
 
228
% endfor
 
229
    </div>
 
230
    </div>
 
231
% endif
 
232
 
 
233
<div class="stacktrace">
 
234
% for (filename, lineno, function, line) in tback.reverse_traceback:
 
235
    <div class="location">${filename}, line ${lineno}:</div>
 
236
    <div class="sourceline">${line | h}</div>
 
237
% endfor
 
238
</div>
 
239
 
 
240
% if full:
 
241
</body>
 
242
</html>
 
243
% endif
 
244
""", output_encoding=sys.getdefaultencoding(), encoding_errors='htmlentityreplace')