jinja

Форк
0
/
test_lexnparse.py 
1030 строк · 34.6 Кб
1
import pytest
2

3
from jinja2 import Environment
4
from jinja2 import nodes
5
from jinja2 import Template
6
from jinja2 import TemplateSyntaxError
7
from jinja2 import UndefinedError
8
from jinja2.lexer import Token
9
from jinja2.lexer import TOKEN_BLOCK_BEGIN
10
from jinja2.lexer import TOKEN_BLOCK_END
11
from jinja2.lexer import TOKEN_EOF
12
from jinja2.lexer import TokenStream
13

14

15
class TestTokenStream:
16
    test_tokens = [
17
        Token(1, TOKEN_BLOCK_BEGIN, ""),
18
        Token(2, TOKEN_BLOCK_END, ""),
19
    ]
20

21
    def test_simple(self, env):
22
        ts = TokenStream(self.test_tokens, "foo", "bar")
23
        assert ts.current.type is TOKEN_BLOCK_BEGIN
24
        assert bool(ts)
25
        assert not bool(ts.eos)
26
        next(ts)
27
        assert ts.current.type is TOKEN_BLOCK_END
28
        assert bool(ts)
29
        assert not bool(ts.eos)
30
        next(ts)
31
        assert ts.current.type is TOKEN_EOF
32
        assert not bool(ts)
33
        assert bool(ts.eos)
34

35
    def test_iter(self, env):
36
        token_types = [t.type for t in TokenStream(self.test_tokens, "foo", "bar")]
37
        assert token_types == [
38
            "block_begin",
39
            "block_end",
40
        ]
41

42

43
class TestLexer:
44
    def test_raw1(self, env):
45
        tmpl = env.from_string(
46
            "{% raw %}foo{% endraw %}|"
47
            "{%raw%}{{ bar }}|{% baz %}{%       endraw    %}"
48
        )
49
        assert tmpl.render() == "foo|{{ bar }}|{% baz %}"
50

51
    def test_raw2(self, env):
52
        tmpl = env.from_string("1  {%- raw -%}   2   {%- endraw -%}   3")
53
        assert tmpl.render() == "123"
54

55
    def test_raw3(self, env):
56
        # The second newline after baz exists because it is AFTER the
57
        # {% raw %} and is ignored.
58
        env = Environment(lstrip_blocks=True, trim_blocks=True)
59
        tmpl = env.from_string("bar\n{% raw %}\n  {{baz}}2 spaces\n{% endraw %}\nfoo")
60
        assert tmpl.render(baz="test") == "bar\n\n  {{baz}}2 spaces\nfoo"
61

62
    def test_raw4(self, env):
63
        # The trailing dash of the {% raw -%} cleans both the spaces and
64
        # newlines up to the first character of data.
65
        env = Environment(lstrip_blocks=True, trim_blocks=False)
66
        tmpl = env.from_string(
67
            "bar\n{%- raw -%}\n\n  \n  2 spaces\n space{%- endraw -%}\nfoo"
68
        )
69
        assert tmpl.render() == "bar2 spaces\n spacefoo"
70

71
    def test_balancing(self, env):
72
        env = Environment("{%", "%}", "${", "}")
73
        tmpl = env.from_string(
74
            """{% for item in seq
75
            %}${{'foo': item}|upper}{% endfor %}"""
76
        )
77
        assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
78

79
    def test_comments(self, env):
80
        env = Environment("<!--", "-->", "{", "}")
81
        tmpl = env.from_string(
82
            """\
83
<ul>
84
<!--- for item in seq -->
85
  <li>{item}</li>
86
<!--- endfor -->
87
</ul>"""
88
        )
89
        assert tmpl.render(seq=list(range(3))) == (
90
            "<ul>\n  <li>0</li>\n  <li>1</li>\n  <li>2</li>\n</ul>"
91
        )
92

93
    def test_string_escapes(self, env):
94
        for char in "\0", "\u2668", "\xe4", "\t", "\r", "\n":
95
            tmpl = env.from_string(f"{{{{ {char!r} }}}}")
96
            assert tmpl.render() == char
97
        assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == "\u2668"
98

99
    def test_bytefallback(self, env):
100
        from pprint import pformat
101

102
        tmpl = env.from_string("""{{ 'foo'|pprint }}|{{ 'bär'|pprint }}""")
103
        assert tmpl.render() == pformat("foo") + "|" + pformat("bär")
104

105
    def test_operators(self, env):
106
        from jinja2.lexer import operators
107

108
        for test, expect in operators.items():
109
            if test in "([{}])":
110
                continue
111
            stream = env.lexer.tokenize(f"{{{{ {test} }}}}")
112
            next(stream)
113
            assert stream.current.type == expect
114

115
    def test_normalizing(self, env):
116
        for seq in "\r", "\r\n", "\n":
117
            env = Environment(newline_sequence=seq)
118
            tmpl = env.from_string("1\n2\r\n3\n4\n")
119
            result = tmpl.render()
120
            assert result.replace(seq, "X") == "1X2X3X4"
121

122
    def test_trailing_newline(self, env):
123
        for keep in [True, False]:
124
            env = Environment(keep_trailing_newline=keep)
125
            for template, expected in [
126
                ("", {}),
127
                ("no\nnewline", {}),
128
                ("with\nnewline\n", {False: "with\nnewline"}),
129
                ("with\nseveral\n\n\n", {False: "with\nseveral\n\n"}),
130
            ]:
131
                tmpl = env.from_string(template)
132
                expect = expected.get(keep, template)
133
                result = tmpl.render()
134
                assert result == expect, (keep, template, result, expect)
135

136
    @pytest.mark.parametrize(
137
        ("name", "valid"),
138
        [
139
            ("foo", True),
140
            ("föö", True),
141
            ("き", True),
142
            ("_", True),
143
            ("1a", False),  # invalid ascii start
144
            ("a-", False),  # invalid ascii continue
145
            ("\U0001f40da", False),  # invalid unicode start
146
            ("a🐍\U0001f40d", False),  # invalid unicode continue
147
            # start characters not matched by \w
148
            ("\u1885", True),
149
            ("\u1886", True),
150
            ("\u2118", True),
151
            ("\u212e", True),
152
            # continue character not matched by \w
153
            ("\xb7", False),
154
            ("a\xb7", True),
155
        ],
156
    )
157
    def test_name(self, env, name, valid):
158
        t = "{{ " + name + " }}"
159

160
        if valid:
161
            # valid for version being tested, shouldn't raise
162
            env.from_string(t)
163
        else:
164
            pytest.raises(TemplateSyntaxError, env.from_string, t)
165

166
    def test_lineno_with_strip(self, env):
167
        tokens = env.lex(
168
            """\
169
<html>
170
    <body>
171
    {%- block content -%}
172
        <hr>
173
        {{ item }}
174
    {% endblock %}
175
    </body>
176
</html>"""
177
        )
178
        for tok in tokens:
179
            lineno, token_type, value = tok
180
            if token_type == "name" and value == "item":
181
                assert lineno == 5
182
                break
183

184

185
class TestParser:
186
    def test_php_syntax(self, env):
187
        env = Environment("<?", "?>", "<?=", "?>", "<!--", "-->")
188
        tmpl = env.from_string(
189
            """\
190
<!-- I'm a comment, I'm not interesting -->\
191
<? for item in seq -?>
192
    <?= item ?>
193
<?- endfor ?>"""
194
        )
195
        assert tmpl.render(seq=list(range(5))) == "01234"
196

197
    def test_erb_syntax(self, env):
198
        env = Environment("<%", "%>", "<%=", "%>", "<%#", "%>")
199
        tmpl = env.from_string(
200
            """\
201
<%# I'm a comment, I'm not interesting %>\
202
<% for item in seq -%>
203
    <%= item %>
204
<%- endfor %>"""
205
        )
206
        assert tmpl.render(seq=list(range(5))) == "01234"
207

208
    def test_comment_syntax(self, env):
209
        env = Environment("<!--", "-->", "${", "}", "<!--#", "-->")
210
        tmpl = env.from_string(
211
            """\
212
<!--# I'm a comment, I'm not interesting -->\
213
<!-- for item in seq --->
214
    ${item}
215
<!--- endfor -->"""
216
        )
217
        assert tmpl.render(seq=list(range(5))) == "01234"
218

219
    def test_balancing(self, env):
220
        tmpl = env.from_string("""{{{'foo':'bar'}.foo}}""")
221
        assert tmpl.render() == "bar"
222

223
    def test_start_comment(self, env):
224
        tmpl = env.from_string(
225
            """{# foo comment
226
and bar comment #}
227
{% macro blub() %}foo{% endmacro %}
228
{{ blub() }}"""
229
        )
230
        assert tmpl.render().strip() == "foo"
231

232
    def test_line_syntax(self, env):
233
        env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%")
234
        tmpl = env.from_string(
235
            """\
236
<%# regular comment %>
237
% for item in seq:
238
    ${item}
239
% endfor"""
240
        )
241
        assert [
242
            int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()
243
        ] == list(range(5))
244

245
        env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%", "##")
246
        tmpl = env.from_string(
247
            """\
248
<%# regular comment %>
249
% for item in seq:
250
    ${item} ## the rest of the stuff
251
% endfor"""
252
        )
253
        assert [
254
            int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()
255
        ] == list(range(5))
256

257
    def test_line_syntax_priority(self, env):
258
        # XXX: why is the whitespace there in front of the newline?
259
        env = Environment("{%", "%}", "${", "}", "/*", "*/", "##", "#")
260
        tmpl = env.from_string(
261
            """\
262
/* ignore me.
263
   I'm a multiline comment */
264
## for item in seq:
265
* ${item}          # this is just extra stuff
266
## endfor"""
267
        )
268
        assert tmpl.render(seq=[1, 2]).strip() == "* 1\n* 2"
269
        env = Environment("{%", "%}", "${", "}", "/*", "*/", "#", "##")
270
        tmpl = env.from_string(
271
            """\
272
/* ignore me.
273
   I'm a multiline comment */
274
# for item in seq:
275
* ${item}          ## this is just extra stuff
276
    ## extra stuff i just want to ignore
277
# endfor"""
278
        )
279
        assert tmpl.render(seq=[1, 2]).strip() == "* 1\n\n* 2"
280

281
    def test_error_messages(self, env):
282
        def assert_error(code, expected):
283
            with pytest.raises(TemplateSyntaxError, match=expected):
284
                Template(code)
285

286
        assert_error(
287
            "{% for item in seq %}...{% endif %}",
288
            "Encountered unknown tag 'endif'. Jinja was looking "
289
            "for the following tags: 'endfor' or 'else'. The "
290
            "innermost block that needs to be closed is 'for'.",
291
        )
292
        assert_error(
293
            "{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}",
294
            "Encountered unknown tag 'endfor'. Jinja was looking for "
295
            "the following tags: 'elif' or 'else' or 'endif'. The "
296
            "innermost block that needs to be closed is 'if'.",
297
        )
298
        assert_error(
299
            "{% if foo %}",
300
            "Unexpected end of template. Jinja was looking for the "
301
            "following tags: 'elif' or 'else' or 'endif'. The "
302
            "innermost block that needs to be closed is 'if'.",
303
        )
304
        assert_error(
305
            "{% for item in seq %}",
306
            "Unexpected end of template. Jinja was looking for the "
307
            "following tags: 'endfor' or 'else'. The innermost block "
308
            "that needs to be closed is 'for'.",
309
        )
310
        assert_error(
311
            "{% block foo-bar-baz %}",
312
            "Block names in Jinja have to be valid Python identifiers "
313
            "and may not contain hyphens, use an underscore instead.",
314
        )
315
        assert_error("{% unknown_tag %}", "Encountered unknown tag 'unknown_tag'.")
316

317

318
class TestSyntax:
319
    def test_call(self, env):
320
        env = Environment()
321
        env.globals["foo"] = lambda a, b, c, e, g: a + b + c + e + g
322
        tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}")
323
        assert tmpl.render() == "abdfh"
324

325
    def test_slicing(self, env):
326
        tmpl = env.from_string("{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}")
327
        assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]"
328

329
    def test_attr(self, env):
330
        tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}")
331
        assert tmpl.render(foo={"bar": 42}) == "42|42"
332

333
    def test_subscript(self, env):
334
        tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}")
335
        assert tmpl.render(foo=[0, 1, 2]) == "0|2"
336

337
    def test_tuple(self, env):
338
        tmpl = env.from_string("{{ () }}|{{ (1,) }}|{{ (1, 2) }}")
339
        assert tmpl.render() == "()|(1,)|(1, 2)"
340

341
    def test_math(self, env):
342
        tmpl = env.from_string("{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}")
343
        assert tmpl.render() == "1.5|8"
344

345
    def test_div(self, env):
346
        tmpl = env.from_string("{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}")
347
        assert tmpl.render() == "1|1.5|1"
348

349
    def test_unary(self, env):
350
        tmpl = env.from_string("{{ +3 }}|{{ -3 }}")
351
        assert tmpl.render() == "3|-3"
352

353
    def test_concat(self, env):
354
        tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}")
355
        assert tmpl.render() == "[1, 2]foo"
356

357
    @pytest.mark.parametrize(
358
        ("a", "op", "b"),
359
        [
360
            (1, ">", 0),
361
            (1, ">=", 1),
362
            (2, "<", 3),
363
            (3, "<=", 4),
364
            (4, "==", 4),
365
            (4, "!=", 5),
366
        ],
367
    )
368
    def test_compare(self, env, a, op, b):
369
        t = env.from_string(f"{{{{ {a} {op} {b} }}}}")
370
        assert t.render() == "True"
371

372
    def test_compare_parens(self, env):
373
        t = env.from_string("{{ i * (j < 5) }}")
374
        assert t.render(i=2, j=3) == "2"
375

376
    @pytest.mark.parametrize(
377
        ("src", "expect"),
378
        [
379
            ("{{ 4 < 2 < 3 }}", "False"),
380
            ("{{ a < b < c }}", "False"),
381
            ("{{ 4 > 2 > 3 }}", "False"),
382
            ("{{ a > b > c }}", "False"),
383
            ("{{ 4 > 2 < 3 }}", "True"),
384
            ("{{ a > b < c }}", "True"),
385
        ],
386
    )
387
    def test_compare_compound(self, env, src, expect):
388
        t = env.from_string(src)
389
        assert t.render(a=4, b=2, c=3) == expect
390

391
    def test_inop(self, env):
392
        tmpl = env.from_string("{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}")
393
        assert tmpl.render() == "True|False"
394

395
    @pytest.mark.parametrize("value", ("[]", "{}", "()"))
396
    def test_collection_literal(self, env, value):
397
        t = env.from_string(f"{{{{ {value} }}}}")
398
        assert t.render() == value
399

400
    @pytest.mark.parametrize(
401
        ("value", "expect"),
402
        (
403
            ("1", "1"),
404
            ("123", "123"),
405
            ("12_34_56", "123456"),
406
            ("1.2", "1.2"),
407
            ("34.56", "34.56"),
408
            ("3_4.5_6", "34.56"),
409
            ("1e0", "1.0"),
410
            ("10e1", "100.0"),
411
            ("2.5e100", "2.5e+100"),
412
            ("2.5e+100", "2.5e+100"),
413
            ("25.6e-10", "2.56e-09"),
414
            ("1_2.3_4e5_6", "1.234e+57"),
415
            ("0", "0"),
416
            ("0_00", "0"),
417
            ("0b1001_1111", "159"),
418
            ("0o123", "83"),
419
            ("0o1_23", "83"),
420
            ("0x123abc", "1194684"),
421
            ("0x12_3abc", "1194684"),
422
        ),
423
    )
424
    def test_numeric_literal(self, env, value, expect):
425
        t = env.from_string(f"{{{{ {value} }}}}")
426
        assert t.render() == expect
427

428
    def test_bool(self, env):
429
        tmpl = env.from_string(
430
            "{{ true and false }}|{{ false or true }}|{{ not false }}"
431
        )
432
        assert tmpl.render() == "False|True|True"
433

434
    def test_grouping(self, env):
435
        tmpl = env.from_string(
436
            "{{ (true and false) or (false and true) and not false }}"
437
        )
438
        assert tmpl.render() == "False"
439

440
    def test_django_attr(self, env):
441
        tmpl = env.from_string("{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}")
442
        assert tmpl.render() == "1|1"
443

444
    def test_conditional_expression(self, env):
445
        tmpl = env.from_string("""{{ 0 if true else 1 }}""")
446
        assert tmpl.render() == "0"
447

448
    def test_short_conditional_expression(self, env):
449
        tmpl = env.from_string("<{{ 1 if false }}>")
450
        assert tmpl.render() == "<>"
451

452
        tmpl = env.from_string("<{{ (1 if false).bar }}>")
453
        pytest.raises(UndefinedError, tmpl.render)
454

455
    def test_filter_priority(self, env):
456
        tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}')
457
        assert tmpl.render() == "FOOBAR"
458

459
    def test_function_calls(self, env):
460
        tests = [
461
            (True, "*foo, bar"),
462
            (True, "*foo, *bar"),
463
            (True, "**foo, *bar"),
464
            (True, "**foo, bar"),
465
            (True, "**foo, **bar"),
466
            (True, "**foo, bar=42"),
467
            (False, "foo, bar"),
468
            (False, "foo, bar=42"),
469
            (False, "foo, bar=23, *args"),
470
            (False, "foo, *args, bar=23"),
471
            (False, "a, b=c, *d, **e"),
472
            (False, "*foo, bar=42"),
473
            (False, "*foo, **bar"),
474
            (False, "*foo, bar=42, **baz"),
475
            (False, "foo, *args, bar=23, **baz"),
476
        ]
477
        for should_fail, sig in tests:
478
            if should_fail:
479
                with pytest.raises(TemplateSyntaxError):
480
                    env.from_string(f"{{{{ foo({sig}) }}}}")
481
            else:
482
                env.from_string(f"foo({sig})")
483

484
    def test_tuple_expr(self, env):
485
        for tmpl in [
486
            "{{ () }}",
487
            "{{ (1, 2) }}",
488
            "{{ (1, 2,) }}",
489
            "{{ 1, }}",
490
            "{{ 1, 2 }}",
491
            "{% for foo, bar in seq %}...{% endfor %}",
492
            "{% for x in foo, bar %}...{% endfor %}",
493
            "{% for x in foo, %}...{% endfor %}",
494
        ]:
495
            assert env.from_string(tmpl)
496

497
    def test_trailing_comma(self, env):
498
        tmpl = env.from_string("{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}")
499
        assert tmpl.render().lower() == "(1, 2)|[1, 2]|{1: 2}"
500

501
    def test_block_end_name(self, env):
502
        env.from_string("{% block foo %}...{% endblock foo %}")
503
        pytest.raises(
504
            TemplateSyntaxError, env.from_string, "{% block x %}{% endblock y %}"
505
        )
506

507
    def test_constant_casing(self, env):
508
        for const in True, False, None:
509
            const = str(const)
510
            tmpl = env.from_string(
511
                f"{{{{ {const} }}}}|{{{{ {const.lower()} }}}}|{{{{ {const.upper()} }}}}"
512
            )
513
            assert tmpl.render() == f"{const}|{const}|"
514

515
    def test_test_chaining(self, env):
516
        pytest.raises(
517
            TemplateSyntaxError, env.from_string, "{{ foo is string is sequence }}"
518
        )
519
        assert env.from_string("{{ 42 is string or 42 is number }}").render() == "True"
520

521
    def test_string_concatenation(self, env):
522
        tmpl = env.from_string('{{ "foo" "bar" "baz" }}')
523
        assert tmpl.render() == "foobarbaz"
524

525
    def test_notin(self, env):
526
        bar = range(100)
527
        tmpl = env.from_string("""{{ not 42 in bar }}""")
528
        assert tmpl.render(bar=bar) == "False"
529

530
    def test_operator_precedence(self, env):
531
        tmpl = env.from_string("""{{ 2 * 3 + 4 % 2 + 1 - 2 }}""")
532
        assert tmpl.render() == "5"
533

534
    def test_implicit_subscribed_tuple(self, env):
535
        class Foo:
536
            def __getitem__(self, x):
537
                return x
538

539
        t = env.from_string("{{ foo[1, 2] }}")
540
        assert t.render(foo=Foo()) == "(1, 2)"
541

542
    def test_raw2(self, env):
543
        tmpl = env.from_string("{% raw %}{{ FOO }} and {% BAR %}{% endraw %}")
544
        assert tmpl.render() == "{{ FOO }} and {% BAR %}"
545

546
    def test_const(self, env):
547
        tmpl = env.from_string(
548
            "{{ true }}|{{ false }}|{{ none }}|"
549
            "{{ none is defined }}|{{ missing is defined }}"
550
        )
551
        assert tmpl.render() == "True|False|None|True|False"
552

553
    def test_neg_filter_priority(self, env):
554
        node = env.parse("{{ -1|foo }}")
555
        assert isinstance(node.body[0].nodes[0], nodes.Filter)
556
        assert isinstance(node.body[0].nodes[0].node, nodes.Neg)
557

558
    def test_const_assign(self, env):
559
        constass1 = """{% set true = 42 %}"""
560
        constass2 = """{% for none in seq %}{% endfor %}"""
561
        for tmpl in constass1, constass2:
562
            pytest.raises(TemplateSyntaxError, env.from_string, tmpl)
563

564
    def test_localset(self, env):
565
        tmpl = env.from_string(
566
            """{% set foo = 0 %}\
567
{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\
568
{{ foo }}"""
569
        )
570
        assert tmpl.render() == "0"
571

572
    def test_parse_unary(self, env):
573
        tmpl = env.from_string('{{ -foo["bar"] }}')
574
        assert tmpl.render(foo={"bar": 42}) == "-42"
575
        tmpl = env.from_string('{{ -foo["bar"]|abs }}')
576
        assert tmpl.render(foo={"bar": 42}) == "42"
577

578

579
class TestLstripBlocks:
580
    def test_lstrip(self, env):
581
        env = Environment(lstrip_blocks=True, trim_blocks=False)
582
        tmpl = env.from_string("""    {% if True %}\n    {% endif %}""")
583
        assert tmpl.render() == "\n"
584

585
    def test_lstrip_trim(self, env):
586
        env = Environment(lstrip_blocks=True, trim_blocks=True)
587
        tmpl = env.from_string("""    {% if True %}\n    {% endif %}""")
588
        assert tmpl.render() == ""
589

590
    def test_no_lstrip(self, env):
591
        env = Environment(lstrip_blocks=True, trim_blocks=False)
592
        tmpl = env.from_string("""    {%+ if True %}\n    {%+ endif %}""")
593
        assert tmpl.render() == "    \n    "
594

595
    def test_lstrip_blocks_false_with_no_lstrip(self, env):
596
        # Test that + is a NOP (but does not cause an error) if lstrip_blocks=False
597
        env = Environment(lstrip_blocks=False, trim_blocks=False)
598
        tmpl = env.from_string("""    {% if True %}\n    {% endif %}""")
599
        assert tmpl.render() == "    \n    "
600
        tmpl = env.from_string("""    {%+ if True %}\n    {%+ endif %}""")
601
        assert tmpl.render() == "    \n    "
602

603
    def test_lstrip_endline(self, env):
604
        env = Environment(lstrip_blocks=True, trim_blocks=False)
605
        tmpl = env.from_string("""    hello{% if True %}\n    goodbye{% endif %}""")
606
        assert tmpl.render() == "    hello\n    goodbye"
607

608
    def test_lstrip_inline(self, env):
609
        env = Environment(lstrip_blocks=True, trim_blocks=False)
610
        tmpl = env.from_string("""    {% if True %}hello    {% endif %}""")
611
        assert tmpl.render() == "hello    "
612

613
    def test_lstrip_nested(self, env):
614
        env = Environment(lstrip_blocks=True, trim_blocks=False)
615
        tmpl = env.from_string(
616
            """    {% if True %}a {% if True %}b {% endif %}c {% endif %}"""
617
        )
618
        assert tmpl.render() == "a b c "
619

620
    def test_lstrip_left_chars(self, env):
621
        env = Environment(lstrip_blocks=True, trim_blocks=False)
622
        tmpl = env.from_string(
623
            """    abc {% if True %}
624
        hello{% endif %}"""
625
        )
626
        assert tmpl.render() == "    abc \n        hello"
627

628
    def test_lstrip_embeded_strings(self, env):
629
        env = Environment(lstrip_blocks=True, trim_blocks=False)
630
        tmpl = env.from_string("""    {% set x = " {% str %} " %}{{ x }}""")
631
        assert tmpl.render() == " {% str %} "
632

633
    def test_lstrip_preserve_leading_newlines(self, env):
634
        env = Environment(lstrip_blocks=True, trim_blocks=False)
635
        tmpl = env.from_string("""\n\n\n{% set hello = 1 %}""")
636
        assert tmpl.render() == "\n\n\n"
637

638
    def test_lstrip_comment(self, env):
639
        env = Environment(lstrip_blocks=True, trim_blocks=False)
640
        tmpl = env.from_string(
641
            """    {# if True #}
642
hello
643
    {#endif#}"""
644
        )
645
        assert tmpl.render() == "\nhello\n"
646

647
    def test_lstrip_angle_bracket_simple(self, env):
648
        env = Environment(
649
            "<%",
650
            "%>",
651
            "${",
652
            "}",
653
            "<%#",
654
            "%>",
655
            "%",
656
            "##",
657
            lstrip_blocks=True,
658
            trim_blocks=True,
659
        )
660
        tmpl = env.from_string("""    <% if True %>hello    <% endif %>""")
661
        assert tmpl.render() == "hello    "
662

663
    def test_lstrip_angle_bracket_comment(self, env):
664
        env = Environment(
665
            "<%",
666
            "%>",
667
            "${",
668
            "}",
669
            "<%#",
670
            "%>",
671
            "%",
672
            "##",
673
            lstrip_blocks=True,
674
            trim_blocks=True,
675
        )
676
        tmpl = env.from_string("""    <%# if True %>hello    <%# endif %>""")
677
        assert tmpl.render() == "hello    "
678

679
    def test_lstrip_angle_bracket(self, env):
680
        env = Environment(
681
            "<%",
682
            "%>",
683
            "${",
684
            "}",
685
            "<%#",
686
            "%>",
687
            "%",
688
            "##",
689
            lstrip_blocks=True,
690
            trim_blocks=True,
691
        )
692
        tmpl = env.from_string(
693
            """\
694
    <%# regular comment %>
695
    <% for item in seq %>
696
${item} ## the rest of the stuff
697
   <% endfor %>"""
698
        )
699
        assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5))
700

701
    def test_lstrip_angle_bracket_compact(self, env):
702
        env = Environment(
703
            "<%",
704
            "%>",
705
            "${",
706
            "}",
707
            "<%#",
708
            "%>",
709
            "%",
710
            "##",
711
            lstrip_blocks=True,
712
            trim_blocks=True,
713
        )
714
        tmpl = env.from_string(
715
            """\
716
    <%#regular comment%>
717
    <%for item in seq%>
718
${item} ## the rest of the stuff
719
   <%endfor%>"""
720
        )
721
        assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5))
722

723
    def test_lstrip_blocks_outside_with_new_line(self):
724
        env = Environment(lstrip_blocks=True, trim_blocks=False)
725
        tmpl = env.from_string(
726
            "  {% if kvs %}(\n"
727
            "   {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
728
            "  ){% endif %}"
729
        )
730
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
731
        assert out == "(\na=1 b=2 \n  )"
732

733
    def test_lstrip_trim_blocks_outside_with_new_line(self):
734
        env = Environment(lstrip_blocks=True, trim_blocks=True)
735
        tmpl = env.from_string(
736
            "  {% if kvs %}(\n"
737
            "   {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
738
            "  ){% endif %}"
739
        )
740
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
741
        assert out == "(\na=1 b=2   )"
742

743
    def test_lstrip_blocks_inside_with_new_line(self):
744
        env = Environment(lstrip_blocks=True, trim_blocks=False)
745
        tmpl = env.from_string(
746
            "  ({% if kvs %}\n"
747
            "   {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
748
            "  {% endif %})"
749
        )
750
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
751
        assert out == "  (\na=1 b=2 \n)"
752

753
    def test_lstrip_trim_blocks_inside_with_new_line(self):
754
        env = Environment(lstrip_blocks=True, trim_blocks=True)
755
        tmpl = env.from_string(
756
            "  ({% if kvs %}\n"
757
            "   {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n"
758
            "  {% endif %})"
759
        )
760
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
761
        assert out == "  (a=1 b=2 )"
762

763
    def test_lstrip_blocks_without_new_line(self):
764
        env = Environment(lstrip_blocks=True, trim_blocks=False)
765
        tmpl = env.from_string(
766
            "  {% if kvs %}"
767
            "   {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}"
768
            "  {% endif %}"
769
        )
770
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
771
        assert out == "   a=1 b=2   "
772

773
    def test_lstrip_trim_blocks_without_new_line(self):
774
        env = Environment(lstrip_blocks=True, trim_blocks=True)
775
        tmpl = env.from_string(
776
            "  {% if kvs %}"
777
            "   {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}"
778
            "  {% endif %}"
779
        )
780
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
781
        assert out == "   a=1 b=2   "
782

783
    def test_lstrip_blocks_consume_after_without_new_line(self):
784
        env = Environment(lstrip_blocks=True, trim_blocks=False)
785
        tmpl = env.from_string(
786
            "  {% if kvs -%}"
787
            "   {% for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}"
788
            "  {% endif -%}"
789
        )
790
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
791
        assert out == "a=1 b=2 "
792

793
    def test_lstrip_trim_blocks_consume_before_without_new_line(self):
794
        env = Environment(lstrip_blocks=False, trim_blocks=False)
795
        tmpl = env.from_string(
796
            "  {%- if kvs %}"
797
            "   {%- for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}"
798
            "  {%- endif %}"
799
        )
800
        out = tmpl.render(kvs=[("a", 1), ("b", 2)])
801
        assert out == "a=1 b=2 "
802

803
    def test_lstrip_trim_blocks_comment(self):
804
        env = Environment(lstrip_blocks=True, trim_blocks=True)
805
        tmpl = env.from_string(" {# 1 space #}\n  {# 2 spaces #}    {# 4 spaces #}")
806
        out = tmpl.render()
807
        assert out == " " * 4
808

809
    def test_lstrip_trim_blocks_raw(self):
810
        env = Environment(lstrip_blocks=True, trim_blocks=True)
811
        tmpl = env.from_string("{{x}}\n{%- raw %} {% endraw -%}\n{{ y }}")
812
        out = tmpl.render(x=1, y=2)
813
        assert out == "1 2"
814

815
    def test_php_syntax_with_manual(self, env):
816
        env = Environment(
817
            "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
818
        )
819
        tmpl = env.from_string(
820
            """\
821
    <!-- I'm a comment, I'm not interesting -->
822
    <? for item in seq -?>
823
        <?= item ?>
824
    <?- endfor ?>"""
825
        )
826
        assert tmpl.render(seq=range(5)) == "01234"
827

828
    def test_php_syntax(self, env):
829
        env = Environment(
830
            "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
831
        )
832
        tmpl = env.from_string(
833
            """\
834
    <!-- I'm a comment, I'm not interesting -->
835
    <? for item in seq ?>
836
        <?= item ?>
837
    <? endfor ?>"""
838
        )
839
        assert tmpl.render(seq=range(5)) == "".join(f"        {x}\n" for x in range(5))
840

841
    def test_php_syntax_compact(self, env):
842
        env = Environment(
843
            "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
844
        )
845
        tmpl = env.from_string(
846
            """\
847
    <!-- I'm a comment, I'm not interesting -->
848
    <?for item in seq?>
849
        <?=item?>
850
    <?endfor?>"""
851
        )
852
        assert tmpl.render(seq=range(5)) == "".join(f"        {x}\n" for x in range(5))
853

854
    def test_erb_syntax(self, env):
855
        env = Environment(
856
            "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
857
        )
858
        tmpl = env.from_string(
859
            """\
860
<%# I'm a comment, I'm not interesting %>
861
    <% for item in seq %>
862
    <%= item %>
863
    <% endfor %>
864
"""
865
        )
866
        assert tmpl.render(seq=range(5)) == "".join(f"    {x}\n" for x in range(5))
867

868
    def test_erb_syntax_with_manual(self, env):
869
        env = Environment(
870
            "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
871
        )
872
        tmpl = env.from_string(
873
            """\
874
<%# I'm a comment, I'm not interesting %>
875
    <% for item in seq -%>
876
        <%= item %>
877
    <%- endfor %>"""
878
        )
879
        assert tmpl.render(seq=range(5)) == "01234"
880

881
    def test_erb_syntax_no_lstrip(self, env):
882
        env = Environment(
883
            "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
884
        )
885
        tmpl = env.from_string(
886
            """\
887
<%# I'm a comment, I'm not interesting %>
888
    <%+ for item in seq -%>
889
        <%= item %>
890
    <%- endfor %>"""
891
        )
892
        assert tmpl.render(seq=range(5)) == "    01234"
893

894
    def test_comment_syntax(self, env):
895
        env = Environment(
896
            "<!--",
897
            "-->",
898
            "${",
899
            "}",
900
            "<!--#",
901
            "-->",
902
            lstrip_blocks=True,
903
            trim_blocks=True,
904
        )
905
        tmpl = env.from_string(
906
            """\
907
<!--# I'm a comment, I'm not interesting -->\
908
<!-- for item in seq --->
909
    ${item}
910
<!--- endfor -->"""
911
        )
912
        assert tmpl.render(seq=range(5)) == "01234"
913

914

915
class TestTrimBlocks:
916
    def test_trim(self, env):
917
        env = Environment(trim_blocks=True, lstrip_blocks=False)
918
        tmpl = env.from_string("    {% if True %}\n    {% endif %}")
919
        assert tmpl.render() == "        "
920

921
    def test_no_trim(self, env):
922
        env = Environment(trim_blocks=True, lstrip_blocks=False)
923
        tmpl = env.from_string("    {% if True +%}\n    {% endif %}")
924
        assert tmpl.render() == "    \n    "
925

926
    def test_no_trim_outer(self, env):
927
        env = Environment(trim_blocks=True, lstrip_blocks=False)
928
        tmpl = env.from_string("{% if True %}X{% endif +%}\nmore things")
929
        assert tmpl.render() == "X\nmore things"
930

931
    def test_lstrip_no_trim(self, env):
932
        env = Environment(trim_blocks=True, lstrip_blocks=True)
933
        tmpl = env.from_string("    {% if True +%}\n    {% endif %}")
934
        assert tmpl.render() == "\n"
935

936
    def test_trim_blocks_false_with_no_trim(self, env):
937
        # Test that + is a NOP (but does not cause an error) if trim_blocks=False
938
        env = Environment(trim_blocks=False, lstrip_blocks=False)
939
        tmpl = env.from_string("    {% if True %}\n    {% endif %}")
940
        assert tmpl.render() == "    \n    "
941
        tmpl = env.from_string("    {% if True +%}\n    {% endif %}")
942
        assert tmpl.render() == "    \n    "
943

944
        tmpl = env.from_string("    {# comment #}\n    ")
945
        assert tmpl.render() == "    \n    "
946
        tmpl = env.from_string("    {# comment +#}\n    ")
947
        assert tmpl.render() == "    \n    "
948

949
        tmpl = env.from_string("    {% raw %}{% endraw %}\n    ")
950
        assert tmpl.render() == "    \n    "
951
        tmpl = env.from_string("    {% raw %}{% endraw +%}\n    ")
952
        assert tmpl.render() == "    \n    "
953

954
    def test_trim_nested(self, env):
955
        env = Environment(trim_blocks=True, lstrip_blocks=True)
956
        tmpl = env.from_string(
957
            "    {% if True %}\na {% if True %}\nb {% endif %}\nc {% endif %}"
958
        )
959
        assert tmpl.render() == "a b c "
960

961
    def test_no_trim_nested(self, env):
962
        env = Environment(trim_blocks=True, lstrip_blocks=True)
963
        tmpl = env.from_string(
964
            "    {% if True +%}\na {% if True +%}\nb {% endif +%}\nc {% endif %}"
965
        )
966
        assert tmpl.render() == "\na \nb \nc "
967

968
    def test_comment_trim(self, env):
969
        env = Environment(trim_blocks=True, lstrip_blocks=True)
970
        tmpl = env.from_string("""    {# comment #}\n\n  """)
971
        assert tmpl.render() == "\n  "
972

973
    def test_comment_no_trim(self, env):
974
        env = Environment(trim_blocks=True, lstrip_blocks=True)
975
        tmpl = env.from_string("""    {# comment +#}\n\n  """)
976
        assert tmpl.render() == "\n\n  "
977

978
    def test_multiple_comment_trim_lstrip(self, env):
979
        env = Environment(trim_blocks=True, lstrip_blocks=True)
980
        tmpl = env.from_string(
981
            "   {# comment #}\n\n{# comment2 #}\n   \n{# comment3 #}\n\n "
982
        )
983
        assert tmpl.render() == "\n   \n\n "
984

985
    def test_multiple_comment_no_trim_lstrip(self, env):
986
        env = Environment(trim_blocks=True, lstrip_blocks=True)
987
        tmpl = env.from_string(
988
            "   {# comment +#}\n\n{# comment2 +#}\n   \n{# comment3 +#}\n\n "
989
        )
990
        assert tmpl.render() == "\n\n\n   \n\n\n "
991

992
    def test_raw_trim_lstrip(self, env):
993
        env = Environment(trim_blocks=True, lstrip_blocks=True)
994
        tmpl = env.from_string("{{x}}{% raw %}\n\n    {% endraw %}\n\n{{ y }}")
995
        assert tmpl.render(x=1, y=2) == "1\n\n\n2"
996

997
    def test_raw_no_trim_lstrip(self, env):
998
        env = Environment(trim_blocks=False, lstrip_blocks=True)
999
        tmpl = env.from_string("{{x}}{% raw %}\n\n      {% endraw +%}\n\n{{ y }}")
1000
        assert tmpl.render(x=1, y=2) == "1\n\n\n\n2"
1001

1002
        # raw blocks do not process inner text, so start tag cannot ignore trim
1003
        with pytest.raises(TemplateSyntaxError):
1004
            tmpl = env.from_string("{{x}}{% raw +%}\n\n  {% endraw +%}\n\n{{ y }}")
1005

1006
    def test_no_trim_angle_bracket(self, env):
1007
        env = Environment(
1008
            "<%", "%>", "${", "}", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
1009
        )
1010
        tmpl = env.from_string("    <% if True +%>\n\n    <% endif %>")
1011
        assert tmpl.render() == "\n\n"
1012

1013
        tmpl = env.from_string("    <%# comment +%>\n\n   ")
1014
        assert tmpl.render() == "\n\n   "
1015

1016
    def test_no_trim_php_syntax(self, env):
1017
        env = Environment(
1018
            "<?",
1019
            "?>",
1020
            "<?=",
1021
            "?>",
1022
            "<!--",
1023
            "-->",
1024
            lstrip_blocks=False,
1025
            trim_blocks=True,
1026
        )
1027
        tmpl = env.from_string("    <? if True +?>\n\n    <? endif ?>")
1028
        assert tmpl.render() == "    \n\n    "
1029
        tmpl = env.from_string("    <!-- comment +-->\n\n    ")
1030
        assert tmpl.render() == "    \n\n    "
1031

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.