Coverage for tests / test_templite.py: 100.000%
143 statements
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
4"""Tests for coverage.templite."""
6from __future__ import annotations 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
8import re 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
10from types import SimpleNamespace 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
11from typing import Any, ContextManager 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
13import pytest 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
15from coverage.templite import Templite, TempliteSyntaxError, TempliteValueError 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
17from tests.coveragetest import CoverageTest 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
19# pylint: disable=possibly-unused-variable
22class TempliteTest(CoverageTest): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
23 """Tests for Templite."""
25 run_in_temp_dir = False 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
27 def try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%
28 self,
29 text: str,
30 ctx: dict[str, Any] | None = None,
31 result: str | None = None,
32 ) -> None:
33 """Render `text` through `ctx`, and it had better be `result`.
35 Result defaults to None so we can shorten the calls where we expect
36 an exception and never get to the result comparison.
38 """
39 actual = Templite(text).render(ctx or {}) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
40 # If result is None, then an exception should have prevented us getting
41 # to here.
42 assert result is not None 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
43 assert actual == result 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
45 def assertSynErr(self, msg: str) -> ContextManager[None]: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
46 """Assert that a `TempliteSyntaxError` will happen.
48 A context manager, and the message should be `msg`.
50 """
51 pat = "^" + re.escape(msg) + "$" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
52 return pytest.raises(TempliteSyntaxError, match=pat) # type: ignore 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
54 def test_passthrough(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
55 # Strings without variables are passed through unchanged.
56 assert Templite("Hello").render() == "Hello" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
57 assert Templite("Hello, 20% fun time!").render() == "Hello, 20% fun time!" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
59 def test_variables(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
60 # Variables use {{var}} syntax.
61 self.try_render("Hello, {{name}}!", {"name": "Ned"}, "Hello, Ned!") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
63 def test_undefined_variables(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
64 # Using undefined names is an error.
65 with pytest.raises(Exception, match="'name'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
66 self.try_render("Hi, {{name}}!") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
68 def test_pipes(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
69 # Variables can be filtered with pipes.
70 data = { 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
71 "name": "Ned",
72 "upper": lambda x: x.upper(),
73 "second": lambda x: x[1],
74 }
75 self.try_render("Hello, {{name|upper}}!", data, "Hello, NED!") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
77 # Pipes can be concatenated.
78 self.try_render("Hello, {{name|upper|second}}!", data, "Hello, E!") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
80 def test_reusability(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
81 # A single Templite can be used more than once with different data.
82 globs = { 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
83 "upper": lambda x: x.upper(),
84 "punct": "!",
85 }
87 template = Templite("This is {{name|upper}}{{punct}}", globs) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
88 assert template.render({"name": "Ned"}) == "This is NED!" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
89 assert template.render({"name": "Ben"}) == "This is BEN!" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
91 def test_attribute(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
92 # Variables' attributes can be accessed with dots.
93 obj = SimpleNamespace(a="Ay") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
94 self.try_render("{{obj.a}}", locals(), "Ay") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
96 obj2 = SimpleNamespace(obj=obj, b="Bee") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
97 self.try_render("{{obj2.obj.a}} {{obj2.b}}", locals(), "Ay Bee") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
99 def test_member_function(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
100 # Variables' member functions can be used, as long as they are nullary.
101 class WithMemberFns(SimpleNamespace): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
102 """A class to try out member function access."""
104 def ditto(self) -> str: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
105 """Return twice the .txt attribute."""
106 return self.txt + self.txt # type: ignore 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
108 obj = WithMemberFns(txt="Once") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
109 self.try_render("{{obj.ditto}}", locals(), "OnceOnce") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
111 def test_item_access(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
112 # Variables' items can be used.
113 d = {"a": 17, "b": 23} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
114 self.try_render("{{d.a}} < {{d.b}}", locals(), "17 < 23") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
116 def test_loops(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
117 # Loops work like in Django.
118 nums = [1, 2, 3, 4] 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
119 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
120 "Look: {% for n in nums %}{{n}}, {% endfor %}done.",
121 locals(),
122 "Look: 1, 2, 3, 4, done.",
123 )
125 # Loop iterables can be filtered.
126 def rev(l: list[int]) -> list[int]: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
127 """Return the reverse of `l`."""
128 l = l[:] 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
129 l.reverse() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
130 return l 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
132 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
133 "Look: {% for n in nums|rev %}{{n}}, {% endfor %}done.",
134 locals(),
135 "Look: 4, 3, 2, 1, done.",
136 )
138 def test_empty_loops(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
139 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
140 "Empty: {% for n in nums %}{{n}}, {% endfor %}done.",
141 {"nums": []},
142 "Empty: done.",
143 )
145 def test_multiline_loops(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
146 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
147 "Look: \n{% for n in nums %}\n{{n}}, \n{% endfor %}done.",
148 {"nums": [1, 2, 3]},
149 "Look: \n\n1, \n\n2, \n\n3, \ndone.",
150 )
152 def test_multiple_loops(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
153 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
154 "{% for n in nums %}{{n}}{% endfor %} and " + "{% for n in nums %}{{n}}{% endfor %}",
155 {"nums": [1, 2, 3]},
156 "123 and 123",
157 )
159 def test_comments(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
160 # Single-line comments work:
161 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
162 "Hello, {# Name goes here: #}{{name}}!",
163 {"name": "Ned"},
164 "Hello, Ned!",
165 )
166 # and so do multi-line comments:
167 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
168 "Hello, {# Name\ngoes\nhere: #}{{name}}!",
169 {"name": "Ned"},
170 "Hello, Ned!",
171 )
173 def test_if(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
174 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
175 "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!",
176 {"ned": 1, "ben": 0},
177 "Hi, NED!",
178 )
179 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
180 "Hi, {% if ned %}NED{% endif %}{% if ben %}BEN{% endif %}!",
181 {"ned": 0, "ben": 1},
182 "Hi, BEN!",
183 )
184 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
185 "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!",
186 {"ned": 0, "ben": 0},
187 "Hi, !",
188 )
189 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
190 "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!",
191 {"ned": 1, "ben": 0},
192 "Hi, NED!",
193 )
194 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
195 "Hi, {% if ned %}NED{% if ben %}BEN{% endif %}{% endif %}!",
196 {"ned": 1, "ben": 1},
197 "Hi, NEDBEN!",
198 )
200 def test_if_else(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
201 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
202 "Hi, {% if ned %}NED{% else %}NO NED{% endif %}!",
203 {"ned": 1},
204 "Hi, NED!",
205 )
206 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
207 "Hi, {% if ned %}NED{% else %}NO NED{% endif %}!",
208 {"ned": 0},
209 "Hi, NO NED!",
210 )
211 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
212 "Hi, {% if n %}NED{% if b %}BEN{% else %}NO BEN{% endif %}{% else %}NO NED{% endif %}!",
213 {"n": 1, "b": 1},
214 "Hi, NEDBEN!",
215 )
216 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
217 "Hi, {% if n %}NED{% if b %}BEN{% else %}NO BEN{% endif %}{% else %}NO NED{% endif %}!",
218 {"n": 1, "b": 0},
219 "Hi, NEDNO BEN!",
220 )
221 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
222 "Hi, {% if n %}NED{% if b %}BEN{% else %}NO BEN{% endif %}{% else %}NO NED{% endif %}!",
223 {"n": 0, "b": 1},
224 "Hi, NO NED!",
225 )
226 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
227 "Hi, {% if n %}NED{% if b %}BEN{% else %}NO BEN{% endif %}{% else %}NO NED{% endif %}!",
228 {"n": 0, "b": 0},
229 "Hi, NO NED!",
230 )
232 def test_complex_if(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
233 class Complex(SimpleNamespace): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
234 """A class to try out complex data access."""
236 def getit(self): # type: ignore 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
237 """Return it."""
238 return self.it 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
240 obj = Complex(it={"x": "Hello", "y": 0}) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
241 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
242 "@"
243 + "{% if obj.getit.x %}X{% endif %}"
244 + "{% if obj.getit.y %}Y{% endif %}"
245 + "{% if obj.getit.y|str %}S{% endif %}"
246 + "!",
247 {"obj": obj, "str": str},
248 "@XS!",
249 )
251 def test_loop_if(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
252 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
253 "@{% for n in nums %}{% if n %}Z{% endif %}{{n}}{% endfor %}!",
254 {"nums": [0, 1, 2]},
255 "@0Z1Z2!",
256 )
257 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
258 "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!",
259 {"nums": [0, 1, 2]},
260 "X@012!",
261 )
262 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
263 "X{%if nums%}@{% for n in nums %}{{n}}{% endfor %}{%endif%}!",
264 {"nums": []},
265 "X!",
266 )
268 def test_nested_loops(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
269 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
270 "@"
271 + "{% for n in nums %}"
272 + "{% for a in abc %}{{a}}{{n}}{% endfor %}"
273 + "{% endfor %}"
274 + "!",
275 {"nums": [0, 1, 2], "abc": ["a", "b", "c"]},
276 "@a0b0c0a1b1c1a2b2c2!",
277 )
279 def test_whitespace_handling(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
280 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
281 "@{% for n in nums %}\n"
282 + " {% for a in abc %}{{a}}{{n}}{% endfor %}\n"
283 + "{% endfor %}!\n",
284 {"nums": [0, 1, 2], "abc": ["a", "b", "c"]},
285 "@\n a0b0c0\n\n a1b1c1\n\n a2b2c2\n!\n",
286 )
287 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
288 "@{% for n in nums -%}\n"
289 + " {% for a in abc -%}\n"
290 + " {# this disappears completely -#}\n"
291 + " {{a-}}\n"
292 + " {{n -}}\n"
293 + " {{n -}}\n"
294 + " {% endfor %}\n"
295 + "{% endfor %}!\n",
296 {"nums": [0, 1, 2], "abc": ["a", "b", "c"]},
297 "@a00b00c00\na11b11c11\na22b22c22\n!\n",
298 )
299 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
300 "@{% for n in nums -%}\n" + " {{n -}}\n" + " x\n" + "{% endfor %}!\n",
301 {"nums": [0, 1, 2]},
302 "@0x\n1x\n2x\n!\n",
303 )
304 self.try_render(" hello ", {}, " hello ") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
306 def test_eat_whitespace(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
307 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
308 "Hey!\n"
309 + "{% joined %}\n"
310 + "@{% for n in nums %}\n"
311 + " {% for a in abc %}\n"
312 + " {# this disappears completely #}\n"
313 + " X\n"
314 + " Y\n"
315 + " {{a}}\n"
316 + " {{n }}\n"
317 + " {% endfor %}\n"
318 + "{% endfor %}!\n"
319 + "{% endjoined %}\n",
320 {"nums": [0, 1, 2], "abc": ["a", "b", "c"]},
321 "Hey!\n@XYa0XYb0XYc0XYa1XYb1XYc1XYa2XYb2XYc2!\n",
322 )
324 def test_non_ascii(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
325 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
326 "{{where}} ollǝɥ",
327 {"where": "ǝɹǝɥʇ"},
328 "ǝɹǝɥʇ ollǝɥ",
329 )
331 def test_exception_during_evaluation(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
332 # TypeError: Couldn't evaluate {{ foo.bar.baz }}:
333 regex = "^Couldn't evaluate None.bar$" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
334 with pytest.raises(TempliteValueError, match=regex): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
335 self.try_render( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
336 "Hey {{foo.bar.baz}} there",
337 {"foo": None},
338 "Hey ??? there",
339 )
341 def test_bad_names(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
342 with self.assertSynErr("Not a valid name: 'var%&!@'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
343 self.try_render("Wat: {{ var%&!@ }}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
344 with self.assertSynErr("Not a valid name: 'filter%&!@'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
345 self.try_render("Wat: {{ foo|filter%&!@ }}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
346 with self.assertSynErr("Not a valid name: '@'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
347 self.try_render("Wat: {% for @ in x %}{% endfor %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
349 def test_bogus_tag_syntax(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
350 with self.assertSynErr("Don't understand tag: 'bogus'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
351 self.try_render("Huh: {% bogus %}!!{% endbogus %}??") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
353 def test_malformed_if(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
354 with self.assertSynErr("Don't understand if: '{% if %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
355 self.try_render("Buh? {% if %}hi!{% endif %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
356 with self.assertSynErr("Don't understand if: '{% if this or that %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
357 self.try_render("Buh? {% if this or that %}hi!{% endif %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
359 def test_malformed_else(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
360 with self.assertSynErr("Don't understand else: '{% else now %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
361 self.try_render("Buh? {% if x %}hi!{% else now %}{% endif %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
362 with self.assertSynErr("Mismatched else: '{% else %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
363 self.try_render("Buh? {% else %}hi!{% endif %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
364 with self.assertSynErr("Mismatched else: '{% else %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
365 self.try_render("Buh? {% for x in xs %}{% else %}hi!{% endfor %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
367 def test_malformed_for(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
368 with self.assertSynErr("Don't understand for: '{% for %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
369 self.try_render("Weird: {% for %}loop{% endfor %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
370 with self.assertSynErr("Don't understand for: '{% for x from y %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
371 self.try_render("Weird: {% for x from y %}loop{% endfor %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
372 with self.assertSynErr("Don't understand for: '{% for x, y in z %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
373 self.try_render("Weird: {% for x, y in z %}loop{% endfor %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
375 def test_bad_nesting(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
376 with self.assertSynErr("Unmatched action tag: 'if'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
377 self.try_render("{% if x %}X") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
378 with self.assertSynErr("Mismatched end tag: 'for'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
379 self.try_render("{% if x %}X{% endfor %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
380 with self.assertSynErr("Too many ends: '{% endif %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
381 self.try_render("{% if x %}{% endif %}{% endif %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
383 def test_malformed_end(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
384 with self.assertSynErr("Don't understand end: '{% end if %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
385 self.try_render("{% if x %}X{% end if %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
386 with self.assertSynErr("Don't understand end: '{% endif now %}'"): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
387 self.try_render("{% if x %}X{% endif now %}") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()