1
# -*- coding: utf-8 -*-
3
# Copyright (C) 2006 Edgewall Software
6
# This software is licensed as described in the file COPYING, which
7
# you should have received as part of this distribution. The terms
8
# are also available at http://genshi.edgewall.org/wiki/License.
10
# This software consists of voluntary contributions made by many
11
# individuals. For the exact contribution history, see the revision
12
# history and logs, available at http://genshi.edgewall.org/log/.
21
from genshi import template
22
from genshi.core import Markup, Stream
23
from genshi.template import BadDirectiveError, MarkupTemplate, Template, \
24
TemplateLoader, TemplateRuntimeError, \
25
TemplateSyntaxError, TextTemplate
28
class AttrsDirectiveTestCase(unittest.TestCase):
29
"""Tests for the `py:attrs` template directive."""
31
def test_combined_with_loop(self):
33
Verify that the directive has access to the loop variables.
35
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
36
<elem py:for="item in items" py:attrs="item"/>
38
items = [{'id': 1, 'class': 'foo'}, {'id': 2, 'class': 'bar'}]
39
self.assertEqual("""<doc>
40
<elem id="1" class="foo"/><elem id="2" class="bar"/>
41
</doc>""", str(tmpl.generate(items=items)))
43
def test_update_existing_attr(self):
45
Verify that an attribute value that evaluates to `None` removes an
46
existing attribute of that name.
48
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
49
<elem class="foo" py:attrs="{'class': 'bar'}"/>
51
self.assertEqual("""<doc>
53
</doc>""", str(tmpl.generate()))
55
def test_remove_existing_attr(self):
57
Verify that an attribute value that evaluates to `None` removes an
58
existing attribute of that name.
60
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
61
<elem class="foo" py:attrs="{'class': None}"/>
63
self.assertEqual("""<doc>
65
</doc>""", str(tmpl.generate()))
68
class ChooseDirectiveTestCase(unittest.TestCase):
69
"""Tests for the `py:choose` template directive and the complementary
70
directives `py:when` and `py:otherwise`."""
72
def test_multiple_true_whens(self):
74
Verify that, if multiple `py:when` bodies match, only the first is
77
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/" py:choose="">
78
<span py:when="1 == 1">1</span>
79
<span py:when="2 == 2">2</span>
80
<span py:when="3 == 3">3</span>
82
self.assertEqual("""<div>
84
</div>""", str(tmpl.generate()))
86
def test_otherwise(self):
87
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/" py:choose="">
88
<span py:when="False">hidden</span>
89
<span py:otherwise="">hello</span>
91
self.assertEqual("""<div>
93
</div>""", str(tmpl.generate()))
95
def test_nesting(self):
97
Verify that `py:choose` blocks can be nested:
99
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
101
<div py:when="1" py:choose="3">
102
<span py:when="2">2</span>
103
<span py:when="3">3</span>
107
self.assertEqual("""<doc>
113
</doc>""", str(tmpl.generate()))
115
def test_complex_nesting(self):
117
Verify more complex nesting.
119
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
121
<div py:when="1" py:choose="">
122
<span py:when="2">OK</span>
123
<span py:when="1">FAIL</span>
127
self.assertEqual("""<doc>
133
</doc>""", str(tmpl.generate()))
135
def test_complex_nesting_otherwise(self):
137
Verify more complex nesting using otherwise.
139
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
141
<div py:when="1" py:choose="2">
142
<span py:when="1">FAIL</span>
143
<span py:otherwise="">OK</span>
147
self.assertEqual("""<doc>
153
</doc>""", str(tmpl.generate()))
155
def test_when_with_strip(self):
157
Verify that a when directive with a strip directive actually strips of
160
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
161
<div py:choose="" py:strip="">
162
<span py:otherwise="">foo</span>
165
self.assertEqual("""<doc>
167
</doc>""", str(tmpl.generate()))
169
def test_when_outside_choose(self):
171
Verify that a `when` directive outside of a `choose` directive is
172
reported as an error.
174
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
177
self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
179
def test_otherwise_outside_choose(self):
181
Verify that an `otherwise` directive outside of a `choose` directive is
182
reported as an error.
184
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
185
<div py:otherwise="" />
187
self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
189
def test_when_without_test(self):
191
Verify that an `when` directive that doesn't have a `test` attribute
192
is reported as an error.
194
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
195
<div py:choose="" py:strip="">
196
<py:when>foo</py:when>
199
self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
201
def test_otherwise_without_test(self):
203
Verify that an `otherwise` directive can be used without a `test`
206
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
207
<div py:choose="" py:strip="">
208
<py:otherwise>foo</py:otherwise>
211
self.assertEqual("""<doc>
213
</doc>""", str(tmpl.generate()))
215
def test_as_element(self):
217
Verify that the directive can also be used as an element.
219
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
221
<py:when test="1 == 1">1</py:when>
222
<py:when test="2 == 2">2</py:when>
223
<py:when test="3 == 3">3</py:when>
226
self.assertEqual("""<doc>
228
</doc>""", str(tmpl.generate()))
230
def test_in_text_template(self):
232
Verify that the directive works as expected in a text template.
234
tmpl = TextTemplate("""#choose
245
self.assertEqual(""" 1\n""", str(tmpl.generate()))
248
class DefDirectiveTestCase(unittest.TestCase):
249
"""Tests for the `py:def` template directive."""
251
def test_function_with_strip(self):
253
Verify that a named template function with a strip directive actually
254
strips of the outer element.
256
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
257
<div py:def="echo(what)" py:strip="">
262
self.assertEqual("""<doc>
264
</doc>""", str(tmpl.generate()))
266
def test_exec_in_replace(self):
267
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
268
<p py:def="echo(greeting, name='world')" class="message">
269
${greeting}, ${name}!
271
<div py:replace="echo('hello')"></div>
273
self.assertEqual("""<div>
277
</div>""", str(tmpl.generate()))
279
def test_as_element(self):
281
Verify that the directive can also be used as an element.
283
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
284
<py:def function="echo(what)">
289
self.assertEqual("""<doc>
291
</doc>""", str(tmpl.generate()))
293
def test_nested_defs(self):
295
Verify that a template function defined inside a conditional block can
296
be called from outside that block.
298
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
299
<py:if test="semantic">
300
<strong py:def="echo(what)">${what}</strong>
302
<py:if test="not semantic">
303
<b py:def="echo(what)">${what}</b>
307
self.assertEqual("""<doc>
309
</doc>""", str(tmpl.generate(semantic=True)))
311
def test_function_with_default_arg(self):
313
Verify that keyword arguments work with `py:def` directives.
315
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
316
<b py:def="echo(what, bold=False)" py:strip="not bold">${what}</b>
319
self.assertEqual("""<doc>
321
</doc>""", str(tmpl.generate()))
323
def test_invocation_in_attribute(self):
324
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
325
<py:def function="echo(what)">${what or 'something'}</py:def>
326
<p class="${echo('foo')}">bar</p>
328
self.assertEqual("""<doc>
329
<p class="foo">bar</p>
330
</doc>""", str(tmpl.generate()))
332
def test_invocation_in_attribute_none(self):
333
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
334
<py:def function="echo()">${None}</py:def>
335
<p class="${echo()}">bar</p>
337
self.assertEqual("""<doc>
339
</doc>""", str(tmpl.generate()))
341
def test_function_raising_typeerror(self):
344
tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
345
<div py:def="dobadfunc()">
348
<div py:content="dobadfunc()"/>
350
self.assertRaises(TypeError, list, tmpl.generate(badfunc=badfunc))
352
def test_def_in_matched(self):
353
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
354
<head py:match="head">${select('*')}</head>
356
<py:def function="maketitle(test)"><b py:replace="test" /></py:def>
357
<title>${maketitle(True)}</title>
360
self.assertEqual("""<doc>
361
<head><title>True</title></head>
362
</doc>""", str(tmpl.generate()))
364
def test_in_text_template(self):
366
Verify that the directive works as expected in a text template.
368
tmpl = TextTemplate("""
369
#def echo(greeting, name='world')
370
${greeting}, ${name}!
372
${echo('Hi', name='you')}
376
""", str(tmpl.generate()))
379
class ForDirectiveTestCase(unittest.TestCase):
380
"""Tests for the `py:for` template directive."""
382
def test_loop_with_strip(self):
384
Verify that the combining the `py:for` directive with `py:strip` works
387
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
388
<div py:for="item in items" py:strip="">
392
self.assertEqual("""<doc>
398
</doc>""", str(tmpl.generate(items=range(1, 6))))
400
def test_as_element(self):
402
Verify that the directive can also be used as an element.
404
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
405
<py:for each="item in items">
409
self.assertEqual("""<doc>
415
</doc>""", str(tmpl.generate(items=range(1, 6))))
417
def test_multi_assignment(self):
419
Verify that assignment to tuples works correctly.
421
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
422
<py:for each="k, v in items">
423
<p>key=$k, value=$v</p>
426
self.assertEqual("""<doc>
427
<p>key=a, value=1</p>
428
<p>key=b, value=2</p>
429
</doc>""", str(tmpl.generate(items=dict(a=1, b=2).items())))
431
def test_nested_assignment(self):
433
Verify that assignment to nested tuples works correctly.
435
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
436
<py:for each="idx, (k, v) in items">
437
<p>$idx: key=$k, value=$v</p>
440
self.assertEqual("""<doc>
441
<p>0: key=a, value=1</p>
442
<p>1: key=b, value=2</p>
443
</doc>""", str(tmpl.generate(items=enumerate(dict(a=1, b=2).items()))))
445
def test_not_iterable(self):
447
Verify that assignment to nested tuples works correctly.
449
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
450
<py:for each="item in foo">
453
</doc>""", filename='test.html')
455
list(tmpl.generate(foo=12))
456
except TemplateRuntimeError, e:
457
self.assertEqual('test.html', e.filename)
458
if sys.version_info[:2] >= (2, 4):
459
self.assertEqual(2, e.lineno)
462
class IfDirectiveTestCase(unittest.TestCase):
463
"""Tests for the `py:if` template directive."""
465
def test_loop_with_strip(self):
467
Verify that the combining the `py:if` directive with `py:strip` works
470
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
471
<b py:if="foo" py:strip="">${bar}</b>
473
self.assertEqual("""<doc>
475
</doc>""", str(tmpl.generate(foo=True, bar='Hello')))
477
def test_as_element(self):
479
Verify that the directive can also be used as an element.
481
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
482
<py:if test="foo">${bar}</py:if>
484
self.assertEqual("""<doc>
486
</doc>""", str(tmpl.generate(foo=True, bar='Hello')))
489
class MatchDirectiveTestCase(unittest.TestCase):
490
"""Tests for the `py:match` template directive."""
492
def test_with_strip(self):
494
Verify that a match template can produce the same kind of element that
495
it matched without entering an infinite recursion.
497
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
498
<elem py:match="elem" py:strip="">
499
<div class="elem">${select('text()')}</div>
503
self.assertEqual("""<doc>
504
<div class="elem">Hey Joe</div>
505
</doc>""", str(tmpl.generate()))
507
def test_without_strip(self):
509
Verify that a match template can produce the same kind of element that
510
it matched without entering an infinite recursion.
512
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
513
<elem py:match="elem">
514
<div class="elem">${select('text()')}</div>
518
self.assertEqual("""<doc>
520
<div class="elem">Hey Joe</div>
522
</doc>""", str(tmpl.generate()))
524
def test_as_element(self):
526
Verify that the directive can also be used as an element.
528
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
529
<py:match path="elem">
530
<div class="elem">${select('text()')}</div>
534
self.assertEqual("""<doc>
535
<div class="elem">Hey Joe</div>
536
</doc>""", str(tmpl.generate()))
538
def test_recursive_match_1(self):
540
Match directives are applied recursively, meaning that they are also
541
applied to any content they may have produced themselves:
543
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
544
<elem py:match="elem">
555
self.assertEqual("""<doc>
566
</doc>""", str(tmpl.generate()))
568
def test_recursive_match_2(self):
570
When two or more match templates match the same element and also
571
themselves output the element they match, avoiding recursion is even
572
more complex, but should work.
574
tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
575
<body py:match="body">
579
<body py:match="body">
587
self.assertEqual("""<html>
589
<div id="header"/><h1>Foo</h1>
592
</html>""", str(tmpl.generate()))
594
def test_not_match_self(self):
596
See http://genshi.edgewall.org/ticket/77
598
tmpl = MarkupTemplate("""<html xmlns="http://www.w3.org/1999/xhtml"
599
xmlns:py="http://genshi.edgewall.org/">
600
<body py:match="body" py:content="select('*')" />
609
self.assertEqual("""<html xmlns="http://www.w3.org/1999/xhtml">
614
</html>""", str(tmpl.generate()))
616
def test_select_text_in_element(self):
618
See http://genshi.edgewall.org/ticket/77#comment:1
620
tmpl = MarkupTemplate("""<html xmlns="http://www.w3.org/1999/xhtml"
621
xmlns:py="http://genshi.edgewall.org/">
622
<body py:match="body" py:content="select('*')" />
633
self.assertEqual("""<html xmlns="http://www.w3.org/1999/xhtml">
640
</html>""", str(tmpl.generate()))
642
def test_select_all_attrs(self):
643
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
644
<div py:match="elem" py:attrs="select('@*')">
647
<elem id="joe">Hey Joe</elem>
649
self.assertEqual("""<doc>
653
</doc>""", str(tmpl.generate()))
655
def test_select_all_attrs_empty(self):
656
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
657
<div py:match="elem" py:attrs="select('@*')">
662
self.assertEqual("""<doc>
666
</doc>""", str(tmpl.generate()))
668
def test_select_all_attrs_in_body(self):
669
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
670
<div py:match="elem">
671
Hey ${select('text()')} ${select('@*')}
673
<elem title="Cool">Joe</elem>
675
self.assertEqual("""<doc>
679
</doc>""", str(tmpl.generate()))
681
def test_def_in_match(self):
682
tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
683
<py:def function="maketitle(test)"><b py:replace="test" /></py:def>
684
<head py:match="head">${select('*')}</head>
685
<head><title>${maketitle(True)}</title></head>
687
self.assertEqual("""<doc>
688
<head><title>True</title></head>
689
</doc>""", str(tmpl.generate()))
691
def test_match_with_xpath_variable(self):
692
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
693
<span py:match="*[name()=$tagname]">
694
Hello ${select('@name')}
696
<greeting name="Dude"/>
698
self.assertEqual("""<div>
702
</div>""", str(tmpl.generate(tagname='greeting')))
703
self.assertEqual("""<div>
704
<greeting name="Dude"/>
705
</div>""", str(tmpl.generate(tagname='sayhello')))
707
def test_content_directive_in_match(self):
708
tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
709
<div py:match="foo">I said <q py:content="select('text()')">something</q>.</div>
712
self.assertEqual("""<html>
713
<div>I said <q>bar</q>.</div>
714
</html>""", str(tmpl.generate()))
716
def test_cascaded_matches(self):
717
tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
718
<body py:match="body">${select('*')}</body>
719
<head py:match="head">${select('title')}</head>
720
<body py:match="body">${select('*')}<hr /></body>
721
<head><title>Welcome to Markup</title></head>
722
<body><h2>Are you ready to mark up?</h2></body>
724
self.assertEqual("""<html>
725
<head><title>Welcome to Markup</title></head>
726
<body><h2>Are you ready to mark up?</h2><hr/></body>
727
</html>""", str(tmpl.generate()))
729
def test_multiple_matches(self):
730
tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
731
<input py:match="form//input" py:attrs="select('@*')"
732
value="${values[str(select('@name'))]}" />
733
<form><p py:for="field in fields">
734
<label>${field.capitalize()}</label>
735
<input type="text" name="${field}" />
738
fields = ['hello_%s' % i for i in range(5)]
739
values = dict([('hello_%s' % i, i) for i in range(5)])
740
self.assertEqual("""<html>
742
<label>Hello_0</label>
743
<input value="0" type="text" name="hello_0"/>
745
<label>Hello_1</label>
746
<input value="1" type="text" name="hello_1"/>
748
<label>Hello_2</label>
749
<input value="2" type="text" name="hello_2"/>
751
<label>Hello_3</label>
752
<input value="3" type="text" name="hello_3"/>
754
<label>Hello_4</label>
755
<input value="4" type="text" name="hello_4"/>
757
</html>""", str(tmpl.generate(fields=fields, values=values)))
759
def test_namespace_context(self):
760
tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
761
xmlns:x="http://www.example.org/">
762
<div py:match="x:foo">Foo</div>
763
<foo xmlns="http://www.example.org/"/>
765
# FIXME: there should be a way to strip out unwanted/unused namespaces,
766
# such as the "x" in this example
767
self.assertEqual("""<html xmlns:x="http://www.example.org/">
769
</html>""", str(tmpl.generate()))
771
def test_match_without_closure(self):
772
tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
773
<p py:match="body/p" class="para">${select('*|text()')}</p>
776
<div><p>Bar</p></div>
779
self.assertEqual("""<html>
781
<p class="para">Foo</p>
782
<div><p>Bar</p></div>
784
</html>""", str(tmpl.generate()))
787
#def test_match_after_step(self):
788
# tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
789
# <span py:match="div/greeting">
790
# Hello ${select('@name')}
792
# <greeting name="Dude" />
794
# self.assertEqual("""<div>
798
# </div>""", str(tmpl.generate()))
801
class StripDirectiveTestCase(unittest.TestCase):
802
"""Tests for the `py:strip` template directive."""
804
def test_strip_false(self):
805
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
806
<div py:strip="False"><b>foo</b></div>
808
self.assertEqual("""<div>
809
<div><b>foo</b></div>
810
</div>""", str(tmpl.generate()))
812
def test_strip_empty(self):
813
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
814
<div py:strip=""><b>foo</b></div>
816
self.assertEqual("""<div>
818
</div>""", str(tmpl.generate()))
821
class WithDirectiveTestCase(unittest.TestCase):
822
"""Tests for the `py:with` template directive."""
824
def test_shadowing(self):
825
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
827
<span py:with="x = x * 2" py:replace="x"/>
830
self.assertEqual("""<div>
834
</div>""", str(tmpl.generate(x=42)))
836
def test_as_element(self):
837
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
838
<py:with vars="x = x * 2">${x}</py:with>
840
self.assertEqual("""<div>
842
</div>""", str(tmpl.generate(x=42)))
844
def test_multiple_vars_same_name(self):
845
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
848
foo = foo.replace('r', 'z')
853
self.assertEqual("""<div>
855
</div>""", str(tmpl.generate(x=42)))
857
def test_multiple_vars_single_assignment(self):
858
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
859
<py:with vars="x = y = z = 1">${x} ${y} ${z}</py:with>
861
self.assertEqual("""<div>
863
</div>""", str(tmpl.generate(x=42)))
865
def test_nested_vars_single_assignment(self):
866
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
867
<py:with vars="x, (y, z) = (1, (2, 3))">${x} ${y} ${z}</py:with>
869
self.assertEqual("""<div>
871
</div>""", str(tmpl.generate(x=42)))
873
def test_multiple_vars_trailing_semicolon(self):
874
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
875
<py:with vars="x = x * 2; y = x / 2;">${x} ${y}</py:with>
877
self.assertEqual("""<div>
879
</div>""", str(tmpl.generate(x=42)))
881
def test_semicolon_escape(self):
882
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
883
<py:with vars="x = 'here is a semicolon: ;'; y = 'here are two semicolons: ;;' ;">
888
self.assertEqual("""<div>
889
here is a semicolon: ;
890
here are two semicolons: ;;
891
</div>""", str(tmpl.generate()))
893
def test_unicode_expr(self):
894
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
895
<span py:with="weeks=(u'一', u'二', u'三', u'四', u'五', u'六', u'日')">
899
self.assertEqual("""<div>
903
</div>""", str(tmpl.generate()))
906
class TemplateTestCase(unittest.TestCase):
907
"""Tests for basic template processing, expression evaluation and error
911
def test_interpolate_string(self):
912
parts = list(Template._interpolate('bla'))
913
self.assertEqual(1, len(parts))
914
self.assertEqual(Stream.TEXT, parts[0][0])
915
self.assertEqual('bla', parts[0][1])
917
def test_interpolate_simple(self):
918
parts = list(Template._interpolate('${bla}'))
919
self.assertEqual(1, len(parts))
920
self.assertEqual(Template.EXPR, parts[0][0])
921
self.assertEqual('bla', parts[0][1].source)
923
def test_interpolate_escaped(self):
924
parts = list(Template._interpolate('$${bla}'))
925
self.assertEqual(1, len(parts))
926
self.assertEqual(Stream.TEXT, parts[0][0])
927
self.assertEqual('${bla}', parts[0][1])
929
def test_interpolate_short(self):
930
parts = list(Template._interpolate('$bla'))
931
self.assertEqual(1, len(parts))
932
self.assertEqual(Template.EXPR, parts[0][0])
933
self.assertEqual('bla', parts[0][1].source)
935
def test_interpolate_short_starting_with_underscore(self):
936
parts = list(Template._interpolate('$_bla'))
937
self.assertEqual(1, len(parts))
938
self.assertEqual(Template.EXPR, parts[0][0])
939
self.assertEqual('_bla', parts[0][1].source)
941
def test_interpolate_short_containing_underscore(self):
942
parts = list(Template._interpolate('$foo_bar'))
943
self.assertEqual(1, len(parts))
944
self.assertEqual(Template.EXPR, parts[0][0])
945
self.assertEqual('foo_bar', parts[0][1].source)
947
def test_interpolate_short_starting_with_dot(self):
948
parts = list(Template._interpolate('$.bla'))
949
self.assertEqual(1, len(parts))
950
self.assertEqual(Stream.TEXT, parts[0][0])
951
self.assertEqual('$.bla', parts[0][1])
953
def test_interpolate_short_containing_dot(self):
954
parts = list(Template._interpolate('$foo.bar'))
955
self.assertEqual(1, len(parts))
956
self.assertEqual(Template.EXPR, parts[0][0])
957
self.assertEqual('foo.bar', parts[0][1].source)
959
def test_interpolate_short_starting_with_digit(self):
960
parts = list(Template._interpolate('$0bla'))
961
self.assertEqual(1, len(parts))
962
self.assertEqual(Stream.TEXT, parts[0][0])
963
self.assertEqual('$0bla', parts[0][1])
965
def test_interpolate_short_containing_digit(self):
966
parts = list(Template._interpolate('$foo0'))
967
self.assertEqual(1, len(parts))
968
self.assertEqual(Template.EXPR, parts[0][0])
969
self.assertEqual('foo0', parts[0][1].source)
971
def test_interpolate_mixed1(self):
972
parts = list(Template._interpolate('$foo bar $baz'))
973
self.assertEqual(3, len(parts))
974
self.assertEqual(Template.EXPR, parts[0][0])
975
self.assertEqual('foo', parts[0][1].source)
976
self.assertEqual(Stream.TEXT, parts[1][0])
977
self.assertEqual(' bar ', parts[1][1])
978
self.assertEqual(Template.EXPR, parts[2][0])
979
self.assertEqual('baz', parts[2][1].source)
981
def test_interpolate_mixed2(self):
982
parts = list(Template._interpolate('foo $bar baz'))
983
self.assertEqual(3, len(parts))
984
self.assertEqual(Stream.TEXT, parts[0][0])
985
self.assertEqual('foo ', parts[0][1])
986
self.assertEqual(Template.EXPR, parts[1][0])
987
self.assertEqual('bar', parts[1][1].source)
988
self.assertEqual(Stream.TEXT, parts[2][0])
989
self.assertEqual(' baz', parts[2][1])
992
class MarkupTemplateTestCase(unittest.TestCase):
993
"""Tests for markup template processing."""
995
def test_interpolate_mixed3(self):
996
tmpl = MarkupTemplate('<root> ${var} $var</root>')
997
self.assertEqual('<root> 42 42</root>', str(tmpl.generate(var=42)))
999
def test_interpolate_leading_trailing_space(self):
1000
tmpl = MarkupTemplate('<root>${ foo }</root>')
1001
self.assertEqual('<root>bar</root>', str(tmpl.generate(foo='bar')))
1003
def test_interpolate_multiline(self):
1004
tmpl = MarkupTemplate("""<root>${dict(
1007
self.assertEqual('<root>baz</root>', str(tmpl.generate(foo='bar')))
1009
def test_interpolate_non_string_attrs(self):
1010
tmpl = MarkupTemplate('<root attr="${1}"/>')
1011
self.assertEqual('<root attr="1"/>', str(tmpl.generate()))
1013
def test_interpolate_list_result(self):
1014
tmpl = MarkupTemplate('<root>$foo</root>')
1015
self.assertEqual('<root>buzz</root>', str(tmpl.generate(foo=('buzz',))))
1017
def test_empty_attr(self):
1018
tmpl = MarkupTemplate('<root attr=""/>')
1019
self.assertEqual('<root attr=""/>', str(tmpl.generate()))
1021
def test_bad_directive_error(self):
1022
xml = '<p xmlns:py="http://genshi.edgewall.org/" py:do="nothing" />'
1024
tmpl = MarkupTemplate(xml, filename='test.html')
1025
except BadDirectiveError, e:
1026
self.assertEqual('test.html', e.filename)
1027
if sys.version_info[:2] >= (2, 4):
1028
self.assertEqual(1, e.lineno)
1030
def test_directive_value_syntax_error(self):
1031
xml = """<p xmlns:py="http://genshi.edgewall.org/" py:if="bar'" />"""
1033
tmpl = MarkupTemplate(xml, filename='test.html')
1034
self.fail('Expected SyntaxError')
1035
except TemplateSyntaxError, e:
1036
self.assertEqual('test.html', e.filename)
1037
if sys.version_info[:2] >= (2, 4):
1038
self.assertEqual(1, e.lineno)
1040
def test_expression_syntax_error(self):
1042
Foo <em>${bar"}</em>
1045
tmpl = MarkupTemplate(xml, filename='test.html')
1046
self.fail('Expected SyntaxError')
1047
except TemplateSyntaxError, e:
1048
self.assertEqual('test.html', e.filename)
1049
if sys.version_info[:2] >= (2, 4):
1050
self.assertEqual(2, e.lineno)
1052
def test_expression_syntax_error_multi_line(self):
1053
xml = """<p><em></em>
1059
tmpl = MarkupTemplate(xml, filename='test.html')
1060
self.fail('Expected SyntaxError')
1061
except TemplateSyntaxError, e:
1062
self.assertEqual('test.html', e.filename)
1063
if sys.version_info[:2] >= (2, 4):
1064
self.assertEqual(3, e.lineno)
1066
def test_markup_noescape(self):
1068
Verify that outputting context data that is a `Markup` instance is not
1071
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1074
self.assertEqual("""<div>
1076
</div>""", str(tmpl.generate(myvar=Markup('<b>foo</b>'))))
1078
def test_text_noescape_quotes(self):
1080
Verify that outputting context data in text nodes doesn't escape quotes.
1082
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1085
self.assertEqual("""<div>
1087
</div>""", str(tmpl.generate(myvar='"foo"')))
1089
def test_attr_escape_quotes(self):
1091
Verify that outputting context data in attribtes escapes quotes.
1093
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1094
<elem class="$myvar"/>
1096
self.assertEqual("""<div>
1097
<elem class=""foo""/>
1098
</div>""", str(tmpl.generate(myvar='"foo"')))
1100
def test_directive_element(self):
1101
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1102
<py:if test="myvar">bar</py:if>
1104
self.assertEqual("""<div>
1106
</div>""", str(tmpl.generate(myvar='"foo"')))
1108
def test_normal_comment(self):
1109
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1112
self.assertEqual("""<div>
1114
</div>""", str(tmpl.generate()))
1116
def test_template_comment(self):
1117
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1121
self.assertEqual("""<div>
1122
</div>""", str(tmpl.generate()))
1124
def test_parse_with_same_namespace_nested(self):
1125
tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1126
<span xmlns:py="http://genshi.edgewall.org/">
1129
self.assertEqual("""<div>
1132
</div>""", str(tmpl.generate()))
1134
def test_latin1_encoded_with_xmldecl(self):
1135
tmpl = MarkupTemplate(u"""<?xml version="1.0" encoding="iso-8859-1" ?>
1136
<div xmlns:py="http://genshi.edgewall.org/">
1138
</div>""".encode('iso-8859-1'), encoding='iso-8859-1')
1139
self.assertEqual(u"""<div>
1141
</div>""", unicode(tmpl.generate()))
1143
def test_latin1_encoded_explicit_encoding(self):
1144
tmpl = MarkupTemplate(u"""<div xmlns:py="http://genshi.edgewall.org/">
1146
</div>""".encode('iso-8859-1'), encoding='iso-8859-1')
1147
self.assertEqual(u"""<div>
1149
</div>""", unicode(tmpl.generate()))
1152
class TextTemplateTestCase(unittest.TestCase):
1153
"""Tests for text template processing."""
1155
def test_escaping(self):
1156
tmpl = TextTemplate('\\#escaped')
1157
self.assertEqual('#escaped', str(tmpl.generate()))
1159
def test_comment(self):
1160
tmpl = TextTemplate('## a comment')
1161
self.assertEqual('', str(tmpl.generate()))
1163
def test_comment_escaping(self):
1164
tmpl = TextTemplate('\\## escaped comment')
1165
self.assertEqual('## escaped comment', str(tmpl.generate()))
1167
def test_end_with_args(self):
1168
tmpl = TextTemplate("""
1172
self.assertEqual('\n', str(tmpl.generate()))
1174
def test_latin1_encoded(self):
1175
text = u'$foo\xf6$bar'.encode('iso-8859-1')
1176
tmpl = TextTemplate(text, encoding='iso-8859-1')
1177
self.assertEqual(u'x\xf6y', unicode(tmpl.generate(foo='x', bar='y')))
1179
def test_empty_lines1(self):
1180
tmpl = TextTemplate("""Your items:
1185
self.assertEqual("""Your items:
1190
""", tmpl.generate(items=range(3)).render('text'))
1192
def test_empty_lines2(self):
1193
tmpl = TextTemplate("""Your items:
1199
self.assertEqual("""Your items:
1207
""", tmpl.generate(items=range(3)).render('text'))
1210
class TemplateLoaderTestCase(unittest.TestCase):
1211
"""Tests for the template loader."""
1214
self.dirname = tempfile.mkdtemp(suffix='markup_test')
1217
shutil.rmtree(self.dirname)
1219
def test_relative_include_samedir(self):
1220
file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
1222
file1.write("""<div>Included</div>""")
1226
file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
1228
file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
1229
<xi:include href="tmpl1.html" />
1234
loader = TemplateLoader([self.dirname])
1235
tmpl = loader.load('tmpl2.html')
1236
self.assertEqual("""<html>
1238
</html>""", tmpl.generate().render())
1240
def test_relative_include_subdir(self):
1241
os.mkdir(os.path.join(self.dirname, 'sub'))
1242
file1 = open(os.path.join(self.dirname, 'sub', 'tmpl1.html'), 'w')
1244
file1.write("""<div>Included</div>""")
1248
file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
1250
file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
1251
<xi:include href="sub/tmpl1.html" />
1256
loader = TemplateLoader([self.dirname])
1257
tmpl = loader.load('tmpl2.html')
1258
self.assertEqual("""<html>
1260
</html>""", tmpl.generate().render())
1262
def test_relative_include_parentdir(self):
1263
file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
1265
file1.write("""<div>Included</div>""")
1269
os.mkdir(os.path.join(self.dirname, 'sub'))
1270
file2 = open(os.path.join(self.dirname, 'sub', 'tmpl2.html'), 'w')
1272
file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
1273
<xi:include href="../tmpl1.html" />
1278
loader = TemplateLoader([self.dirname])
1279
tmpl = loader.load('sub/tmpl2.html')
1280
self.assertEqual("""<html>
1282
</html>""", tmpl.generate().render())
1284
def test_relative_include_without_search_path(self):
1285
file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
1287
file1.write("""<div>Included</div>""")
1291
file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
1293
file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
1294
<xi:include href="tmpl1.html" />
1299
loader = TemplateLoader()
1300
tmpl = loader.load(os.path.join(self.dirname, 'tmpl2.html'))
1301
self.assertEqual("""<html>
1303
</html>""", tmpl.generate().render())
1305
def test_relative_include_without_search_path_nested(self):
1306
file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
1308
file1.write("""<div>Included</div>""")
1312
file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
1314
file2.write("""<div xmlns:xi="http://www.w3.org/2001/XInclude">
1315
<xi:include href="tmpl1.html" />
1320
file3 = open(os.path.join(self.dirname, 'tmpl3.html'), 'w')
1322
file3.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
1323
<xi:include href="tmpl2.html" />
1328
loader = TemplateLoader()
1329
tmpl = loader.load(os.path.join(self.dirname, 'tmpl3.html'))
1330
self.assertEqual("""<html>
1334
</html>""", tmpl.generate().render())
1336
def test_relative_include_from_inmemory_template(self):
1337
file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
1339
file1.write("""<div>Included</div>""")
1343
loader = TemplateLoader([self.dirname])
1344
tmpl2 = MarkupTemplate("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
1345
<xi:include href="../tmpl1.html" />
1346
</html>""", filename='subdir/tmpl2.html', loader=loader)
1348
self.assertEqual("""<html>
1350
</html>""", tmpl2.generate().render())
1352
def test_load_with_default_encoding(self):
1353
f = open(os.path.join(self.dirname, 'tmpl.html'), 'w')
1355
f.write(u'<div>\xf6</div>'.encode('iso-8859-1'))
1358
loader = TemplateLoader([self.dirname], default_encoding='iso-8859-1')
1359
loader.load('tmpl.html')
1361
def test_load_with_explicit_encoding(self):
1362
f = open(os.path.join(self.dirname, 'tmpl.html'), 'w')
1364
f.write(u'<div>\xf6</div>'.encode('iso-8859-1'))
1367
loader = TemplateLoader([self.dirname], default_encoding='utf-8')
1368
loader.load('tmpl.html', encoding='iso-8859-1')
1372
suite = unittest.TestSuite()
1373
suite.addTest(doctest.DocTestSuite(template))
1374
suite.addTest(unittest.makeSuite(AttrsDirectiveTestCase, 'test'))
1375
suite.addTest(unittest.makeSuite(ChooseDirectiveTestCase, 'test'))
1376
suite.addTest(unittest.makeSuite(DefDirectiveTestCase, 'test'))
1377
suite.addTest(unittest.makeSuite(ForDirectiveTestCase, 'test'))
1378
suite.addTest(unittest.makeSuite(IfDirectiveTestCase, 'test'))
1379
suite.addTest(unittest.makeSuite(MatchDirectiveTestCase, 'test'))
1380
suite.addTest(unittest.makeSuite(StripDirectiveTestCase, 'test'))
1381
suite.addTest(unittest.makeSuite(WithDirectiveTestCase, 'test'))
1382
suite.addTest(unittest.makeSuite(TemplateTestCase, 'test'))
1383
suite.addTest(unittest.makeSuite(MarkupTemplateTestCase, 'test'))
1384
suite.addTest(unittest.makeSuite(TextTemplateTestCase, 'test'))
1385
suite.addTest(unittest.makeSuite(TemplateLoaderTestCase, 'test'))
1388
if __name__ == '__main__':
1389
unittest.main(defaultTest='suite')