Mercurial > genshi > mirror
changeset 134:d681d2c3cd8d trunk
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
* Evaluation errors in expressions now include the original expression code in the traceback.
author | cmlenz |
---|---|
date | Sun, 06 Aug 2006 18:07:21 +0000 |
parents | 79f445396cd7 |
children | b86f496f6035 |
files | markup/eval.py markup/input.py markup/template.py markup/tests/eval.py markup/tests/input.py markup/tests/template.py |
diffstat | 6 files changed, 97 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- a/markup/eval.py +++ b/markup/eval.py @@ -72,7 +72,7 @@ @param source: the expression as string """ self.source = source - self.code = _compile(source, filename, lineno) + self.code = _compile(self, filename, lineno) def __repr__(self): return '<Expression "%s">' % self.source @@ -94,8 +94,8 @@ return retval -def _compile(source, filename=None, lineno=-1): - tree = parse(source, 'eval') +def _compile(expr, filename=None, lineno=-1): + tree = parse(expr.source, 'eval') xform = ExpressionASTTransformer() tree = xform.visit(tree) @@ -116,7 +116,7 @@ # clone the code object while adjusting the line number return new.code(0, code.co_nlocals, code.co_stacksize, code.co_flags | 0x0040, code.co_code, code.co_consts, - code.co_names, code.co_varnames, filename, code.co_name, + code.co_names, code.co_varnames, filename, repr(expr), lineno, code.co_lnotab, (), ()) def _lookup_name(data, name, locals_=None):
--- a/markup/input.py +++ b/markup/input.py @@ -109,13 +109,27 @@ def _enqueue(self, kind, data, pos=None): if pos is None: pos = self._getpos() + if kind is Stream.TEXT: + # Expat reports the *end* of the text event as current position. We + # try to fix that up here as much as possible. Unfortunately, the + # offset is only valid for single-line text. For multi-line text, + # it is apparently not possible to determine at what offset it + # started + if '\n' in data: + lines = data.splitlines() + lineno = pos[1] - len(lines) + 1 + offset = -1 + else: + lineno = pos[1] + offset = pos[2] - len(data) + pos = (pos[0], lineno, offset) self._queue.append((kind, data, pos)) def _getpos_unknown(self): - return (self.filename or '<string>', -1, -1) + return (self.filename, -1, -1) def _getpos(self): - return (self.filename or '<string>', self.expat.CurrentLineNumber, + return (self.filename, self.expat.CurrentLineNumber, self.expat.CurrentColumnNumber) def _handle_start(self, tag, attrib):
--- a/markup/template.py +++ b/markup/template.py @@ -812,12 +812,13 @@ @param offset: the column number at which the text starts in the source (optional) """ - def _interpolate(text, patterns): + def _interpolate(text, patterns, filename=filename, lineno=lineno, + offset=offset): for idx, group in enumerate(patterns.pop(0).split(text)): if idx % 2: try: yield EXPR, Expression(group, filename, lineno), \ - (lineno, offset) + (filename, lineno, offset) except SyntaxError, err: raise TemplateSyntaxError(err, filename, lineno, offset + (err.offset or 0)) @@ -826,8 +827,14 @@ for result in _interpolate(group, patterns[:]): yield result else: - yield TEXT, group.replace('$$', '$'), (filename, lineno, - offset) + yield TEXT, group.replace('$$', '$'), \ + (filename, lineno, offset) + if '\n' in group: + lines = group.splitlines() + lineno += len(lines) - 1 + offset += len(lines[-1]) + else: + offset += len(group) return _interpolate(text, [cls._FULL_EXPR_RE, cls._SHORT_EXPR_RE]) _interpolate = classmethod(_interpolate)
--- a/markup/tests/eval.py +++ b/markup/tests/eval.py @@ -235,6 +235,8 @@ frame = exc_traceback.tb_next while frame.tb_next: frame = frame.tb_next + self.assertEqual('<Expression "nothing()">', + frame.tb_frame.f_code.co_name) self.assertEqual('index.html', frame.tb_frame.f_code.co_filename) self.assertEqual(50, frame.tb_lineno)
--- a/markup/tests/input.py +++ b/markup/tests/input.py @@ -12,15 +12,63 @@ # history and logs, available at http://markup.edgewall.org/log/. import doctest +from StringIO import StringIO +import sys import unittest from markup.core import Stream -from markup.input import XMLParser +from markup.input import XMLParser, HTMLParser + + +class XMLParserTestCase(unittest.TestCase): + + def test_text_node_pos_single_line(self): + text = '<elem>foo bar</elem>' + events = list(XMLParser(StringIO(text))) + kind, data, pos = events[1] + self.assertEqual(Stream.TEXT, kind) + self.assertEqual(u'foo bar', data) + if sys.version_info[:2] >= (2, 4): + self.assertEqual((None, 1, 6), pos) + + def test_text_node_pos_multi_line(self): + text = '''<elem>foo +bar</elem>''' + events = list(XMLParser(StringIO(text))) + kind, data, pos = events[1] + self.assertEqual(Stream.TEXT, kind) + self.assertEqual(u'foo\nbar', data) + if sys.version_info[:2] >= (2, 4): + self.assertEqual((None, 1, -1), pos) + + +class HTMLParserTestCase(unittest.TestCase): + + def test_text_node_pos_single_line(self): + text = '<elem>foo bar</elem>' + events = list(HTMLParser(StringIO(text))) + kind, data, pos = events[1] + self.assertEqual(Stream.TEXT, kind) + self.assertEqual(u'foo bar', data) + if sys.version_info[:2] >= (2, 4): + self.assertEqual((None, 1, 6), pos) + + def test_text_node_pos_multi_line(self): + text = '''<elem>foo +bar</elem>''' + events = list(HTMLParser(StringIO(text))) + kind, data, pos = events[1] + self.assertEqual(Stream.TEXT, kind) + self.assertEqual(u'foo\nbar', data) + if sys.version_info[:2] >= (2, 4): + self.assertEqual((None, 1, 6), pos) def suite(): suite = unittest.TestSuite() - suite.addTest(doctest.DocTestSuite(XMLParser.__module__)) + #suite.addTest(doctest.DocTestSuite(XMLParser.__module__)) + suite.addTest(unittest.makeSuite(XMLParserTestCase, 'test')) + suite.addTest(unittest.makeSuite(HTMLParserTestCase, 'test')) return suite if __name__ == '__main__':
--- a/markup/tests/template.py +++ b/markup/tests/template.py @@ -518,7 +518,6 @@ self.assertEqual('test.html', e.filename) if sys.version_info[:2] >= (2, 4): self.assertEqual(1, e.lineno) - # We don't really care about the offset here, do we? def test_expression_syntax_error(self): xml = """<p> @@ -532,6 +531,20 @@ if sys.version_info[:2] >= (2, 4): self.assertEqual(2, e.lineno) + def test_expression_syntax_error_multi_line(self): + xml = """<p><em></em> + + ${bar"} + + </p>""" + try: + tmpl = Template(xml, filename='test.html') + self.fail('Expected SyntaxError') + except TemplateSyntaxError, e: + self.assertEqual('test.html', e.filename) + if sys.version_info[:2] >= (2, 4): + self.assertEqual(3, e.lineno) + def test_markup_noescape(self): """ Verify that outputting context data that is a `Markup` instance is not