cleaning up exception formatting
diff --git a/examples/wsgi/htdocs/index.html b/examples/wsgi/htdocs/index.html index cd0129e..79a5af2 100644 --- a/examples/wsgi/htdocs/index.html +++ b/examples/wsgi/htdocs/index.html
@@ -1,5 +1,5 @@ <%inherit file="root.html"/> This is index.html - + c is ${c is not UNDEFINED and c or "undefined"}
diff --git a/examples/wsgi/run_wsgi.py b/examples/wsgi/run_wsgi.py index cacd065..675c718 100644 --- a/examples/wsgi/run_wsgi.py +++ b/examples/wsgi/run_wsgi.py
@@ -6,6 +6,7 @@ root = './' port = 8000 +error_style = 'html' # select 'text' for plaintext error reporting lookup = TemplateLookup(directories=[root + 'templates', root + 'htdocs'], filesystem_checks=True, module_directory='./modules') @@ -33,8 +34,12 @@ start_response("404 Not Found", []) return ["Cant find template '%s'" % uri] except: - start_response("200 OK", [('Content-type','text/html')]) - return [exceptions.html_error_template().render()] + if error_style == 'text': + start_response("200 OK", [('Content-type','text/plain')]) + return [exceptions.text_error_template().render()] + else: + start_response("200 OK", [('Content-type','text/html')]) + return [exceptions.html_error_template().render()] else: u = re.sub(r'^\/+', '', uri) filename = os.path.join(root, u)
diff --git a/lib/mako/exceptions.py b/lib/mako/exceptions.py index bc60f31..bb96ecc 100644 --- a/lib/mako/exceptions.py +++ b/lib/mako/exceptions.py
@@ -40,7 +40,28 @@ pass class RichTraceback(object): - """pulls the current exception from the sys traceback and extracts Mako-specific information.""" + """pulls the current exception from the sys traceback and extracts Mako-specific + template information. + + Usage: + + RichTraceback() + + Properties: + + error - the exception instance. + source - source code of the file where the error occured. if the error occured within a compiled template, + this is the template source. + lineno - line number where the error occured. if the error occured within a compiled template, the line number + is adjusted to that of the template source + records - a list of 8-tuples containing the original python traceback elements, plus the + filename, line number, source line, and full template source for the traceline mapped back to its originating source + template, if any for that traceline (else the fields are None). + reverse_records - the list of records in reverse + traceback - a list of 4-tuples, in the same format as a regular python traceback, with template-corresponding + traceback records replacing the originals + reverse_traceback - the traceback list in reverse + """ def __init__(self): (self.source, self.lineno) = ("", 0) (t, self.error, self.records) = self._init() @@ -49,8 +70,22 @@ if isinstance(self.error, CompileException) or isinstance(self.error, SyntaxException): self.source = file(self.error.filename).read() self.lineno = self.error.lineno + self._has_source = True self.reverse_records = [r for r in self.records] self.reverse_records.reverse() + def _get_reformatted_records(self, records): + for rec in records: + if rec[6]: + yield (rec[4], rec[5], rec[2], rec[6]) + else: + yield tuple(rec[0:4]) + traceback = property(lambda self:self._get_reformatted_records(self.records), doc=""" + return a list of 4-tuple traceback records (i.e. normal python format) + with template-corresponding lines remapped to the originating template + """) + reverse_traceback = property(lambda self:self._get_reformatted_records(self.reverse_records), doc=""" + return the same data as traceback, except in reverse order + """) def _init(self): """format a traceback from sys.exc_info() into 7-item tuples, containing the regular four traceback tuple items, plus the original template @@ -60,8 +95,6 @@ mods = {} (type, value, trcback) = sys.exc_info() rawrecords = traceback.extract_tb(trcback) - # this line extends the stack all the way back....shouldnt be needed... - #rawrecords = traceback.extract_stack() + rawrecords new_trcback = [] for filename, lineno, function, line in rawrecords: #print "TB", filename, lineno, function, line @@ -93,37 +126,43 @@ template_ln = line_map[lineno] if template_ln <= len(template_lines): template_line = template_lines[template_ln - 1] - self.source = template_source - self.lineno = template_ln else: template_line = None new_trcback.append((filename, lineno, function, line, template_filename, template_ln, template_line, template_source)) + if not self.source: + if new_trcback[-1][5]: + self.source = template_source + self.lineno = template_ln + else: + self.source = file(new_trcback[-1][0]).read() + self.lineno = new_trcback[-1][1] return (type, value, new_trcback) def text_error_template(lookup=None): + """provides a template that renders a stack trace in a similar format to the Python interpreter, + substituting source template filenames, line numbers and code for that of the originating + source template, as applicable.""" import mako.template return mako.template.Template(r""" <%! from mako.exceptions import RichTraceback -%> -Error ! +%>\ <% - (type, value, trcback) = rich_traceback() -%> - -${str(type)} - ${value} - -% for (filename, lineno, function, line, template_filename, template_ln, template_line) in trcback: - % if template_line: - ${template_filename} ${template_ln} ${template_line} - % else: - ${filename} ${lineno} ${line} - % endif + tback = RichTraceback() +%>\ +Traceback (most recent call last): +% for (filename, lineno, function, line) in tback.traceback: + File "${filename}", line ${lineno}, in ${function or '?'} + ${line.strip()} % endfor -""", lookup=lookup) +${str(tback.error.__class__.__name__)}: ${str(tback.error)} +""") -def html_error_template(lookup=None): +def html_error_template(): + """provides a template that renders a stack trace in an HTML format, providing an excerpt of + code as well as substituting source template filenames, line numbers and code + for that of the originating source template, as applicable.""" import mako.template return mako.template.Template(r""" <%! @@ -133,40 +172,19 @@ <head> <title>Mako Runtime Error</title> <style> - body { - font-family:verdana; - } - .stacktrace { - margin:10px 30px 10px 30px; - } - .highlight { - padding:0px 10px 0px 10px; - background-color:#9F9FDF; - } - .nonhighlight { - padding:0px; - background-color:#DFDFDF; - } - .sample { - padding:10px; - margin:10px 40px 10px 40px; - font-family:monospace; - } - .sampleline { - padding:0px 10px 0px 10px; - } - .sourceline { - font-size:80%; - margin-bottom:10px; - } - .location { - font-size:80%; - } + body { font-family:verdana; margin:10px 30px 10px 30px;} + .stacktrace { margin:5px 5px 5px 5px; } + .highlight { padding:0px 10px 0px 10px; background-color:#9F9FDF; } + .nonhighlight { padding:0px; background-color:#DFDFDF; } + .sample { padding:10px; margin:10px 10px 10px 10px; font-family:monospace; } + .sampleline { padding:0px 10px 0px 10px; } + .sourceline { margin:5px 5px 10px 5px; font-family:monospace;} + .location { font-size:80%; } </style> </head> <body> - <h2>Error !</h2> +<h2>Error !</h2> <% tback = RichTraceback() src = tback.source @@ -176,9 +194,7 @@ else: lines = None %> -<p> -${str(tback.error.__class__.__name__)}: ${str(tback.error)} -</p> +<h3>${str(tback.error.__class__.__name__)}: ${str(tback.error)}</h3> % if lines: <div class="sample"> @@ -194,21 +210,13 @@ </div> % endif -<%def name="traceline(filename, lineno, source)"> - <div class="location">${filename} ${lineno}:</div> - <div class="sourceline">${source}</div> -</%def> - <div class="stacktrace"> -% for (filename, lineno, function, line, template_filename, template_ln, template_line, src) in tback.reverse_records: - % if template_line: - ${traceline(template_filename, template_ln, template_line)} - % else: - ${traceline(filename, lineno, line)} - % endif +% for (filename, lineno, function, line) in tback.reverse_traceback: + <div class="location">${filename}, line ${lineno}:</div> + <div class="sourceline">${line | h}</div> % endfor </div> </body> </html> -""", lookup=lookup) \ No newline at end of file +""") \ No newline at end of file