You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1024 lines
34 KiB
1024 lines
34 KiB
import pytest
|
|
|
|
from jinja2 import Environment
|
|
from jinja2 import nodes
|
|
from jinja2 import Template
|
|
from jinja2 import TemplateSyntaxError
|
|
from jinja2 import UndefinedError
|
|
from jinja2.lexer import Token
|
|
from jinja2.lexer import TOKEN_BLOCK_BEGIN
|
|
from jinja2.lexer import TOKEN_BLOCK_END
|
|
from jinja2.lexer import TOKEN_EOF
|
|
from jinja2.lexer import TokenStream
|
|
|
|
|
|
class TestTokenStream:
|
|
test_tokens = [
|
|
Token(1, TOKEN_BLOCK_BEGIN, ""),
|
|
Token(2, TOKEN_BLOCK_END, ""),
|
|
]
|
|
|
|
def test_simple(self, env):
|
|
ts = TokenStream(self.test_tokens, "foo", "bar")
|
|
assert ts.current.type is TOKEN_BLOCK_BEGIN
|
|
assert bool(ts)
|
|
assert not bool(ts.eos)
|
|
next(ts)
|
|
assert ts.current.type is TOKEN_BLOCK_END
|
|
assert bool(ts)
|
|
assert not bool(ts.eos)
|
|
next(ts)
|
|
assert ts.current.type is TOKEN_EOF
|
|
assert not bool(ts)
|
|
assert bool(ts.eos)
|
|
|
|
def test_iter(self, env):
|
|
token_types = [t.type for t in TokenStream(self.test_tokens, "foo", "bar")]
|
|
assert token_types == [
|
|
"block_begin",
|
|
"block_end",
|
|
]
|
|
|
|
|
|
class TestLexer:
|
|
def test_raw1(self, env):
|
|
tmpl = env.from_string(
|
|
"{% raw %}foo{% endraw %}|"
|
|
"{%raw%}{{ bar }}|{% baz %}{% endraw %}"
|
|
)
|
|
assert tmpl.render() == "foo|{{ bar }}|{% baz %}"
|
|
|
|
def test_raw2(self, env):
|
|
tmpl = env.from_string("1 {%- raw -%} 2 {%- endraw -%} 3")
|
|
assert tmpl.render() == "123"
|
|
|
|
def test_raw3(self, env):
|
|
# The second newline after baz exists because it is AFTER the
|
|
# {% raw %} and is ignored.
|
|
env = Environment(lstrip_blocks=True, trim_blocks=True)
|
|
tmpl = env.from_string("bar\n{% raw %}\n {{baz}}2 spaces\n{% endraw %}\nfoo")
|
|
assert tmpl.render(baz="test") == "bar\n\n {{baz}}2 spaces\nfoo"
|
|
|
|
def test_raw4(self, env):
|
|
# The trailing dash of the {% raw -%} cleans both the spaces and
|
|
# newlines up to the first character of data.
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
"bar\n{%- raw -%}\n\n \n 2 spaces\n space{%- endraw -%}\nfoo"
|
|
)
|
|
assert tmpl.render() == "bar2 spaces\n spacefoo"
|
|
|
|
def test_balancing(self, env):
|
|
env = Environment("{%", "%}", "${", "}")
|
|
tmpl = env.from_string(
|
|
"""{% for item in seq
|
|
%}${{'foo': item}|upper}{% endfor %}"""
|
|
)
|
|
assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
|
|
|
|
def test_comments(self, env):
|
|
env = Environment("<!--", "-->", "{", "}")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<ul>
|
|
<!--- for item in seq -->
|
|
<li>{item}</li>
|
|
<!--- endfor -->
|
|
</ul>"""
|
|
)
|
|
assert tmpl.render(seq=list(range(3))) == (
|
|
"<ul>\n <li>0</li>\n <li>1</li>\n <li>2</li>\n</ul>"
|
|
)
|
|
|
|
def test_string_escapes(self, env):
|
|
for char in "\0", "\u2668", "\xe4", "\t", "\r", "\n":
|
|
tmpl = env.from_string(f"{{{{ {char!r} }}}}")
|
|
assert tmpl.render() == char
|
|
assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == "\u2668"
|
|
|
|
def test_bytefallback(self, env):
|
|
from pprint import pformat
|
|
|
|
tmpl = env.from_string("""{{ 'foo'|pprint }}|{{ 'bär'|pprint }}""")
|
|
assert tmpl.render() == pformat("foo") + "|" + pformat("bär")
|
|
|
|
def test_operators(self, env):
|
|
from jinja2.lexer import operators
|
|
|
|
for test, expect in operators.items():
|
|
if test in "([{}])":
|
|
continue
|
|
stream = env.lexer.tokenize(f"{{{{ {test} }}}}")
|
|
next(stream)
|
|
assert stream.current.type == expect
|
|
|
|
def test_normalizing(self, env):
|
|
for seq in "\r", "\r\n", "\n":
|
|
env = Environment(newline_sequence=seq)
|
|
tmpl = env.from_string("1\n2\r\n3\n4\n")
|
|
result = tmpl.render()
|
|
assert result.replace(seq, "X") == "1X2X3X4"
|
|
|
|
def test_trailing_newline(self, env):
|
|
for keep in [True, False]:
|
|
env = Environment(keep_trailing_newline=keep)
|
|
for template, expected in [
|
|
("", {}),
|
|
("no\nnewline", {}),
|
|
("with\nnewline\n", {False: "with\nnewline"}),
|
|
("with\nseveral\n\n\n", {False: "with\nseveral\n\n"}),
|
|
]:
|
|
tmpl = env.from_string(template)
|
|
expect = expected.get(keep, template)
|
|
result = tmpl.render()
|
|
assert result == expect, (keep, template, result, expect)
|
|
|
|
@pytest.mark.parametrize(
|
|
("name", "valid"),
|
|
[
|
|
("foo", True),
|
|
("föö", True),
|
|
("き", True),
|
|
("_", True),
|
|
("1a", False), # invalid ascii start
|
|
("a-", False), # invalid ascii continue
|
|
("\U0001f40da", False), # invalid unicode start
|
|
("a🐍\U0001f40d", False), # invalid unicode continue
|
|
# start characters not matched by \w
|
|
("\u1885", True),
|
|
("\u1886", True),
|
|
("\u2118", True),
|
|
("\u212e", True),
|
|
# continue character not matched by \w
|
|
("\xb7", False),
|
|
("a\xb7", True),
|
|
],
|
|
)
|
|
def test_name(self, env, name, valid):
|
|
t = "{{ " + name + " }}"
|
|
|
|
if valid:
|
|
# valid for version being tested, shouldn't raise
|
|
env.from_string(t)
|
|
else:
|
|
pytest.raises(TemplateSyntaxError, env.from_string, t)
|
|
|
|
def test_lineno_with_strip(self, env):
|
|
tokens = env.lex(
|
|
"""\
|
|
<html>
|
|
<body>
|
|
{%- block content -%}
|
|
<hr>
|
|
{{ item }}
|
|
{% endblock %}
|
|
</body>
|
|
</html>"""
|
|
)
|
|
for tok in tokens:
|
|
lineno, token_type, value = tok
|
|
if token_type == "name" and value == "item":
|
|
assert lineno == 5
|
|
break
|
|
|
|
|
|
class TestParser:
|
|
def test_php_syntax(self, env):
|
|
env = Environment("<?", "?>", "<?=", "?>", "<!--", "-->")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<!-- I'm a comment, I'm not interesting -->\
|
|
<? for item in seq -?>
|
|
<?= item ?>
|
|
<?- endfor ?>"""
|
|
)
|
|
assert tmpl.render(seq=list(range(5))) == "01234"
|
|
|
|
def test_erb_syntax(self, env):
|
|
env = Environment("<%", "%>", "<%=", "%>", "<%#", "%>")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%# I'm a comment, I'm not interesting %>\
|
|
<% for item in seq -%>
|
|
<%= item %>
|
|
<%- endfor %>"""
|
|
)
|
|
assert tmpl.render(seq=list(range(5))) == "01234"
|
|
|
|
def test_comment_syntax(self, env):
|
|
env = Environment("<!--", "-->", "${", "}", "<!--#", "-->")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<!--# I'm a comment, I'm not interesting -->\
|
|
<!-- for item in seq --->
|
|
${item}
|
|
<!--- endfor -->"""
|
|
)
|
|
assert tmpl.render(seq=list(range(5))) == "01234"
|
|
|
|
def test_balancing(self, env):
|
|
tmpl = env.from_string("""{{{'foo':'bar'}.foo}}""")
|
|
assert tmpl.render() == "bar"
|
|
|
|
def test_start_comment(self, env):
|
|
tmpl = env.from_string(
|
|
"""{# foo comment
|
|
and bar comment #}
|
|
{% macro blub() %}foo{% endmacro %}
|
|
{{ blub() }}"""
|
|
)
|
|
assert tmpl.render().strip() == "foo"
|
|
|
|
def test_line_syntax(self, env):
|
|
env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%# regular comment %>
|
|
% for item in seq:
|
|
${item}
|
|
% endfor"""
|
|
)
|
|
assert [
|
|
int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()
|
|
] == list(range(5))
|
|
|
|
env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%", "##")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%# regular comment %>
|
|
% for item in seq:
|
|
${item} ## the rest of the stuff
|
|
% endfor"""
|
|
)
|
|
assert [
|
|
int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()
|
|
] == list(range(5))
|
|
|
|
def test_line_syntax_priority(self, env):
|
|
# XXX: why is the whitespace there in front of the newline?
|
|
env = Environment("{%", "%}", "${", "}", "/*", "*/", "##", "#")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
/* ignore me.
|
|
I'm a multiline comment */
|
|
## for item in seq:
|
|
* ${item} # this is just extra stuff
|
|
## endfor"""
|
|
)
|
|
assert tmpl.render(seq=[1, 2]).strip() == "* 1\n* 2"
|
|
env = Environment("{%", "%}", "${", "}", "/*", "*/", "#", "##")
|
|
tmpl = env.from_string(
|
|
"""\
|
|
/* ignore me.
|
|
I'm a multiline comment */
|
|
# for item in seq:
|
|
* ${item} ## this is just extra stuff
|
|
## extra stuff i just want to ignore
|
|
# endfor"""
|
|
)
|
|
assert tmpl.render(seq=[1, 2]).strip() == "* 1\n\n* 2"
|
|
|
|
def test_error_messages(self, env):
|
|
def assert_error(code, expected):
|
|
with pytest.raises(TemplateSyntaxError, match=expected):
|
|
Template(code)
|
|
|
|
assert_error(
|
|
"{% for item in seq %}...{% endif %}",
|
|
"Encountered unknown tag 'endif'. Jinja was looking "
|
|
"for the following tags: 'endfor' or 'else'. The "
|
|
"innermost block that needs to be closed is 'for'.",
|
|
)
|
|
assert_error(
|
|
"{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}",
|
|
"Encountered unknown tag 'endfor'. Jinja was looking for "
|
|
"the following tags: 'elif' or 'else' or 'endif'. The "
|
|
"innermost block that needs to be closed is 'if'.",
|
|
)
|
|
assert_error(
|
|
"{% if foo %}",
|
|
"Unexpected end of template. Jinja was looking for the "
|
|
"following tags: 'elif' or 'else' or 'endif'. The "
|
|
"innermost block that needs to be closed is 'if'.",
|
|
)
|
|
assert_error(
|
|
"{% for item in seq %}",
|
|
"Unexpected end of template. Jinja was looking for the "
|
|
"following tags: 'endfor' or 'else'. The innermost block "
|
|
"that needs to be closed is 'for'.",
|
|
)
|
|
assert_error(
|
|
"{% block foo-bar-baz %}",
|
|
"Block names in Jinja have to be valid Python identifiers "
|
|
"and may not contain hyphens, use an underscore instead.",
|
|
)
|
|
assert_error("{% unknown_tag %}", "Encountered unknown tag 'unknown_tag'.")
|
|
|
|
|
|
class TestSyntax:
|
|
def test_call(self, env):
|
|
env = Environment()
|
|
env.globals["foo"] = lambda a, b, c, e, g: a + b + c + e + g
|
|
tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}")
|
|
assert tmpl.render() == "abdfh"
|
|
|
|
def test_slicing(self, env):
|
|
tmpl = env.from_string("{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}")
|
|
assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]"
|
|
|
|
def test_attr(self, env):
|
|
tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}")
|
|
assert tmpl.render(foo={"bar": 42}) == "42|42"
|
|
|
|
def test_subscript(self, env):
|
|
tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}")
|
|
assert tmpl.render(foo=[0, 1, 2]) == "0|2"
|
|
|
|
def test_tuple(self, env):
|
|
tmpl = env.from_string("{{ () }}|{{ (1,) }}|{{ (1, 2) }}")
|
|
assert tmpl.render() == "()|(1,)|(1, 2)"
|
|
|
|
def test_math(self, env):
|
|
tmpl = env.from_string("{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}")
|
|
assert tmpl.render() == "1.5|8"
|
|
|
|
def test_div(self, env):
|
|
tmpl = env.from_string("{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}")
|
|
assert tmpl.render() == "1|1.5|1"
|
|
|
|
def test_unary(self, env):
|
|
tmpl = env.from_string("{{ +3 }}|{{ -3 }}")
|
|
assert tmpl.render() == "3|-3"
|
|
|
|
def test_concat(self, env):
|
|
tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}")
|
|
assert tmpl.render() == "[1, 2]foo"
|
|
|
|
@pytest.mark.parametrize(
|
|
("a", "op", "b"),
|
|
[
|
|
(1, ">", 0),
|
|
(1, ">=", 1),
|
|
(2, "<", 3),
|
|
(3, "<=", 4),
|
|
(4, "==", 4),
|
|
(4, "!=", 5),
|
|
],
|
|
)
|
|
def test_compare(self, env, a, op, b):
|
|
t = env.from_string(f"{{{{ {a} {op} {b} }}}}")
|
|
assert t.render() == "True"
|
|
|
|
def test_compare_parens(self, env):
|
|
t = env.from_string("{{ i * (j < 5) }}")
|
|
assert t.render(i=2, j=3) == "2"
|
|
|
|
@pytest.mark.parametrize(
|
|
("src", "expect"),
|
|
[
|
|
("{{ 4 < 2 < 3 }}", "False"),
|
|
("{{ a < b < c }}", "False"),
|
|
("{{ 4 > 2 > 3 }}", "False"),
|
|
("{{ a > b > c }}", "False"),
|
|
("{{ 4 > 2 < 3 }}", "True"),
|
|
("{{ a > b < c }}", "True"),
|
|
],
|
|
)
|
|
def test_compare_compound(self, env, src, expect):
|
|
t = env.from_string(src)
|
|
assert t.render(a=4, b=2, c=3) == expect
|
|
|
|
def test_inop(self, env):
|
|
tmpl = env.from_string("{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}")
|
|
assert tmpl.render() == "True|False"
|
|
|
|
@pytest.mark.parametrize("value", ("[]", "{}", "()"))
|
|
def test_collection_literal(self, env, value):
|
|
t = env.from_string(f"{{{{ {value} }}}}")
|
|
assert t.render() == value
|
|
|
|
@pytest.mark.parametrize(
|
|
("value", "expect"),
|
|
(
|
|
("1", "1"),
|
|
("123", "123"),
|
|
("12_34_56", "123456"),
|
|
("1.2", "1.2"),
|
|
("34.56", "34.56"),
|
|
("3_4.5_6", "34.56"),
|
|
("1e0", "1.0"),
|
|
("10e1", "100.0"),
|
|
("2.5e100", "2.5e+100"),
|
|
("2.5e+100", "2.5e+100"),
|
|
("25.6e-10", "2.56e-09"),
|
|
("1_2.3_4e5_6", "1.234e+57"),
|
|
),
|
|
)
|
|
def test_numeric_literal(self, env, value, expect):
|
|
t = env.from_string(f"{{{{ {value} }}}}")
|
|
assert t.render() == expect
|
|
|
|
def test_bool(self, env):
|
|
tmpl = env.from_string(
|
|
"{{ true and false }}|{{ false or true }}|{{ not false }}"
|
|
)
|
|
assert tmpl.render() == "False|True|True"
|
|
|
|
def test_grouping(self, env):
|
|
tmpl = env.from_string(
|
|
"{{ (true and false) or (false and true) and not false }}"
|
|
)
|
|
assert tmpl.render() == "False"
|
|
|
|
def test_django_attr(self, env):
|
|
tmpl = env.from_string("{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}")
|
|
assert tmpl.render() == "1|1"
|
|
|
|
def test_conditional_expression(self, env):
|
|
tmpl = env.from_string("""{{ 0 if true else 1 }}""")
|
|
assert tmpl.render() == "0"
|
|
|
|
def test_short_conditional_expression(self, env):
|
|
tmpl = env.from_string("<{{ 1 if false }}>")
|
|
assert tmpl.render() == "<>"
|
|
|
|
tmpl = env.from_string("<{{ (1 if false).bar }}>")
|
|
pytest.raises(UndefinedError, tmpl.render)
|
|
|
|
def test_filter_priority(self, env):
|
|
tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}')
|
|
assert tmpl.render() == "FOOBAR"
|
|
|
|
def test_function_calls(self, env):
|
|
tests = [
|
|
(True, "*foo, bar"),
|
|
(True, "*foo, *bar"),
|
|
(True, "**foo, *bar"),
|
|
(True, "**foo, bar"),
|
|
(True, "**foo, **bar"),
|
|
(True, "**foo, bar=42"),
|
|
(False, "foo, bar"),
|
|
(False, "foo, bar=42"),
|
|
(False, "foo, bar=23, *args"),
|
|
(False, "foo, *args, bar=23"),
|
|
(False, "a, b=c, *d, **e"),
|
|
(False, "*foo, bar=42"),
|
|
(False, "*foo, **bar"),
|
|
(False, "*foo, bar=42, **baz"),
|
|
(False, "foo, *args, bar=23, **baz"),
|
|
]
|
|
for should_fail, sig in tests:
|
|
if should_fail:
|
|
with pytest.raises(TemplateSyntaxError):
|
|
env.from_string(f"{{{{ foo({sig}) }}}}")
|
|
else:
|
|
env.from_string(f"foo({sig})")
|
|
|
|
def test_tuple_expr(self, env):
|
|
for tmpl in [
|
|
"{{ () }}",
|
|
"{{ (1, 2) }}",
|
|
"{{ (1, 2,) }}",
|
|
"{{ 1, }}",
|
|
"{{ 1, 2 }}",
|
|
"{% for foo, bar in seq %}...{% endfor %}",
|
|
"{% for x in foo, bar %}...{% endfor %}",
|
|
"{% for x in foo, %}...{% endfor %}",
|
|
]:
|
|
assert env.from_string(tmpl)
|
|
|
|
def test_trailing_comma(self, env):
|
|
tmpl = env.from_string("{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}")
|
|
assert tmpl.render().lower() == "(1, 2)|[1, 2]|{1: 2}"
|
|
|
|
def test_block_end_name(self, env):
|
|
env.from_string("{% block foo %}...{% endblock foo %}")
|
|
pytest.raises(
|
|
TemplateSyntaxError, env.from_string, "{% block x %}{% endblock y %}"
|
|
)
|
|
|
|
def test_constant_casing(self, env):
|
|
for const in True, False, None:
|
|
const = str(const)
|
|
tmpl = env.from_string(
|
|
f"{{{{ {const} }}}}|{{{{ {const.lower()} }}}}|{{{{ {const.upper()} }}}}"
|
|
)
|
|
assert tmpl.render() == f"{const}|{const}|"
|
|
|
|
def test_test_chaining(self, env):
|
|
pytest.raises(
|
|
TemplateSyntaxError, env.from_string, "{{ foo is string is sequence }}"
|
|
)
|
|
assert env.from_string("{{ 42 is string or 42 is number }}").render() == "True"
|
|
|
|
def test_string_concatenation(self, env):
|
|
tmpl = env.from_string('{{ "foo" "bar" "baz" }}')
|
|
assert tmpl.render() == "foobarbaz"
|
|
|
|
def test_notin(self, env):
|
|
bar = range(100)
|
|
tmpl = env.from_string("""{{ not 42 in bar }}""")
|
|
assert tmpl.render(bar=bar) == "False"
|
|
|
|
def test_operator_precedence(self, env):
|
|
tmpl = env.from_string("""{{ 2 * 3 + 4 % 2 + 1 - 2 }}""")
|
|
assert tmpl.render() == "5"
|
|
|
|
def test_implicit_subscribed_tuple(self, env):
|
|
class Foo:
|
|
def __getitem__(self, x):
|
|
return x
|
|
|
|
t = env.from_string("{{ foo[1, 2] }}")
|
|
assert t.render(foo=Foo()) == "(1, 2)"
|
|
|
|
def test_raw2(self, env):
|
|
tmpl = env.from_string("{% raw %}{{ FOO }} and {% BAR %}{% endraw %}")
|
|
assert tmpl.render() == "{{ FOO }} and {% BAR %}"
|
|
|
|
def test_const(self, env):
|
|
tmpl = env.from_string(
|
|
"{{ true }}|{{ false }}|{{ none }}|"
|
|
"{{ none is defined }}|{{ missing is defined }}"
|
|
)
|
|
assert tmpl.render() == "True|False|None|True|False"
|
|
|
|
def test_neg_filter_priority(self, env):
|
|
node = env.parse("{{ -1|foo }}")
|
|
assert isinstance(node.body[0].nodes[0], nodes.Filter)
|
|
assert isinstance(node.body[0].nodes[0].node, nodes.Neg)
|
|
|
|
def test_const_assign(self, env):
|
|
constass1 = """{% set true = 42 %}"""
|
|
constass2 = """{% for none in seq %}{% endfor %}"""
|
|
for tmpl in constass1, constass2:
|
|
pytest.raises(TemplateSyntaxError, env.from_string, tmpl)
|
|
|
|
def test_localset(self, env):
|
|
tmpl = env.from_string(
|
|
"""{% set foo = 0 %}\
|
|
{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\
|
|
{{ foo }}"""
|
|
)
|
|
assert tmpl.render() == "0"
|
|
|
|
def test_parse_unary(self, env):
|
|
tmpl = env.from_string('{{ -foo["bar"] }}')
|
|
assert tmpl.render(foo={"bar": 42}) == "-42"
|
|
tmpl = env.from_string('{{ -foo["bar"]|abs }}')
|
|
assert tmpl.render(foo={"bar": 42}) == "42"
|
|
|
|
|
|
class TestLstripBlocks:
|
|
def test_lstrip(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(""" {% if True %}\n {% endif %}""")
|
|
assert tmpl.render() == "\n"
|
|
|
|
def test_lstrip_trim(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=True)
|
|
tmpl = env.from_string(""" {% if True %}\n {% endif %}""")
|
|
assert tmpl.render() == ""
|
|
|
|
def test_no_lstrip(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""")
|
|
assert tmpl.render() == " \n "
|
|
|
|
def test_lstrip_blocks_false_with_no_lstrip(self, env):
|
|
# Test that + is a NOP (but does not cause an error) if lstrip_blocks=False
|
|
env = Environment(lstrip_blocks=False, trim_blocks=False)
|
|
tmpl = env.from_string(""" {% if True %}\n {% endif %}""")
|
|
assert tmpl.render() == " \n "
|
|
tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""")
|
|
assert tmpl.render() == " \n "
|
|
|
|
def test_lstrip_endline(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(""" hello{% if True %}\n goodbye{% endif %}""")
|
|
assert tmpl.render() == " hello\n goodbye"
|
|
|
|
def test_lstrip_inline(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(""" {% if True %}hello {% endif %}""")
|
|
assert tmpl.render() == "hello "
|
|
|
|
def test_lstrip_nested(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
""" {% if True %}a {% if True %}b {% endif %}c {% endif %}"""
|
|
)
|
|
assert tmpl.render() == "a b c "
|
|
|
|
def test_lstrip_left_chars(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
""" abc {% if True %}
|
|
hello{% endif %}"""
|
|
)
|
|
assert tmpl.render() == " abc \n hello"
|
|
|
|
def test_lstrip_embeded_strings(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(""" {% set x = " {% str %} " %}{{ x }}""")
|
|
assert tmpl.render() == " {% str %} "
|
|
|
|
def test_lstrip_preserve_leading_newlines(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string("""\n\n\n{% set hello = 1 %}""")
|
|
assert tmpl.render() == "\n\n\n"
|
|
|
|
def test_lstrip_comment(self, env):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
""" {# if True #}
|
|
hello
|
|
{#endif#}"""
|
|
)
|
|
assert tmpl.render() == "\nhello\n"
|
|
|
|
def test_lstrip_angle_bracket_simple(self, env):
|
|
env = Environment(
|
|
"<%",
|
|
"%>",
|
|
"${",
|
|
"}",
|
|
"<%#",
|
|
"%>",
|
|
"%",
|
|
"##",
|
|
lstrip_blocks=True,
|
|
trim_blocks=True,
|
|
)
|
|
tmpl = env.from_string(""" <% if True %>hello <% endif %>""")
|
|
assert tmpl.render() == "hello "
|
|
|
|
def test_lstrip_angle_bracket_comment(self, env):
|
|
env = Environment(
|
|
"<%",
|
|
"%>",
|
|
"${",
|
|
"}",
|
|
"<%#",
|
|
"%>",
|
|
"%",
|
|
"##",
|
|
lstrip_blocks=True,
|
|
trim_blocks=True,
|
|
)
|
|
tmpl = env.from_string(""" <%# if True %>hello <%# endif %>""")
|
|
assert tmpl.render() == "hello "
|
|
|
|
def test_lstrip_angle_bracket(self, env):
|
|
env = Environment(
|
|
"<%",
|
|
"%>",
|
|
"${",
|
|
"}",
|
|
"<%#",
|
|
"%>",
|
|
"%",
|
|
"##",
|
|
lstrip_blocks=True,
|
|
trim_blocks=True,
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%# regular comment %>
|
|
<% for item in seq %>
|
|
${item} ## the rest of the stuff
|
|
<% endfor %>"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5))
|
|
|
|
def test_lstrip_angle_bracket_compact(self, env):
|
|
env = Environment(
|
|
"<%",
|
|
"%>",
|
|
"${",
|
|
"}",
|
|
"<%#",
|
|
"%>",
|
|
"%",
|
|
"##",
|
|
lstrip_blocks=True,
|
|
trim_blocks=True,
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%#regular comment%>
|
|
<%for item in seq%>
|
|
${item} ## the rest of the stuff
|
|
<%endfor%>"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5))
|
|
|
|
def test_lstrip_blocks_outside_with_new_line(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
" {% if kvs %}(\n"
|
|
" {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
|
|
" ){% endif %}"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == "(\na=1 b=2 \n )"
|
|
|
|
def test_lstrip_trim_blocks_outside_with_new_line(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=True)
|
|
tmpl = env.from_string(
|
|
" {% if kvs %}(\n"
|
|
" {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
|
|
" ){% endif %}"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == "(\na=1 b=2 )"
|
|
|
|
def test_lstrip_blocks_inside_with_new_line(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
" ({% if kvs %}\n"
|
|
" {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
|
|
" {% endif %})"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == " (\na=1 b=2 \n)"
|
|
|
|
def test_lstrip_trim_blocks_inside_with_new_line(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=True)
|
|
tmpl = env.from_string(
|
|
" ({% if kvs %}\n"
|
|
" {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
|
|
" {% endif %})"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == " (a=1 b=2 )"
|
|
|
|
def test_lstrip_blocks_without_new_line(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
" {% if kvs %}"
|
|
" {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}"
|
|
" {% endif %}"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == " a=1 b=2 "
|
|
|
|
def test_lstrip_trim_blocks_without_new_line(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=True)
|
|
tmpl = env.from_string(
|
|
" {% if kvs %}"
|
|
" {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}"
|
|
" {% endif %}"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == " a=1 b=2 "
|
|
|
|
def test_lstrip_blocks_consume_after_without_new_line(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
" {% if kvs -%}"
|
|
" {% for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}"
|
|
" {% endif -%}"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == "a=1 b=2 "
|
|
|
|
def test_lstrip_trim_blocks_consume_before_without_new_line(self):
|
|
env = Environment(lstrip_blocks=False, trim_blocks=False)
|
|
tmpl = env.from_string(
|
|
" {%- if kvs %}"
|
|
" {%- for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}"
|
|
" {%- endif %}"
|
|
)
|
|
out = tmpl.render(kvs=[("a", 1), ("b", 2)])
|
|
assert out == "a=1 b=2 "
|
|
|
|
def test_lstrip_trim_blocks_comment(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=True)
|
|
tmpl = env.from_string(" {# 1 space #}\n {# 2 spaces #} {# 4 spaces #}")
|
|
out = tmpl.render()
|
|
assert out == " " * 4
|
|
|
|
def test_lstrip_trim_blocks_raw(self):
|
|
env = Environment(lstrip_blocks=True, trim_blocks=True)
|
|
tmpl = env.from_string("{{x}}\n{%- raw %} {% endraw -%}\n{{ y }}")
|
|
out = tmpl.render(x=1, y=2)
|
|
assert out == "1 2"
|
|
|
|
def test_php_syntax_with_manual(self, env):
|
|
env = Environment(
|
|
"<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<!-- I'm a comment, I'm not interesting -->
|
|
<? for item in seq -?>
|
|
<?= item ?>
|
|
<?- endfor ?>"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "01234"
|
|
|
|
def test_php_syntax(self, env):
|
|
env = Environment(
|
|
"<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<!-- I'm a comment, I'm not interesting -->
|
|
<? for item in seq ?>
|
|
<?= item ?>
|
|
<? endfor ?>"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5))
|
|
|
|
def test_php_syntax_compact(self, env):
|
|
env = Environment(
|
|
"<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<!-- I'm a comment, I'm not interesting -->
|
|
<?for item in seq?>
|
|
<?=item?>
|
|
<?endfor?>"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5))
|
|
|
|
def test_erb_syntax(self, env):
|
|
env = Environment(
|
|
"<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%# I'm a comment, I'm not interesting %>
|
|
<% for item in seq %>
|
|
<%= item %>
|
|
<% endfor %>
|
|
"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5))
|
|
|
|
def test_erb_syntax_with_manual(self, env):
|
|
env = Environment(
|
|
"<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%# I'm a comment, I'm not interesting %>
|
|
<% for item in seq -%>
|
|
<%= item %>
|
|
<%- endfor %>"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "01234"
|
|
|
|
def test_erb_syntax_no_lstrip(self, env):
|
|
env = Environment(
|
|
"<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<%# I'm a comment, I'm not interesting %>
|
|
<%+ for item in seq -%>
|
|
<%= item %>
|
|
<%- endfor %>"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == " 01234"
|
|
|
|
def test_comment_syntax(self, env):
|
|
env = Environment(
|
|
"<!--",
|
|
"-->",
|
|
"${",
|
|
"}",
|
|
"<!--#",
|
|
"-->",
|
|
lstrip_blocks=True,
|
|
trim_blocks=True,
|
|
)
|
|
tmpl = env.from_string(
|
|
"""\
|
|
<!--# I'm a comment, I'm not interesting -->\
|
|
<!-- for item in seq --->
|
|
${item}
|
|
<!--- endfor -->"""
|
|
)
|
|
assert tmpl.render(seq=range(5)) == "01234"
|
|
|
|
|
|
class TestTrimBlocks:
|
|
def test_trim(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=False)
|
|
tmpl = env.from_string(" {% if True %}\n {% endif %}")
|
|
assert tmpl.render() == " "
|
|
|
|
def test_no_trim(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=False)
|
|
tmpl = env.from_string(" {% if True +%}\n {% endif %}")
|
|
assert tmpl.render() == " \n "
|
|
|
|
def test_no_trim_outer(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=False)
|
|
tmpl = env.from_string("{% if True %}X{% endif +%}\nmore things")
|
|
assert tmpl.render() == "X\nmore things"
|
|
|
|
def test_lstrip_no_trim(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string(" {% if True +%}\n {% endif %}")
|
|
assert tmpl.render() == "\n"
|
|
|
|
def test_trim_blocks_false_with_no_trim(self, env):
|
|
# Test that + is a NOP (but does not cause an error) if trim_blocks=False
|
|
env = Environment(trim_blocks=False, lstrip_blocks=False)
|
|
tmpl = env.from_string(" {% if True %}\n {% endif %}")
|
|
assert tmpl.render() == " \n "
|
|
tmpl = env.from_string(" {% if True +%}\n {% endif %}")
|
|
assert tmpl.render() == " \n "
|
|
|
|
tmpl = env.from_string(" {# comment #}\n ")
|
|
assert tmpl.render() == " \n "
|
|
tmpl = env.from_string(" {# comment +#}\n ")
|
|
assert tmpl.render() == " \n "
|
|
|
|
tmpl = env.from_string(" {% raw %}{% endraw %}\n ")
|
|
assert tmpl.render() == " \n "
|
|
tmpl = env.from_string(" {% raw %}{% endraw +%}\n ")
|
|
assert tmpl.render() == " \n "
|
|
|
|
def test_trim_nested(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string(
|
|
" {% if True %}\na {% if True %}\nb {% endif %}\nc {% endif %}"
|
|
)
|
|
assert tmpl.render() == "a b c "
|
|
|
|
def test_no_trim_nested(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string(
|
|
" {% if True +%}\na {% if True +%}\nb {% endif +%}\nc {% endif %}"
|
|
)
|
|
assert tmpl.render() == "\na \nb \nc "
|
|
|
|
def test_comment_trim(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string(""" {# comment #}\n\n """)
|
|
assert tmpl.render() == "\n "
|
|
|
|
def test_comment_no_trim(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string(""" {# comment +#}\n\n """)
|
|
assert tmpl.render() == "\n\n "
|
|
|
|
def test_multiple_comment_trim_lstrip(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string(
|
|
" {# comment #}\n\n{# comment2 #}\n \n{# comment3 #}\n\n "
|
|
)
|
|
assert tmpl.render() == "\n \n\n "
|
|
|
|
def test_multiple_comment_no_trim_lstrip(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string(
|
|
" {# comment +#}\n\n{# comment2 +#}\n \n{# comment3 +#}\n\n "
|
|
)
|
|
assert tmpl.render() == "\n\n\n \n\n\n "
|
|
|
|
def test_raw_trim_lstrip(self, env):
|
|
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw %}\n\n{{ y }}")
|
|
assert tmpl.render(x=1, y=2) == "1\n\n\n2"
|
|
|
|
def test_raw_no_trim_lstrip(self, env):
|
|
env = Environment(trim_blocks=False, lstrip_blocks=True)
|
|
tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw +%}\n\n{{ y }}")
|
|
assert tmpl.render(x=1, y=2) == "1\n\n\n\n2"
|
|
|
|
# raw blocks do not process inner text, so start tag cannot ignore trim
|
|
with pytest.raises(TemplateSyntaxError):
|
|
tmpl = env.from_string("{{x}}{% raw +%}\n\n {% endraw +%}\n\n{{ y }}")
|
|
|
|
def test_no_trim_angle_bracket(self, env):
|
|
env = Environment(
|
|
"<%", "%>", "${", "}", "<%#", "%>", lstrip_blocks=True, trim_blocks=True,
|
|
)
|
|
tmpl = env.from_string(" <% if True +%>\n\n <% endif %>")
|
|
assert tmpl.render() == "\n\n"
|
|
|
|
tmpl = env.from_string(" <%# comment +%>\n\n ")
|
|
assert tmpl.render() == "\n\n "
|
|
|
|
def test_no_trim_php_syntax(self, env):
|
|
env = Environment(
|
|
"<?",
|
|
"?>",
|
|
"<?=",
|
|
"?>",
|
|
"<!--",
|
|
"-->",
|
|
lstrip_blocks=False,
|
|
trim_blocks=True,
|
|
)
|
|
tmpl = env.from_string(" <? if True +?>\n\n <? endif ?>")
|
|
assert tmpl.render() == " \n\n "
|
|
tmpl = env.from_string(" <!-- comment +-->\n\n ")
|
|
assert tmpl.render() == " \n\n "
|