lexnparse.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. # -*- coding: utf-8 -*-
  2. """
  3. jinja2.testsuite.lexnparse
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  5. All the unittests regarding lexing, parsing and syntax.
  6. :copyright: (c) 2010 by the Jinja Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import unittest
  10. from jinja2.testsuite import JinjaTestCase
  11. from jinja2 import Environment, Template, TemplateSyntaxError, \
  12. UndefinedError, nodes
  13. from jinja2._compat import next, iteritems, text_type, PY2
  14. from jinja2.lexer import Token, TokenStream, TOKEN_EOF, \
  15. TOKEN_BLOCK_BEGIN, TOKEN_BLOCK_END
  16. env = Environment()
  17. # how does a string look like in jinja syntax?
  18. if PY2:
  19. def jinja_string_repr(string):
  20. return repr(string)[1:]
  21. else:
  22. jinja_string_repr = repr
  23. class TokenStreamTestCase(JinjaTestCase):
  24. test_tokens = [Token(1, TOKEN_BLOCK_BEGIN, ''),
  25. Token(2, TOKEN_BLOCK_END, ''),
  26. ]
  27. def test_simple(self):
  28. ts = TokenStream(self.test_tokens, "foo", "bar")
  29. assert ts.current.type is TOKEN_BLOCK_BEGIN
  30. assert bool(ts)
  31. assert not bool(ts.eos)
  32. next(ts)
  33. assert ts.current.type is TOKEN_BLOCK_END
  34. assert bool(ts)
  35. assert not bool(ts.eos)
  36. next(ts)
  37. assert ts.current.type is TOKEN_EOF
  38. assert not bool(ts)
  39. assert bool(ts.eos)
  40. def test_iter(self):
  41. token_types = [t.type for t in TokenStream(self.test_tokens, "foo", "bar")]
  42. assert token_types == ['block_begin', 'block_end', ]
  43. class LexerTestCase(JinjaTestCase):
  44. def test_raw1(self):
  45. tmpl = env.from_string('{% raw %}foo{% endraw %}|'
  46. '{%raw%}{{ bar }}|{% baz %}{% endraw %}')
  47. assert tmpl.render() == 'foo|{{ bar }}|{% baz %}'
  48. def test_raw2(self):
  49. tmpl = env.from_string('1 {%- raw -%} 2 {%- endraw -%} 3')
  50. assert tmpl.render() == '123'
  51. def test_balancing(self):
  52. env = Environment('{%', '%}', '${', '}')
  53. tmpl = env.from_string('''{% for item in seq
  54. %}${{'foo': item}|upper}{% endfor %}''')
  55. assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
  56. def test_comments(self):
  57. env = Environment('<!--', '-->', '{', '}')
  58. tmpl = env.from_string('''\
  59. <ul>
  60. <!--- for item in seq -->
  61. <li>{item}</li>
  62. <!--- endfor -->
  63. </ul>''')
  64. assert tmpl.render(seq=list(range(3))) == ("<ul>\n <li>0</li>\n "
  65. "<li>1</li>\n <li>2</li>\n</ul>")
  66. def test_string_escapes(self):
  67. for char in u'\0', u'\u2668', u'\xe4', u'\t', u'\r', u'\n':
  68. tmpl = env.from_string('{{ %s }}' % jinja_string_repr(char))
  69. assert tmpl.render() == char
  70. assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u'\u2668'
  71. def test_bytefallback(self):
  72. from pprint import pformat
  73. tmpl = env.from_string(u'''{{ 'foo'|pprint }}|{{ 'bär'|pprint }}''')
  74. assert tmpl.render() == pformat('foo') + '|' + pformat(u'bär')
  75. def test_operators(self):
  76. from jinja2.lexer import operators
  77. for test, expect in iteritems(operators):
  78. if test in '([{}])':
  79. continue
  80. stream = env.lexer.tokenize('{{ %s }}' % test)
  81. next(stream)
  82. assert stream.current.type == expect
  83. def test_normalizing(self):
  84. for seq in '\r', '\r\n', '\n':
  85. env = Environment(newline_sequence=seq)
  86. tmpl = env.from_string('1\n2\r\n3\n4\n')
  87. result = tmpl.render()
  88. assert result.replace(seq, 'X') == '1X2X3X4'
  89. def test_trailing_newline(self):
  90. for keep in [True, False]:
  91. env = Environment(keep_trailing_newline=keep)
  92. for template,expected in [
  93. ('', {}),
  94. ('no\nnewline', {}),
  95. ('with\nnewline\n', {False: 'with\nnewline'}),
  96. ('with\nseveral\n\n\n', {False: 'with\nseveral\n\n'}),
  97. ]:
  98. tmpl = env.from_string(template)
  99. expect = expected.get(keep, template)
  100. result = tmpl.render()
  101. assert result == expect, (keep, template, result, expect)
  102. class ParserTestCase(JinjaTestCase):
  103. def test_php_syntax(self):
  104. env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->')
  105. tmpl = env.from_string('''\
  106. <!-- I'm a comment, I'm not interesting -->\
  107. <? for item in seq -?>
  108. <?= item ?>
  109. <?- endfor ?>''')
  110. assert tmpl.render(seq=list(range(5))) == '01234'
  111. def test_erb_syntax(self):
  112. env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>')
  113. tmpl = env.from_string('''\
  114. <%# I'm a comment, I'm not interesting %>\
  115. <% for item in seq -%>
  116. <%= item %>
  117. <%- endfor %>''')
  118. assert tmpl.render(seq=list(range(5))) == '01234'
  119. def test_comment_syntax(self):
  120. env = Environment('<!--', '-->', '${', '}', '<!--#', '-->')
  121. tmpl = env.from_string('''\
  122. <!--# I'm a comment, I'm not interesting -->\
  123. <!-- for item in seq --->
  124. ${item}
  125. <!--- endfor -->''')
  126. assert tmpl.render(seq=list(range(5))) == '01234'
  127. def test_balancing(self):
  128. tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''')
  129. assert tmpl.render() == 'bar'
  130. def test_start_comment(self):
  131. tmpl = env.from_string('''{# foo comment
  132. and bar comment #}
  133. {% macro blub() %}foo{% endmacro %}
  134. {{ blub() }}''')
  135. assert tmpl.render().strip() == 'foo'
  136. def test_line_syntax(self):
  137. env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%')
  138. tmpl = env.from_string('''\
  139. <%# regular comment %>
  140. % for item in seq:
  141. ${item}
  142. % endfor''')
  143. assert [int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()] == \
  144. list(range(5))
  145. env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##')
  146. tmpl = env.from_string('''\
  147. <%# regular comment %>
  148. % for item in seq:
  149. ${item} ## the rest of the stuff
  150. % endfor''')
  151. assert [int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()] == \
  152. list(range(5))
  153. def test_line_syntax_priority(self):
  154. # XXX: why is the whitespace there in front of the newline?
  155. env = Environment('{%', '%}', '${', '}', '/*', '*/', '##', '#')
  156. tmpl = env.from_string('''\
  157. /* ignore me.
  158. I'm a multiline comment */
  159. ## for item in seq:
  160. * ${item} # this is just extra stuff
  161. ## endfor''')
  162. assert tmpl.render(seq=[1, 2]).strip() == '* 1\n* 2'
  163. env = Environment('{%', '%}', '${', '}', '/*', '*/', '#', '##')
  164. tmpl = env.from_string('''\
  165. /* ignore me.
  166. I'm a multiline comment */
  167. # for item in seq:
  168. * ${item} ## this is just extra stuff
  169. ## extra stuff i just want to ignore
  170. # endfor''')
  171. assert tmpl.render(seq=[1, 2]).strip() == '* 1\n\n* 2'
  172. def test_error_messages(self):
  173. def assert_error(code, expected):
  174. try:
  175. Template(code)
  176. except TemplateSyntaxError as e:
  177. assert str(e) == expected, 'unexpected error message'
  178. else:
  179. assert False, 'that was supposed to be an error'
  180. assert_error('{% for item in seq %}...{% endif %}',
  181. "Encountered unknown tag 'endif'. Jinja was looking "
  182. "for the following tags: 'endfor' or 'else'. The "
  183. "innermost block that needs to be closed is 'for'.")
  184. assert_error('{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}',
  185. "Encountered unknown tag 'endfor'. Jinja was looking for "
  186. "the following tags: 'elif' or 'else' or 'endif'. The "
  187. "innermost block that needs to be closed is 'if'.")
  188. assert_error('{% if foo %}',
  189. "Unexpected end of template. Jinja was looking for the "
  190. "following tags: 'elif' or 'else' or 'endif'. The "
  191. "innermost block that needs to be closed is 'if'.")
  192. assert_error('{% for item in seq %}',
  193. "Unexpected end of template. Jinja was looking for the "
  194. "following tags: 'endfor' or 'else'. The innermost block "
  195. "that needs to be closed is 'for'.")
  196. assert_error('{% block foo-bar-baz %}',
  197. "Block names in Jinja have to be valid Python identifiers "
  198. "and may not contain hyphens, use an underscore instead.")
  199. assert_error('{% unknown_tag %}',
  200. "Encountered unknown tag 'unknown_tag'.")
  201. class SyntaxTestCase(JinjaTestCase):
  202. def test_call(self):
  203. env = Environment()
  204. env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g
  205. tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}")
  206. assert tmpl.render() == 'abdfh'
  207. def test_slicing(self):
  208. tmpl = env.from_string('{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}')
  209. assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
  210. def test_attr(self):
  211. tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}")
  212. assert tmpl.render(foo={'bar': 42}) == '42|42'
  213. def test_subscript(self):
  214. tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}")
  215. assert tmpl.render(foo=[0, 1, 2]) == '0|2'
  216. def test_tuple(self):
  217. tmpl = env.from_string('{{ () }}|{{ (1,) }}|{{ (1, 2) }}')
  218. assert tmpl.render() == '()|(1,)|(1, 2)'
  219. def test_math(self):
  220. tmpl = env.from_string('{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}')
  221. assert tmpl.render() == '1.5|8'
  222. def test_div(self):
  223. tmpl = env.from_string('{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}')
  224. assert tmpl.render() == '1|1.5|1'
  225. def test_unary(self):
  226. tmpl = env.from_string('{{ +3 }}|{{ -3 }}')
  227. assert tmpl.render() == '3|-3'
  228. def test_concat(self):
  229. tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}")
  230. assert tmpl.render() == '[1, 2]foo'
  231. def test_compare(self):
  232. tmpl = env.from_string('{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|'
  233. '{{ 2 == 2 }}|{{ 1 <= 1 }}')
  234. assert tmpl.render() == 'True|True|True|True|True'
  235. def test_inop(self):
  236. tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}')
  237. assert tmpl.render() == 'True|False'
  238. def test_literals(self):
  239. tmpl = env.from_string('{{ [] }}|{{ {} }}|{{ () }}')
  240. assert tmpl.render().lower() == '[]|{}|()'
  241. def test_bool(self):
  242. tmpl = env.from_string('{{ true and false }}|{{ false '
  243. 'or true }}|{{ not false }}')
  244. assert tmpl.render() == 'False|True|True'
  245. def test_grouping(self):
  246. tmpl = env.from_string('{{ (true and false) or (false and true) and not false }}')
  247. assert tmpl.render() == 'False'
  248. def test_django_attr(self):
  249. tmpl = env.from_string('{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}')
  250. assert tmpl.render() == '1|1'
  251. def test_conditional_expression(self):
  252. tmpl = env.from_string('''{{ 0 if true else 1 }}''')
  253. assert tmpl.render() == '0'
  254. def test_short_conditional_expression(self):
  255. tmpl = env.from_string('<{{ 1 if false }}>')
  256. assert tmpl.render() == '<>'
  257. tmpl = env.from_string('<{{ (1 if false).bar }}>')
  258. self.assert_raises(UndefinedError, tmpl.render)
  259. def test_filter_priority(self):
  260. tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}')
  261. assert tmpl.render() == 'FOOBAR'
  262. def test_function_calls(self):
  263. tests = [
  264. (True, '*foo, bar'),
  265. (True, '*foo, *bar'),
  266. (True, '*foo, bar=42'),
  267. (True, '**foo, *bar'),
  268. (True, '**foo, bar'),
  269. (False, 'foo, bar'),
  270. (False, 'foo, bar=42'),
  271. (False, 'foo, bar=23, *args'),
  272. (False, 'a, b=c, *d, **e'),
  273. (False, '*foo, **bar')
  274. ]
  275. for should_fail, sig in tests:
  276. if should_fail:
  277. self.assert_raises(TemplateSyntaxError,
  278. env.from_string, '{{ foo(%s) }}' % sig)
  279. else:
  280. env.from_string('foo(%s)' % sig)
  281. def test_tuple_expr(self):
  282. for tmpl in [
  283. '{{ () }}',
  284. '{{ (1, 2) }}',
  285. '{{ (1, 2,) }}',
  286. '{{ 1, }}',
  287. '{{ 1, 2 }}',
  288. '{% for foo, bar in seq %}...{% endfor %}',
  289. '{% for x in foo, bar %}...{% endfor %}',
  290. '{% for x in foo, %}...{% endfor %}'
  291. ]:
  292. assert env.from_string(tmpl)
  293. def test_trailing_comma(self):
  294. tmpl = env.from_string('{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}')
  295. assert tmpl.render().lower() == '(1, 2)|[1, 2]|{1: 2}'
  296. def test_block_end_name(self):
  297. env.from_string('{% block foo %}...{% endblock foo %}')
  298. self.assert_raises(TemplateSyntaxError, env.from_string,
  299. '{% block x %}{% endblock y %}')
  300. def test_constant_casing(self):
  301. for const in True, False, None:
  302. tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % (
  303. str(const), str(const).lower(), str(const).upper()
  304. ))
  305. assert tmpl.render() == '%s|%s|' % (const, const)
  306. def test_test_chaining(self):
  307. self.assert_raises(TemplateSyntaxError, env.from_string,
  308. '{{ foo is string is sequence }}')
  309. assert env.from_string('{{ 42 is string or 42 is number }}'
  310. ).render() == 'True'
  311. def test_string_concatenation(self):
  312. tmpl = env.from_string('{{ "foo" "bar" "baz" }}')
  313. assert tmpl.render() == 'foobarbaz'
  314. def test_notin(self):
  315. bar = range(100)
  316. tmpl = env.from_string('''{{ not 42 in bar }}''')
  317. assert tmpl.render(bar=bar) == text_type(not 42 in bar)
  318. def test_implicit_subscribed_tuple(self):
  319. class Foo(object):
  320. def __getitem__(self, x):
  321. return x
  322. t = env.from_string('{{ foo[1, 2] }}')
  323. assert t.render(foo=Foo()) == u'(1, 2)'
  324. def test_raw2(self):
  325. tmpl = env.from_string('{% raw %}{{ FOO }} and {% BAR %}{% endraw %}')
  326. assert tmpl.render() == '{{ FOO }} and {% BAR %}'
  327. def test_const(self):
  328. tmpl = env.from_string('{{ true }}|{{ false }}|{{ none }}|'
  329. '{{ none is defined }}|{{ missing is defined }}')
  330. assert tmpl.render() == 'True|False|None|True|False'
  331. def test_neg_filter_priority(self):
  332. node = env.parse('{{ -1|foo }}')
  333. assert isinstance(node.body[0].nodes[0], nodes.Filter)
  334. assert isinstance(node.body[0].nodes[0].node, nodes.Neg)
  335. def test_const_assign(self):
  336. constass1 = '''{% set true = 42 %}'''
  337. constass2 = '''{% for none in seq %}{% endfor %}'''
  338. for tmpl in constass1, constass2:
  339. self.assert_raises(TemplateSyntaxError, env.from_string, tmpl)
  340. def test_localset(self):
  341. tmpl = env.from_string('''{% set foo = 0 %}\
  342. {% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\
  343. {{ foo }}''')
  344. assert tmpl.render() == '0'
  345. def test_parse_unary(self):
  346. tmpl = env.from_string('{{ -foo["bar"] }}')
  347. assert tmpl.render(foo={'bar': 42}) == '-42'
  348. tmpl = env.from_string('{{ -foo["bar"]|abs }}')
  349. assert tmpl.render(foo={'bar': 42}) == '42'
  350. class LstripBlocksTestCase(JinjaTestCase):
  351. def test_lstrip(self):
  352. env = Environment(lstrip_blocks=True, trim_blocks=False)
  353. tmpl = env.from_string(''' {% if True %}\n {% endif %}''')
  354. assert tmpl.render() == "\n"
  355. def test_lstrip_trim(self):
  356. env = Environment(lstrip_blocks=True, trim_blocks=True)
  357. tmpl = env.from_string(''' {% if True %}\n {% endif %}''')
  358. assert tmpl.render() == ""
  359. def test_no_lstrip(self):
  360. env = Environment(lstrip_blocks=True, trim_blocks=False)
  361. tmpl = env.from_string(''' {%+ if True %}\n {%+ endif %}''')
  362. assert tmpl.render() == " \n "
  363. def test_lstrip_endline(self):
  364. env = Environment(lstrip_blocks=True, trim_blocks=False)
  365. tmpl = env.from_string(''' hello{% if True %}\n goodbye{% endif %}''')
  366. assert tmpl.render() == " hello\n goodbye"
  367. def test_lstrip_inline(self):
  368. env = Environment(lstrip_blocks=True, trim_blocks=False)
  369. tmpl = env.from_string(''' {% if True %}hello {% endif %}''')
  370. assert tmpl.render() == 'hello '
  371. def test_lstrip_nested(self):
  372. env = Environment(lstrip_blocks=True, trim_blocks=False)
  373. tmpl = env.from_string(''' {% if True %}a {% if True %}b {% endif %}c {% endif %}''')
  374. assert tmpl.render() == 'a b c '
  375. def test_lstrip_left_chars(self):
  376. env = Environment(lstrip_blocks=True, trim_blocks=False)
  377. tmpl = env.from_string(''' abc {% if True %}
  378. hello{% endif %}''')
  379. assert tmpl.render() == ' abc \n hello'
  380. def test_lstrip_embeded_strings(self):
  381. env = Environment(lstrip_blocks=True, trim_blocks=False)
  382. tmpl = env.from_string(''' {% set x = " {% str %} " %}{{ x }}''')
  383. assert tmpl.render() == ' {% str %} '
  384. def test_lstrip_preserve_leading_newlines(self):
  385. env = Environment(lstrip_blocks=True, trim_blocks=False)
  386. tmpl = env.from_string('''\n\n\n{% set hello = 1 %}''')
  387. assert tmpl.render() == '\n\n\n'
  388. def test_lstrip_comment(self):
  389. env = Environment(lstrip_blocks=True, trim_blocks=False)
  390. tmpl = env.from_string(''' {# if True #}
  391. hello
  392. {#endif#}''')
  393. assert tmpl.render() == '\nhello\n'
  394. def test_lstrip_angle_bracket_simple(self):
  395. env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##',
  396. lstrip_blocks=True, trim_blocks=True)
  397. tmpl = env.from_string(''' <% if True %>hello <% endif %>''')
  398. assert tmpl.render() == 'hello '
  399. def test_lstrip_angle_bracket_comment(self):
  400. env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##',
  401. lstrip_blocks=True, trim_blocks=True)
  402. tmpl = env.from_string(''' <%# if True %>hello <%# endif %>''')
  403. assert tmpl.render() == 'hello '
  404. def test_lstrip_angle_bracket(self):
  405. env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##',
  406. lstrip_blocks=True, trim_blocks=True)
  407. tmpl = env.from_string('''\
  408. <%# regular comment %>
  409. <% for item in seq %>
  410. ${item} ## the rest of the stuff
  411. <% endfor %>''')
  412. assert tmpl.render(seq=range(5)) == \
  413. ''.join('%s\n' % x for x in range(5))
  414. def test_lstrip_angle_bracket_compact(self):
  415. env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##',
  416. lstrip_blocks=True, trim_blocks=True)
  417. tmpl = env.from_string('''\
  418. <%#regular comment%>
  419. <%for item in seq%>
  420. ${item} ## the rest of the stuff
  421. <%endfor%>''')
  422. assert tmpl.render(seq=range(5)) == \
  423. ''.join('%s\n' % x for x in range(5))
  424. def test_php_syntax_with_manual(self):
  425. env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->',
  426. lstrip_blocks=True, trim_blocks=True)
  427. tmpl = env.from_string('''\
  428. <!-- I'm a comment, I'm not interesting -->
  429. <? for item in seq -?>
  430. <?= item ?>
  431. <?- endfor ?>''')
  432. assert tmpl.render(seq=range(5)) == '01234'
  433. def test_php_syntax(self):
  434. env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->',
  435. lstrip_blocks=True, trim_blocks=True)
  436. tmpl = env.from_string('''\
  437. <!-- I'm a comment, I'm not interesting -->
  438. <? for item in seq ?>
  439. <?= item ?>
  440. <? endfor ?>''')
  441. assert tmpl.render(seq=range(5)) == ''.join(' %s\n' % x for x in range(5))
  442. def test_php_syntax_compact(self):
  443. env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->',
  444. lstrip_blocks=True, trim_blocks=True)
  445. tmpl = env.from_string('''\
  446. <!-- I'm a comment, I'm not interesting -->
  447. <?for item in seq?>
  448. <?=item?>
  449. <?endfor?>''')
  450. assert tmpl.render(seq=range(5)) == ''.join(' %s\n' % x for x in range(5))
  451. def test_erb_syntax(self):
  452. env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>',
  453. lstrip_blocks=True, trim_blocks=True)
  454. #env.from_string('')
  455. #for n,r in env.lexer.rules.iteritems():
  456. # print n
  457. #print env.lexer.rules['root'][0][0].pattern
  458. #print "'%s'" % tmpl.render(seq=range(5))
  459. tmpl = env.from_string('''\
  460. <%# I'm a comment, I'm not interesting %>
  461. <% for item in seq %>
  462. <%= item %>
  463. <% endfor %>
  464. ''')
  465. assert tmpl.render(seq=range(5)) == ''.join(' %s\n' % x for x in range(5))
  466. def test_erb_syntax_with_manual(self):
  467. env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>',
  468. lstrip_blocks=True, trim_blocks=True)
  469. tmpl = env.from_string('''\
  470. <%# I'm a comment, I'm not interesting %>
  471. <% for item in seq -%>
  472. <%= item %>
  473. <%- endfor %>''')
  474. assert tmpl.render(seq=range(5)) == '01234'
  475. def test_erb_syntax_no_lstrip(self):
  476. env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>',
  477. lstrip_blocks=True, trim_blocks=True)
  478. tmpl = env.from_string('''\
  479. <%# I'm a comment, I'm not interesting %>
  480. <%+ for item in seq -%>
  481. <%= item %>
  482. <%- endfor %>''')
  483. assert tmpl.render(seq=range(5)) == ' 01234'
  484. def test_comment_syntax(self):
  485. env = Environment('<!--', '-->', '${', '}', '<!--#', '-->',
  486. lstrip_blocks=True, trim_blocks=True)
  487. tmpl = env.from_string('''\
  488. <!--# I'm a comment, I'm not interesting -->\
  489. <!-- for item in seq --->
  490. ${item}
  491. <!--- endfor -->''')
  492. assert tmpl.render(seq=range(5)) == '01234'
  493. def suite():
  494. suite = unittest.TestSuite()
  495. suite.addTest(unittest.makeSuite(TokenStreamTestCase))
  496. suite.addTest(unittest.makeSuite(LexerTestCase))
  497. suite.addTest(unittest.makeSuite(ParserTestCase))
  498. suite.addTest(unittest.makeSuite(SyntaxTestCase))
  499. suite.addTest(unittest.makeSuite(LstripBlocksTestCase))
  500. return suite