# -*- coding: utf-8 -*-
#
# Copyright (C) 2006-2010 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://genshi.edgewall.org/wiki/License.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://genshi.edgewall.org/log/.
import doctest
import re
import sys
import unittest
from genshi.compat import IS_PYTHON2
from genshi.template import directives, MarkupTemplate, TextTemplate, \
TemplateRuntimeError, TemplateSyntaxError
class AttrsDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:attrs` template directive."""
def test_combined_with_loop(self):
"""
Verify that the directive has access to the loop variables.
"""
tmpl = MarkupTemplate("""""")
items = [{'id': 1}, {'id': 2}]
self.assertEqual("""""", tmpl.generate(items=items).render(encoding=None))
def test_update_existing_attr(self):
"""
Verify that an attribute value that evaluates to `None` removes an
existing attribute of that name.
"""
tmpl = MarkupTemplate("""""")
self.assertEqual("""""", tmpl.generate().render(encoding=None))
def test_remove_existing_attr(self):
"""
Verify that an attribute value that evaluates to `None` removes an
existing attribute of that name.
"""
tmpl = MarkupTemplate("""""")
self.assertEqual("""""", tmpl.generate().render(encoding=None))
class ChooseDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:choose` template directive and the complementary
directives `py:when` and `py:otherwise`."""
def test_multiple_true_whens(self):
"""
Verify that, if multiple `py:when` bodies match, only the first is
output.
"""
tmpl = MarkupTemplate("""
""", tmpl.generate().render(encoding=None))
def test_complex_nesting_otherwise(self):
"""
Verify more complex nesting using otherwise.
"""
tmpl = MarkupTemplate("""
FAILOK
""")
self.assertEqual("""
OK
""", tmpl.generate().render(encoding=None))
def test_when_with_strip(self):
"""
Verify that a when directive with a strip directive actually strips of
the outer element.
"""
tmpl = MarkupTemplate("""
foo
""")
self.assertEqual("""foo""", tmpl.generate().render(encoding=None))
def test_when_outside_choose(self):
"""
Verify that a `when` directive outside of a `choose` directive is
reported as an error.
"""
tmpl = MarkupTemplate("""""")
self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
def test_otherwise_outside_choose(self):
"""
Verify that an `otherwise` directive outside of a `choose` directive is
reported as an error.
"""
tmpl = MarkupTemplate("""""")
self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
def test_when_without_test(self):
"""
Verify that an `when` directive that doesn't have a `test` attribute
is reported as an error.
"""
tmpl = MarkupTemplate("""
foo
""")
self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
def test_when_without_test_but_with_choose_value(self):
"""
Verify that an `when` directive that doesn't have a `test` attribute
works as expected as long as the parent `choose` directive has a test
expression.
"""
tmpl = MarkupTemplate("""
foo
""")
self.assertEqual("""
foo
""", tmpl.generate(foo='Yeah').render(encoding=None))
def test_otherwise_without_test(self):
"""
Verify that an `otherwise` directive can be used without a `test`
attribute.
"""
tmpl = MarkupTemplate("""
foo
""")
self.assertEqual("""
foo
""", tmpl.generate().render(encoding=None))
def test_as_element(self):
"""
Verify that the directive can also be used as an element.
"""
tmpl = MarkupTemplate("""123""")
self.assertEqual("""
1
""", tmpl.generate().render(encoding=None))
def test_in_text_template(self):
"""
Verify that the directive works as expected in a text template.
"""
tmpl = TextTemplate("""#choose
#when 1 == 1
1
#end
#when 2 == 2
2
#end
#when 3 == 3
3
#end
#end""")
self.assertEqual(""" 1\n""",
tmpl.generate().render(encoding=None))
class DefDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:def` template directive."""
def test_function_with_strip(self):
"""
Verify that a named template function with a strip directive actually
strips of the outer element.
"""
tmpl = MarkupTemplate("""
""", tmpl.generate().render(encoding=None))
def test_as_element(self):
"""
Verify that the directive can also be used as an element.
"""
tmpl = MarkupTemplate("""${what}
${echo('foo')}
""")
self.assertEqual("""foo""", tmpl.generate().render(encoding=None))
def test_nested_defs(self):
"""
Verify that a template function defined inside a conditional block can
be called from outside that block.
"""
tmpl = MarkupTemplate("""${what}${what}
${echo('foo')}
""")
self.assertEqual("""foo""", tmpl.generate(semantic=True).render(encoding=None))
def test_function_with_default_arg(self):
"""
Verify that keyword arguments work with `py:def` directives.
"""
tmpl = MarkupTemplate("""${what}
${echo('foo')}
""")
self.assertEqual("""
foo
""", tmpl.generate().render(encoding=None))
def test_invocation_in_attribute(self):
tmpl = MarkupTemplate("""${what or 'something'}
""")
self.assertRaises(TypeError, list, tmpl.generate(badfunc=badfunc))
def test_def_in_matched(self):
tmpl = MarkupTemplate("""
${select('*')}
${maketitle(True)}""")
self.assertEqual("""True""", tmpl.generate().render(encoding=None))
def test_in_text_template(self):
"""
Verify that the directive works as expected in a text template.
"""
tmpl = TextTemplate("""
#def echo(greeting, name='world')
${greeting}, ${name}!
#end
${echo('Hi', name='you')}
""")
self.assertEqual("""
Hi, you!
""", tmpl.generate().render(encoding=None))
def test_function_with_star_args(self):
"""
Verify that a named template function using "star arguments" works as
expected.
"""
tmpl = MarkupTemplate("""
${repr(args)}
${repr(sorted(kwargs.items()))}
${f(1, 2, a=3, b=4)}
""")
self.assertEqual("""
[1, 2]
[('a', 3), ('b', 4)]
""", tmpl.generate().render(encoding=None))
class ForDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:for` template directive."""
def test_loop_with_strip(self):
"""
Verify that the combining the `py:for` directive with `py:strip` works
correctly.
"""
tmpl = MarkupTemplate("""
${item}
""")
self.assertEqual("""12345""", tmpl.generate(items=range(1, 6)).render(encoding=None))
def test_as_element(self):
"""
Verify that the directive can also be used as an element.
"""
tmpl = MarkupTemplate("""${item}""")
self.assertEqual("""12345""", tmpl.generate(items=range(1, 6)).render(encoding=None))
def test_multi_assignment(self):
"""
Verify that assignment to tuples works correctly.
"""
tmpl = MarkupTemplate("""
key=$k, value=$v
""")
self.assertEqual("""
key=a, value=1
key=b, value=2
""", tmpl.generate(items=(('a', 1), ('b', 2)))
.render(encoding=None))
def test_nested_assignment(self):
"""
Verify that assignment to nested tuples works correctly.
"""
tmpl = MarkupTemplate("""
$idx: key=$k, value=$v
""")
self.assertEqual("""
0: key=a, value=1
1: key=b, value=2
""", tmpl.generate(items=enumerate([('a', 1), ('b', 2)]))
.render(encoding=None))
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))
self.fail('Expected TemplateRuntimeError')
except TypeError as e:
assert (str(e) == "iteration over non-sequence" or
str(e) == "'int' object is not iterable")
exc_type, exc_value, exc_traceback = sys.exc_info()
frame = exc_traceback.tb_next
frames = []
while frame.tb_next:
frame = frame.tb_next
frames.append(frame)
expected_iter_str = "u'iter(foo)'" if IS_PYTHON2 else "'iter(foo)'"
self.assertEqual("" % expected_iter_str,
frames[-1].tb_frame.f_code.co_name)
self.assertEqual('test.html',
frames[-1].tb_frame.f_code.co_filename)
self.assertEqual(2, frames[-1].tb_lineno)
def test_for_with_empty_value(self):
"""
Verify an empty 'for' value is an error
"""
try:
MarkupTemplate("""
empty
""", filename='test.html').generate()
self.fail('ExpectedTemplateSyntaxError')
except TemplateSyntaxError as 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."""
def test_loop_with_strip(self):
"""
Verify that the combining the `py:if` directive with `py:strip` works
correctly.
"""
tmpl = MarkupTemplate("""${bar}""")
self.assertEqual("""
Hello
""", tmpl.generate(foo=True, bar='Hello').render(encoding=None))
def test_as_element(self):
"""
Verify that the directive can also be used as an element.
"""
tmpl = MarkupTemplate("""${bar}""")
self.assertEqual("""
Hello
""", tmpl.generate(foo=True, bar='Hello').render(encoding=None))
class MatchDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:match` template directive."""
def test_with_strip(self):
"""
Verify that a match template can produce the same kind of element that
it matched without entering an infinite recursion.
"""
tmpl = MarkupTemplate("""
${select('text()')}
Hey Joe""")
self.assertEqual("""
Hey Joe
""", tmpl.generate().render(encoding=None))
def test_without_strip(self):
"""
Verify that a match template can produce the same kind of element that
it matched without entering an infinite recursion.
"""
tmpl = MarkupTemplate("""
${select('text()')}
Hey Joe""")
self.assertEqual("""
Hey Joe
""", tmpl.generate().render(encoding=None))
def test_as_element(self):
"""
Verify that the directive can also be used as an element.
"""
tmpl = MarkupTemplate("""
${select('text()')}
Hey Joe""")
self.assertEqual("""
Hey Joe
""", tmpl.generate().render(encoding=None))
def test_recursive_match_1(self):
"""
Match directives are applied recursively, meaning that they are also
applied to any content they may have produced themselves:
"""
tmpl = MarkupTemplate("""
${select('*')}
""")
self.assertEqual("""
""", tmpl.generate().render(encoding=None))
def test_recursive_match_2(self):
"""
When two or more match templates match the same element and also
themselves output the element they match, avoiding recursion is even
more complex, but should work.
"""
tmpl = MarkupTemplate("""
${select('*')}
${select('*')}
""", tmpl.generate().render(encoding=None))
def test_multiple_matches(self):
tmpl = MarkupTemplate("""
""")
fields = ['hello_%s' % i for i in range(5)]
values = dict([('hello_%s' % i, i) for i in range(5)])
self.assertEqual("""
""", tmpl.generate(fields=fields, values=values)
.render(encoding=None))
def test_namespace_context(self):
tmpl = MarkupTemplate("""
Foo
""")
# FIXME: there should be a way to strip out unwanted/unused namespaces,
# such as the "x" in this example
self.assertEqual("""
""", tmpl.generate().render(encoding=None))
class ContentDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:content` template directive."""
def test_as_element(self):
try:
MarkupTemplate("""Foo""", filename='test.html').generate()
self.fail('Expected TemplateSyntaxError')
except TemplateSyntaxError as e:
self.assertEqual('test.html', e.filename)
self.assertEqual(2, e.lineno)
class ReplaceDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:replace` template directive."""
def test_replace_with_empty_value(self):
"""
Verify that the directive raises an apprioriate exception when an empty
expression is supplied.
"""
try:
MarkupTemplate("""Foo""", filename='test.html').generate()
self.fail('Expected TemplateSyntaxError')
except TemplateSyntaxError as e:
self.assertEqual('test.html', e.filename)
self.assertEqual(2, e.lineno)
def test_as_element(self):
tmpl = MarkupTemplate("""
""", filename='test.html')
self.assertEqual("""
Test
""", tmpl.generate(title='Test').render(encoding=None))
class StripDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:strip` template directive."""
def test_strip_false(self):
tmpl = MarkupTemplate("""
""", tmpl.generate().render(encoding=None))
class WithDirectiveTestCase(unittest.TestCase):
"""Tests for the `py:with` template directive."""
def test_shadowing(self):
tmpl = MarkupTemplate("""
here is a semicolon: ;
here are two semicolons: ;;
""", tmpl.generate().render(encoding=None))
def test_ast_transformation(self):
"""
Verify that the usual template expression AST transformations are
applied despite the code being compiled to a `Suite` object.
"""
tmpl = MarkupTemplate("""
""", tmpl.generate().render(encoding=None))
def test_with_empty_value(self):
"""
Verify that an empty py:with works (useless, but legal)
"""
tmpl = MarkupTemplate("""