Skip to content
Snippets Groups Projects
Commit baa8a63e authored by Mike Bayer's avatar Mike Bayer
Browse files

- RichTraceback() now accepts an optional traceback object

  to be used in place of sys.exc_info()[2].  html_error_template()
  and text_error_template() accept an optional
  render()-time argument "traceback" which is passed to the
  RichTraceback object.
- lexer tests now rely upon an always-sorted dict repr()
parent 718d98ee
No related branches found
No related tags found
No related merge requests found
......@@ -47,6 +47,12 @@
- fixed the html_error_template not handling tracebacks from
normal .py files with a magic encoding comment [ticket:88]
- RichTraceback() now accepts an optional traceback object
to be used in place of sys.exc_info()[2]. html_error_template()
and text_error_template() accept an optional
render()-time argument "traceback" which is passed to the
RichTraceback object.
- added ModuleTemplate class, which allows the construction
of a Template given a Python module generated by a previous
Template. This allows Python modules alone to be used
......
......@@ -64,10 +64,11 @@ class RichTraceback(object):
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):
def __init__(self, traceback=None):
(self.source, self.lineno) = ("", 0)
(t, self.error, self.records) = self._init()
(t, self.error, self.records) = self._init(traceback)
if self.error is None:
self.error = t
if isinstance(self.error, CompileException) or isinstance(self.error, SyntaxException):
......@@ -90,14 +91,15 @@ class RichTraceback(object):
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):
def _init(self, trcback):
"""format a traceback from sys.exc_info() into 7-item tuples, containing
the regular four traceback tuple items, plus the original template
filename, the line number adjusted relative to the template source, and
code line from that line number of the template."""
import mako.template
mods = {}
(type, value, trcback) = sys.exc_info()
if not trcback:
(type, value, trcback) = sys.exc_info()
rawrecords = traceback.extract_tb(trcback)
new_trcback = []
for filename, lineno, function, line in rawrecords:
......@@ -160,11 +162,12 @@ def text_error_template(lookup=None):
source template, as applicable."""
import mako.template
return mako.template.Template(r"""
<%page args="traceback=None"/>
<%!
from mako.exceptions import RichTraceback
%>\
<%
tback = RichTraceback()
tback = RichTraceback(traceback=traceback)
%>\
Traceback (most recent call last):
% for (filename, lineno, function, line) in tback.traceback:
......@@ -189,7 +192,7 @@ def html_error_template():
<%!
from mako.exceptions import RichTraceback
%>
<%page args="full=True, css=True"/>
<%page args="full=True, css=True, traceback=None"/>
% if full:
<html>
<head>
......@@ -214,7 +217,7 @@ def html_error_template():
<h2>Error !</h2>
<%
tback = RichTraceback()
tback = RichTraceback(traceback=traceback)
src = tback.source
line = tback.lineno
if src:
......
......@@ -43,7 +43,7 @@ class TemplateNode(Node):
return self.nodes
def __repr__(self):
return "TemplateNode(%r, %r)" % (self.page_attributes, self.nodes)
return "TemplateNode(%s, %r)" % (util.sorted_dict_repr(self.page_attributes), self.nodes)
class ControlLine(Node):
"""defines a control line, a line-oriented python line or end tag.
......@@ -293,9 +293,9 @@ class Tag(Node):
return self.expression_undeclared_identifiers
def __repr__(self):
return "%s(%r, %r, %r, %r)" % (self.__class__.__name__,
return "%s(%r, %s, %r, %r)" % (self.__class__.__name__,
self.keyword,
self.attributes,
util.sorted_dict_repr(self.attributes),
(self.lineno, self.pos),
[repr(x) for x in self.nodes]
)
......
......@@ -180,6 +180,16 @@ def parse_encoding(fp):
finally:
fp.seek(pos)
def sorted_dict_repr(d):
"""repr() a dictionary with the keys in order.
Used by the lexer unit test to compare parse trees based on strings.
"""
keys = d.keys()
keys.sort()
return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}"
def restore__ast(_ast):
"""Attempt to restore the required classes to the _ast module if it
appears to be missing them
......
......@@ -129,7 +129,7 @@ class LexerTest(unittest.TestCase):
<%self:go x="1" y="2" z="${'hi' + ' ' + 'there'}"/>
"""
nodes = Lexer(template).parse()
assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n \n ', (1, 1)), CallNamespaceTag(u'self:go', {u'y': u'2', u'x': u'1', u'z': u"${'hi' + ' ' + 'there'}"}, (3, 13), []), Text(u'\n ', (3, 64))])"""
assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n \n ', (1, 1)), CallNamespaceTag(u'self:go', {u'x': u'1', u'y': u'2', u'z': u"${'hi' + ' ' + 'there'}"}, (3, 13), []), Text(u'\n ', (3, 64))])"""
def test_ns_tag_open(self):
template = """
......@@ -139,7 +139,7 @@ class LexerTest(unittest.TestCase):
</%self:go>
"""
nodes = Lexer(template).parse()
assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n \n ', (1, 1)), CallNamespaceTag(u'self:go', {u'y': u'${process()}', u'x': u'1'}, (3, 13), ["Text(u'\\n this is the body\\n ', (3, 46))"]), Text(u'\n ', (5, 24))])"""
assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n \n ', (1, 1)), CallNamespaceTag(u'self:go', {u'x': u'1', u'y': u'${process()}'}, (3, 13), ["Text(u'\\n this is the body\\n ', (3, 46))"]), Text(u'\n ', (5, 24))])"""
def test_expr_in_attribute(self):
"""test some slightly trickier expressions.
......@@ -160,7 +160,7 @@ class LexerTest(unittest.TestCase):
some template
"""
nodes = Lexer(template).parse()
assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n ', (1, 1)), PageTag(u'page', {u'cached': u'True', u'args': u'a, b'}, (2, 13), []), Text(u'\n \n some template\n ', (2, 48))])"""
assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n ', (1, 1)), PageTag(u'page', {u'args': u'a, b', u'cached': u'True'}, (2, 13), []), Text(u'\n \n some template\n ', (2, 48))])"""
def test_nesting(self):
template = """
......@@ -397,15 +397,8 @@ text text la la
</table>
"""
nodes = Lexer(template).parse()
expected = r"""TemplateNode({}, [NamespaceTag(u'namespace', {u'name': u'foo', u'file': u'somefile.html'}, (1, 1), []), Text(u'\n', (1, 46)), Comment(u'inherit from foobar.html', (2, 1)), InheritTag(u'inherit', {u'file': u'foobar.html'}, (3, 1), []), Text(u'\n\n', (3, 31)), DefTag(u'def', {u'name': u'header()'}, (5, 1), ["Text(u'\\n <div>header</div>\\n', (5, 23))"]), Text(u'\n', (7, 8)), DefTag(u'def', {u'name': u'footer()'}, (8, 1), ["Text(u'\\n <div> footer</div>\\n', (8, 23))"]), Text(u'\n\n<table>\n', (10, 8)), ControlLine(u'for', u'for j in data():', False, (13, 1)), Text(u' <tr>\n', (14, 1)), ControlLine(u'for', u'for x in j:', False, (15, 1)), Text(u' <td>Hello ', (16, 1)), Expression(u'x', ['h'], (16, 23)), Text(u'</td>\n', (16, 30)), ControlLine(u'for', u'endfor', True, (17, 1)), Text(u' </tr>\n', (18, 1)), ControlLine(u'for', u'endfor', True, (19, 1)), Text(u'</table>\n', (20, 1))])"""
result = repr(nodes)
# Don't assume dict ordering. Annoying
assert len(result) == len(expected)
start = expected.find("{u'name':")
end = expected.find("somefile.html'},")
assert expected[:start] == result[:start]
assert expected[end:] == result[end:]
assert result[start:end] in ({u'name': u'foo', u'file': u'somefile.html'}, {u'file': u'somefile.html', u'name': u'foo'})
expected = r"""TemplateNode({}, [NamespaceTag(u'namespace', {u'file': u'somefile.html', u'name': u'foo'}, (1, 1), []), Text(u'\n', (1, 46)), Comment(u'inherit from foobar.html', (2, 1)), InheritTag(u'inherit', {u'file': u'foobar.html'}, (3, 1), []), Text(u'\n\n', (3, 31)), DefTag(u'def', {u'name': u'header()'}, (5, 1), ["Text(u'\\n <div>header</div>\\n', (5, 23))"]), Text(u'\n', (7, 8)), DefTag(u'def', {u'name': u'footer()'}, (8, 1), ["Text(u'\\n <div> footer</div>\\n', (8, 23))"]), Text(u'\n\n<table>\n', (10, 8)), ControlLine(u'for', u'for j in data():', False, (13, 1)), Text(u' <tr>\n', (14, 1)), ControlLine(u'for', u'for x in j:', False, (15, 1)), Text(u' <td>Hello ', (16, 1)), Expression(u'x', ['h'], (16, 23)), Text(u'</td>\n', (16, 30)), ControlLine(u'for', u'endfor', True, (17, 1)), Text(u' </tr>\n', (18, 1)), ControlLine(u'for', u'endfor', True, (19, 1)), Text(u'</table>\n', (20, 1))])"""
assert repr(nodes) == expected
def test_comment_after_statement(self):
template = """
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment