Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/mako/exceptions.py: 32%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# mako/exceptions.py
2# Copyright 2006-2026 the Mako authors and contributors <see AUTHORS file>
3#
4# This module is part of Mako and is released under
5# the MIT License: http://www.opensource.org/licenses/mit-license.php
7"""exception classes"""
9import sys
10import traceback
12from mako import compat
13from mako import util
16class MakoException(Exception):
17 pass
20class RuntimeException(MakoException):
21 pass
24def _format_filepos(lineno, pos, filename):
25 if filename is None:
26 return " at line: %d char: %d" % (lineno, pos)
27 else:
28 return " in file '%s' at line: %d char: %d" % (filename, lineno, pos)
31class CompileException(MakoException):
32 def __init__(self, message, source, lineno, pos, filename):
33 MakoException.__init__(
34 self, message + _format_filepos(lineno, pos, filename)
35 )
36 self.lineno = lineno
37 self.pos = pos
38 self.filename = filename
39 self.source = source
42class SyntaxException(MakoException):
43 def __init__(self, message, source, lineno, pos, filename):
44 MakoException.__init__(
45 self, message + _format_filepos(lineno, pos, filename)
46 )
47 self.lineno = lineno
48 self.pos = pos
49 self.filename = filename
50 self.source = source
53class UnsupportedError(MakoException):
54 """raised when a retired feature is used."""
57class NameConflictError(MakoException):
58 """raised when a reserved word is used inappropriately"""
61class TemplateLookupException(MakoException):
62 pass
65class TopLevelLookupException(TemplateLookupException):
66 pass
69class RichTraceback:
70 """Pull the current exception from the ``sys`` traceback and extracts
71 Mako-specific template information.
73 See the usage examples in :ref:`handling_exceptions`.
75 """
77 def __init__(self, error=None, traceback=None):
78 self.source, self.lineno = "", 0
80 if error is None or traceback is None:
81 t, value, tback = sys.exc_info()
83 if error is None:
84 error = value or t
86 if traceback is None:
87 traceback = tback
89 self.error = error
90 self.records = self._init(traceback)
92 if isinstance(self.error, (CompileException, SyntaxException)):
93 self.source = self.error.source
94 self.lineno = self.error.lineno
95 self._has_source = True
97 self._init_message()
99 @property
100 def errorname(self):
101 return compat.exception_name(self.error)
103 def _init_message(self):
104 """Find a unicode representation of self.error"""
105 try:
106 self.message = str(self.error)
107 except UnicodeError:
108 try:
109 self.message = str(self.error)
110 except UnicodeEncodeError:
111 # Fallback to args as neither unicode nor
112 # str(Exception(u'\xe6')) work in Python < 2.6
113 self.message = self.error.args[0]
114 if not isinstance(self.message, str):
115 self.message = str(self.message, "ascii", "replace")
117 def _get_reformatted_records(self, records):
118 for rec in records:
119 if rec[6] is not None:
120 yield (rec[4], rec[5], rec[2], rec[6])
121 else:
122 yield tuple(rec[0:4])
124 @property
125 def traceback(self):
126 """Return a list of 4-tuple traceback records (i.e. normal python
127 format) with template-corresponding lines remapped to the originating
128 template.
130 """
131 return list(self._get_reformatted_records(self.records))
133 @property
134 def reverse_records(self):
135 return reversed(self.records)
137 @property
138 def reverse_traceback(self):
139 """Return the same data as traceback, except in reverse order."""
141 return list(self._get_reformatted_records(self.reverse_records))
143 def _init(self, trcback):
144 """format a traceback from sys.exc_info() into 7-item tuples,
145 containing the regular four traceback tuple items, plus the original
146 template filename, the line number adjusted relative to the template
147 source, and code line from that line number of the template."""
149 import mako.template
151 mods = {}
152 rawrecords = traceback.extract_tb(trcback)
153 new_trcback = []
154 for filename, lineno, function, line in rawrecords:
155 if not line:
156 line = ""
157 try:
158 line_map, template_lines, template_filename = mods[filename]
159 except KeyError:
160 try:
161 info = mako.template._get_module_info(filename)
162 module_source = info.code
163 template_source = info.source
164 template_filename = (
165 info.template_filename or info.template_uri or filename
166 )
167 except KeyError:
168 # A normal .py file (not a Template)
169 new_trcback.append(
170 (
171 filename,
172 lineno,
173 function,
174 line,
175 None,
176 None,
177 None,
178 None,
179 )
180 )
181 continue
183 template_ln = 1
185 mtm = mako.template.ModuleInfo
186 source_map = mtm.get_module_source_metadata(
187 module_source, full_line_map=True
188 )
189 line_map = source_map["full_line_map"]
191 template_lines = [
192 line_ for line_ in template_source.split("\n")
193 ]
194 mods[filename] = (line_map, template_lines, template_filename)
196 template_ln = line_map[lineno - 1]
198 if template_ln <= len(template_lines):
199 template_line = template_lines[template_ln - 1]
200 else:
201 template_line = None
202 new_trcback.append(
203 (
204 filename,
205 lineno,
206 function,
207 line,
208 template_filename,
209 template_ln,
210 template_line,
211 template_source,
212 )
213 )
214 if not self.source:
215 for l in range(len(new_trcback) - 1, 0, -1):
216 if new_trcback[l][5]:
217 self.source = new_trcback[l][7]
218 self.lineno = new_trcback[l][5]
219 break
220 else:
221 if new_trcback:
222 try:
223 # A normal .py file (not a Template)
224 with open(new_trcback[-1][0], "rb") as fp:
225 encoding = util.parse_encoding(fp)
226 if not encoding:
227 encoding = "utf-8"
228 fp.seek(0)
229 self.source = fp.read()
230 if encoding:
231 self.source = self.source.decode(encoding)
232 except IOError:
233 self.source = ""
234 self.lineno = new_trcback[-1][1]
235 return new_trcback
238def text_error_template(lookup=None):
239 """Provides a template that renders a stack trace in a similar format to
240 the Python interpreter, substituting source template filenames, line
241 numbers and code for that of the originating source template, as
242 applicable.
244 """
245 import mako.template
247 return mako.template.Template(r"""
248<%page args="error=None, traceback=None"/>
249<%!
250 from mako.exceptions import RichTraceback
251%>\
252<%
253 tback = RichTraceback(error=error, traceback=traceback)
254%>\
255Traceback (most recent call last):
256% for (filename, lineno, function, line) in tback.traceback:
257 File "${filename}", line ${lineno}, in ${function or '?'}
258 ${line | trim}
259% endfor
260${tback.errorname}: ${tback.message}
261""")
264def _install_pygments():
265 global syntax_highlight, pygments_html_formatter
266 from mako.ext.pygmentplugin import syntax_highlight # noqa
267 from mako.ext.pygmentplugin import pygments_html_formatter # noqa
270def _install_fallback():
271 global syntax_highlight, pygments_html_formatter
272 from mako.filters import html_escape
274 pygments_html_formatter = None
276 def syntax_highlight(filename="", language=None):
277 return html_escape
280def _install_highlighting():
281 try:
282 _install_pygments()
283 except ImportError:
284 _install_fallback()
287_install_highlighting()
290def html_error_template():
291 """Provides a template that renders a stack trace in an HTML format,
292 providing an excerpt of code as well as substituting source template
293 filenames, line numbers and code for that of the originating source
294 template, as applicable.
296 The template's default ``encoding_errors`` value is
297 ``'htmlentityreplace'``. The template has two options. With the
298 ``full`` option disabled, only a section of an HTML document is
299 returned. With the ``css`` option disabled, the default stylesheet
300 won't be included.
302 """
303 import mako.template
305 return mako.template.Template(
306 r"""
307<%!
308 from mako.exceptions import RichTraceback, syntax_highlight,\
309 pygments_html_formatter
310%>
311<%page args="full=True, css=True, error=None, traceback=None"/>
312% if full:
313<html>
314<head>
315 <title>Mako Runtime Error</title>
316% endif
317% if css:
318 <style>
319 body { font-family:verdana; margin:10px 30px 10px 30px;}
320 .stacktrace { margin:5px 5px 5px 5px; }
321 .highlight { padding:0px 10px 0px 10px; background-color:#9F9FDF; }
322 .nonhighlight { padding:0px; background-color:#DFDFDF; }
323 .sample { padding:10px; margin:10px 10px 10px 10px;
324 font-family:monospace; }
325 .sampleline { padding:0px 10px 0px 10px; }
326 .sourceline { margin:5px 5px 10px 5px; font-family:monospace;}
327 .location { font-size:80%; }
328 .highlight { white-space:pre; }
329 .sampleline { white-space:pre; }
331 % if pygments_html_formatter:
332 ${pygments_html_formatter.get_style_defs()}
333 .linenos { min-width: 2.5em; text-align: right; }
334 pre { margin: 0; }
335 .syntax-highlighted { padding: 0 10px; }
336 .syntax-highlightedtable { border-spacing: 1px; }
337 .nonhighlight { border-top: 1px solid #DFDFDF;
338 border-bottom: 1px solid #DFDFDF; }
339 .stacktrace .nonhighlight { margin: 5px 15px 10px; }
340 .sourceline { margin: 0 0; font-family:monospace; }
341 .code { background-color: #F8F8F8; width: 100%; }
342 .error .code { background-color: #FFBDBD; }
343 .error .syntax-highlighted { background-color: #FFBDBD; }
344 % endif
346 </style>
347% endif
348% if full:
349</head>
350<body>
351% endif
353<h2>Error !</h2>
354<%
355 tback = RichTraceback(error=error, traceback=traceback)
356 src = tback.source
357 line = tback.lineno
358 if src:
359 lines = src.split('\n')
360 else:
361 lines = None
362%>
363<h3>${tback.errorname}: ${tback.message|h}</h3>
365% if lines:
366 <div class="sample">
367 <div class="nonhighlight">
368% for index in range(max(0, line-4),min(len(lines), line+5)):
369 <%
370 if pygments_html_formatter:
371 pygments_html_formatter.linenostart = index + 1
372 %>
373 % if index + 1 == line:
374 <%
375 if pygments_html_formatter:
376 old_cssclass = pygments_html_formatter.cssclass
377 pygments_html_formatter.cssclass = 'error ' + old_cssclass
378 %>
379 ${lines[index] | syntax_highlight(language='mako')}
380 <%
381 if pygments_html_formatter:
382 pygments_html_formatter.cssclass = old_cssclass
383 %>
384 % else:
385 ${lines[index] | syntax_highlight(language='mako')}
386 % endif
387% endfor
388 </div>
389 </div>
390% endif
392<div class="stacktrace">
393% for (filename, lineno, function, line) in tback.reverse_traceback:
394 <div class="location">${filename}, line ${lineno}:</div>
395 <div class="nonhighlight">
396 <%
397 if pygments_html_formatter:
398 pygments_html_formatter.linenostart = lineno
399 %>
400 <div class="sourceline">${line | syntax_highlight(filename)}</div>
401 </div>
402% endfor
403</div>
405% if full:
406</body>
407</html>
408% endif
409""",
410 output_encoding=sys.getdefaultencoding(),
411 encoding_errors="htmlentityreplace",
412 )