1from collections import namedtuple 2 3import pytest 4 5from jinja2 import Environment 6from jinja2.utils import Markup 7 8 9async def make_aiter(iter): 10 for item in iter: 11 yield item 12 13 14def mark_dualiter(parameter, factory): 15 def decorator(f): 16 return pytest.mark.parametrize( 17 parameter, [lambda: factory(), lambda: make_aiter(factory())] 18 )(f) 19 20 return decorator 21 22 23@pytest.fixture 24def env_async(): 25 return Environment(enable_async=True) 26 27 28@mark_dualiter("foo", lambda: range(10)) 29def test_first(env_async, foo): 30 tmpl = env_async.from_string("{{ foo()|first }}") 31 out = tmpl.render(foo=foo) 32 assert out == "0" 33 34 35@mark_dualiter( 36 "items", 37 lambda: [ 38 {"foo": 1, "bar": 2}, 39 {"foo": 2, "bar": 3}, 40 {"foo": 1, "bar": 1}, 41 {"foo": 3, "bar": 4}, 42 ], 43) 44def test_groupby(env_async, items): 45 tmpl = env_async.from_string( 46 """ 47 {%- for grouper, list in items()|groupby('foo') -%} 48 {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}| 49 {%- endfor %}""" 50 ) 51 assert tmpl.render(items=items).split("|") == [ 52 "1: 1, 2: 1, 1", 53 "2: 2, 3", 54 "3: 3, 4", 55 "", 56 ] 57 58 59@mark_dualiter("items", lambda: [("a", 1), ("a", 2), ("b", 1)]) 60def test_groupby_tuple_index(env_async, items): 61 tmpl = env_async.from_string( 62 """ 63 {%- for grouper, list in items()|groupby(0) -%} 64 {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}| 65 {%- endfor %}""" 66 ) 67 assert tmpl.render(items=items) == "a:1:2|b:1|" 68 69 70def make_articles(): 71 Date = namedtuple("Date", "day,month,year") 72 Article = namedtuple("Article", "title,date") 73 return [ 74 Article("aha", Date(1, 1, 1970)), 75 Article("interesting", Date(2, 1, 1970)), 76 Article("really?", Date(3, 1, 1970)), 77 Article("totally not", Date(1, 1, 1971)), 78 ] 79 80 81@mark_dualiter("articles", make_articles) 82def test_groupby_multidot(env_async, articles): 83 tmpl = env_async.from_string( 84 """ 85 {%- for year, list in articles()|groupby('date.year') -%} 86 {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}| 87 {%- endfor %}""" 88 ) 89 assert tmpl.render(articles=articles).split("|") == [ 90 "1970[aha][interesting][really?]", 91 "1971[totally not]", 92 "", 93 ] 94 95 96@mark_dualiter("int_items", lambda: [1, 2, 3]) 97def test_join_env_int(env_async, int_items): 98 tmpl = env_async.from_string('{{ items()|join("|") }}') 99 out = tmpl.render(items=int_items) 100 assert out == "1|2|3" 101 102 103@mark_dualiter("string_items", lambda: ["<foo>", Markup("<span>foo</span>")]) 104def test_join_string_list(string_items): 105 env2 = Environment(autoescape=True, enable_async=True) 106 tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}') 107 assert tmpl.render(items=string_items) == "<foo><span>foo</span>" 108 109 110def make_users(): 111 User = namedtuple("User", "username") 112 return map(User, ["foo", "bar"]) 113 114 115@mark_dualiter("users", make_users) 116def test_join_attribute(env_async, users): 117 tmpl = env_async.from_string("""{{ users()|join(', ', 'username') }}""") 118 assert tmpl.render(users=users) == "foo, bar" 119 120 121@mark_dualiter("items", lambda: [1, 2, 3, 4, 5]) 122def test_simple_reject(env_async, items): 123 tmpl = env_async.from_string('{{ items()|reject("odd")|join("|") }}') 124 assert tmpl.render(items=items) == "2|4" 125 126 127@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5]) 128def test_bool_reject(env_async, items): 129 tmpl = env_async.from_string('{{ items()|reject|join("|") }}') 130 assert tmpl.render(items=items) == "None|False|0" 131 132 133@mark_dualiter("items", lambda: [1, 2, 3, 4, 5]) 134def test_simple_select(env_async, items): 135 tmpl = env_async.from_string('{{ items()|select("odd")|join("|") }}') 136 assert tmpl.render(items=items) == "1|3|5" 137 138 139@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5]) 140def test_bool_select(env_async, items): 141 tmpl = env_async.from_string('{{ items()|select|join("|") }}') 142 assert tmpl.render(items=items) == "1|2|3|4|5" 143 144 145def make_users(): 146 User = namedtuple("User", "name,is_active") 147 return [ 148 User("john", True), 149 User("jane", True), 150 User("mike", False), 151 ] 152 153 154@mark_dualiter("users", make_users) 155def test_simple_select_attr(env_async, users): 156 tmpl = env_async.from_string( 157 '{{ users()|selectattr("is_active")|map(attribute="name")|join("|") }}' 158 ) 159 assert tmpl.render(users=users) == "john|jane" 160 161 162@mark_dualiter("items", lambda: list("123")) 163def test_simple_map(env_async, items): 164 tmpl = env_async.from_string('{{ items()|map("int")|sum }}') 165 assert tmpl.render(items=items) == "6" 166 167 168def test_map_sum(env_async): # async map + async filter 169 tmpl = env_async.from_string('{{ [[1,2], [3], [4,5,6]]|map("sum")|list }}') 170 assert tmpl.render() == "[3, 3, 15]" 171 172 173@mark_dualiter("users", make_users) 174def test_attribute_map(env_async, users): 175 tmpl = env_async.from_string('{{ users()|map(attribute="name")|join("|") }}') 176 assert tmpl.render(users=users) == "john|jane|mike" 177 178 179def test_empty_map(env_async): 180 tmpl = env_async.from_string('{{ none|map("upper")|list }}') 181 assert tmpl.render() == "[]" 182 183 184@mark_dualiter("items", lambda: [1, 2, 3, 4, 5, 6]) 185def test_sum(env_async, items): 186 tmpl = env_async.from_string("""{{ items()|sum }}""") 187 assert tmpl.render(items=items) == "21" 188 189 190@mark_dualiter("items", lambda: [{"value": 23}, {"value": 1}, {"value": 18}]) 191def test_sum_attributes(env_async, items): 192 tmpl = env_async.from_string("""{{ items()|sum('value') }}""") 193 assert tmpl.render(items=items) 194 195 196def test_sum_attributes_nested(env_async): 197 tmpl = env_async.from_string("""{{ values|sum('real.value') }}""") 198 assert ( 199 tmpl.render( 200 values=[ 201 {"real": {"value": 23}}, 202 {"real": {"value": 1}}, 203 {"real": {"value": 18}}, 204 ] 205 ) 206 == "42" 207 ) 208 209 210def test_sum_attributes_tuple(env_async): 211 tmpl = env_async.from_string("""{{ values.items()|sum('1') }}""") 212 assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18}) == "42" 213 214 215@mark_dualiter("items", lambda: range(10)) 216def test_slice(env_async, items): 217 tmpl = env_async.from_string( 218 "{{ items()|slice(3)|list }}|{{ items()|slice(3, 'X')|list }}" 219 ) 220 out = tmpl.render(items=items) 221 assert out == ( 222 "[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|" 223 "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]" 224 ) 225