# HG changeset patch # User cmlenz # Date 1160421941 0 # Node ID fb3a75cd58af4e0ad6e6a7f9e5f04dfab372c5f4 # Parent e68705cb462e4fe4c5a6da5ee3026bc4206ec7aa Better error message when trying a `py:for` loop over an object that is not iterable. Closes #60. diff --git a/genshi/template.py b/genshi/template.py --- a/genshi/template.py +++ b/genshi/template.py @@ -73,6 +73,19 @@ TemplateSyntaxError.__init__(self, message, filename, lineno) +class TemplateRuntimeError(TemplateError): + """Exception raised when an the evualation of a Python expression in a + template causes an error.""" + + def __init__(self, message, filename='', lineno=-1, offset=-1): + self.msg = message + message = '%s (%s, line %d)' % (self.msg, filename, lineno) + TemplateError.__init__(self, message) + self.filename = filename + self.lineno = lineno + self.offset = offset + + class TemplateNotFound(TemplateError): """Exception raised when a specific template file could not be found.""" @@ -435,12 +448,16 @@ assign = self.assign scope = {} stream = list(stream) - for item in iter(iterable): - assign(scope, item) - ctxt.push(scope) - for event in _apply_directives(stream, ctxt, directives): - yield event - ctxt.pop() + try: + iterator = iter(iterable) + for item in iterator: + assign(scope, item) + ctxt.push(scope) + for event in _apply_directives(stream, ctxt, directives): + yield event + ctxt.pop() + except TypeError, e: + raise TemplateRuntimeError(str(e), *stream[0][2]) def __repr__(self): return '<%s>' % self.__class__.__name__ @@ -652,14 +669,14 @@ def __call__(self, stream, ctxt, directives): matched, frame = ctxt._find('_choose.matched') if not frame: - raise TemplateSyntaxError('"when" directives can only be used ' - 'inside a "choose" directive', - *stream.next()[2]) + raise TemplateRuntimeError('"when" directives can only be used ' + 'inside a "choose" directive', + *stream.next()[2]) if matched: return [] if not self.expr: - raise TemplateSyntaxError('"when" directive has no test condition', - *stream.next()[2]) + raise TemplateRuntimeError('"when" directive has no test condition', + *stream.next()[2]) value = self.expr.evaluate(ctxt) if '_choose.value' in frame: matched = (value == frame['_choose.value']) @@ -681,9 +698,9 @@ def __call__(self, stream, ctxt, directives): matched, frame = ctxt._find('_choose.matched') if not frame: - raise TemplateSyntaxError('an "otherwise" directive can only be ' - 'used inside a "choose" directive', - *stream.next()[2]) + raise TemplateRuntimeError('an "otherwise" directive can only be ' + 'used inside a "choose" directive', + *stream.next()[2]) if matched: return [] frame['_choose.matched'] = True diff --git a/genshi/tests/template.py b/genshi/tests/template.py --- a/genshi/tests/template.py +++ b/genshi/tests/template.py @@ -21,7 +21,8 @@ from genshi import template from genshi.core import Markup, Stream from genshi.template import BadDirectiveError, MarkupTemplate, Template, \ - TemplateLoader, TemplateSyntaxError, TextTemplate + TemplateLoader, TemplateRuntimeError, \ + TemplateSyntaxError, TextTemplate class AttrsDirectiveTestCase(unittest.TestCase): @@ -173,7 +174,7 @@ tmpl = MarkupTemplate("""
""") - self.assertRaises(TemplateSyntaxError, str, tmpl.generate()) + self.assertRaises(TemplateRuntimeError, str, tmpl.generate()) def test_otherwise_outside_choose(self): """ @@ -183,7 +184,7 @@ tmpl = MarkupTemplate("""
""") - self.assertRaises(TemplateSyntaxError, str, tmpl.generate()) + self.assertRaises(TemplateRuntimeError, str, tmpl.generate()) def test_when_without_test(self): """ @@ -195,7 +196,7 @@ foo
""") - self.assertRaises(TemplateSyntaxError, str, tmpl.generate()) + self.assertRaises(TemplateRuntimeError, str, tmpl.generate()) def test_otherwise_without_test(self): """ @@ -440,6 +441,22 @@

1: key=b, value=2

""", str(tmpl.generate(items=enumerate(dict(a=1, b=2).items())))) + def test_not_iterable(self): + """ + Verify that assignment to nested tuples works correctly. + """ + tmpl = MarkupTemplate(""" + + $item + + """, filename='test.html') + try: + list(tmpl.generate(foo=12)) + except TemplateRuntimeError, e: + self.assertEqual('test.html', e.filename) + if sys.version_info[:2] >= (2, 4): + self.assertEqual(2, e.lineno) + class IfDirectiveTestCase(unittest.TestCase): """Tests for the `py:if` template directive."""