1import pytest 2 3from jinja2.environment import Environment 4from jinja2.exceptions import TemplateNotFound 5from jinja2.exceptions import TemplatesNotFound 6from jinja2.exceptions import TemplateSyntaxError 7from jinja2.exceptions import UndefinedError 8from jinja2.loaders import DictLoader 9 10 11@pytest.fixture 12def test_env(): 13 env = Environment( 14 loader=DictLoader( 15 dict( 16 module="{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}", 17 header="[{{ foo }}|{{ 23 }}]", 18 o_printer="({{ o }})", 19 ) 20 ) 21 ) 22 env.globals["bar"] = 23 23 return env 24 25 26class TestImports: 27 def test_context_imports(self, test_env): 28 t = test_env.from_string('{% import "module" as m %}{{ m.test() }}') 29 assert t.render(foo=42) == "[|23]" 30 t = test_env.from_string( 31 '{% import "module" as m without context %}{{ m.test() }}' 32 ) 33 assert t.render(foo=42) == "[|23]" 34 t = test_env.from_string( 35 '{% import "module" as m with context %}{{ m.test() }}' 36 ) 37 assert t.render(foo=42) == "[42|23]" 38 t = test_env.from_string('{% from "module" import test %}{{ test() }}') 39 assert t.render(foo=42) == "[|23]" 40 t = test_env.from_string( 41 '{% from "module" import test without context %}{{ test() }}' 42 ) 43 assert t.render(foo=42) == "[|23]" 44 t = test_env.from_string( 45 '{% from "module" import test with context %}{{ test() }}' 46 ) 47 assert t.render(foo=42) == "[42|23]" 48 49 def test_import_needs_name(self, test_env): 50 test_env.from_string('{% from "foo" import bar %}') 51 test_env.from_string('{% from "foo" import bar, baz %}') 52 53 with pytest.raises(TemplateSyntaxError): 54 test_env.from_string('{% from "foo" import %}') 55 56 def test_no_trailing_comma(self, test_env): 57 with pytest.raises(TemplateSyntaxError): 58 test_env.from_string('{% from "foo" import bar, %}') 59 60 with pytest.raises(TemplateSyntaxError): 61 test_env.from_string('{% from "foo" import bar,, %}') 62 63 with pytest.raises(TemplateSyntaxError): 64 test_env.from_string('{% from "foo" import, %}') 65 66 def test_trailing_comma_with_context(self, test_env): 67 test_env.from_string('{% from "foo" import bar, baz with context %}') 68 test_env.from_string('{% from "foo" import bar, baz, with context %}') 69 test_env.from_string('{% from "foo" import bar, with context %}') 70 test_env.from_string('{% from "foo" import bar, with, context %}') 71 test_env.from_string('{% from "foo" import bar, with with context %}') 72 73 with pytest.raises(TemplateSyntaxError): 74 test_env.from_string('{% from "foo" import bar,, with context %}') 75 76 with pytest.raises(TemplateSyntaxError): 77 test_env.from_string('{% from "foo" import bar with context, %}') 78 79 def test_exports(self, test_env): 80 m = test_env.from_string( 81 """ 82 {% macro toplevel() %}...{% endmacro %} 83 {% macro __private() %}...{% endmacro %} 84 {% set variable = 42 %} 85 {% for item in [1] %} 86 {% macro notthere() %}{% endmacro %} 87 {% endfor %} 88 """ 89 ).module 90 assert m.toplevel() == "..." 91 assert not hasattr(m, "__missing") 92 assert m.variable == 42 93 assert not hasattr(m, "notthere") 94 95 def test_not_exported(self, test_env): 96 t = test_env.from_string("{% from 'module' import nothing %}{{ nothing() }}") 97 98 with pytest.raises(UndefinedError, match="does not export the requested name"): 99 t.render() 100 101 def test_import_with_globals(self, test_env): 102 env = Environment( 103 loader=DictLoader( 104 { 105 "macros": "{% macro test() %}foo: {{ foo }}{% endmacro %}", 106 "test": "{% import 'macros' as m %}{{ m.test() }}", 107 "test1": "{% import 'macros' as m %}{{ m.test() }}", 108 } 109 ) 110 ) 111 tmpl = env.get_template("test", globals={"foo": "bar"}) 112 assert tmpl.render() == "foo: bar" 113 114 tmpl = env.get_template("test1") 115 assert tmpl.render() == "foo: " 116 117 def test_import_with_globals_override(self, test_env): 118 env = Environment( 119 loader=DictLoader( 120 { 121 "macros": "{% set foo = '42' %}{% macro test() %}" 122 "foo: {{ foo }}{% endmacro %}", 123 "test": "{% from 'macros' import test %}{{ test() }}", 124 } 125 ) 126 ) 127 tmpl = env.get_template("test", globals={"foo": "bar"}) 128 assert tmpl.render() == "foo: 42" 129 130 def test_from_import_with_globals(self, test_env): 131 env = Environment( 132 loader=DictLoader( 133 { 134 "macros": "{% macro testing() %}foo: {{ foo }}{% endmacro %}", 135 "test": "{% from 'macros' import testing %}{{ testing() }}", 136 } 137 ) 138 ) 139 tmpl = env.get_template("test", globals={"foo": "bar"}) 140 assert tmpl.render() == "foo: bar" 141 142 143class TestIncludes: 144 def test_context_include(self, test_env): 145 t = test_env.from_string('{% include "header" %}') 146 assert t.render(foo=42) == "[42|23]" 147 t = test_env.from_string('{% include "header" with context %}') 148 assert t.render(foo=42) == "[42|23]" 149 t = test_env.from_string('{% include "header" without context %}') 150 assert t.render(foo=42) == "[|23]" 151 152 def test_choice_includes(self, test_env): 153 t = test_env.from_string('{% include ["missing", "header"] %}') 154 assert t.render(foo=42) == "[42|23]" 155 156 t = test_env.from_string('{% include ["missing", "missing2"] ignore missing %}') 157 assert t.render(foo=42) == "" 158 159 t = test_env.from_string('{% include ["missing", "missing2"] %}') 160 pytest.raises(TemplateNotFound, t.render) 161 with pytest.raises(TemplatesNotFound) as e: 162 t.render() 163 164 assert e.value.templates == ["missing", "missing2"] 165 assert e.value.name == "missing2" 166 167 def test_includes(t, **ctx): 168 ctx["foo"] = 42 169 assert t.render(ctx) == "[42|23]" 170 171 t = test_env.from_string('{% include ["missing", "header"] %}') 172 test_includes(t) 173 t = test_env.from_string("{% include x %}") 174 test_includes(t, x=["missing", "header"]) 175 t = test_env.from_string('{% include [x, "header"] %}') 176 test_includes(t, x="missing") 177 t = test_env.from_string("{% include x %}") 178 test_includes(t, x="header") 179 t = test_env.from_string("{% include [x] %}") 180 test_includes(t, x="header") 181 182 def test_include_ignoring_missing(self, test_env): 183 t = test_env.from_string('{% include "missing" %}') 184 pytest.raises(TemplateNotFound, t.render) 185 for extra in "", "with context", "without context": 186 t = test_env.from_string( 187 '{% include "missing" ignore missing ' + extra + " %}" 188 ) 189 assert t.render() == "" 190 191 def test_context_include_with_overrides(self, test_env): 192 env = Environment( 193 loader=DictLoader( 194 dict( 195 main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}", 196 item="{{ item }}", 197 ) 198 ) 199 ) 200 assert env.get_template("main").render() == "123" 201 202 def test_unoptimized_scopes(self, test_env): 203 t = test_env.from_string( 204 """ 205 {% macro outer(o) %} 206 {% macro inner() %} 207 {% include "o_printer" %} 208 {% endmacro %} 209 {{ inner() }} 210 {% endmacro %} 211 {{ outer("FOO") }} 212 """ 213 ) 214 assert t.render().strip() == "(FOO)" 215 216 def test_import_from_with_context(self): 217 env = Environment( 218 loader=DictLoader({"a": "{% macro x() %}{{ foobar }}{% endmacro %}"}) 219 ) 220 t = env.from_string( 221 "{% set foobar = 42 %}{% from 'a' import x with context %}{{ x() }}" 222 ) 223 assert t.render() == "42" 224