import pytest from jinja2.environment import Environment from jinja2.exceptions import TemplateNotFound from jinja2.exceptions import TemplatesNotFound from jinja2.exceptions import TemplateSyntaxError from jinja2.exceptions import UndefinedError from jinja2.loaders import DictLoader @pytest.fixture def test_env(): env = Environment( loader=DictLoader( dict( module="{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}", header="[{{ foo }}|{{ 23 }}]", o_printer="({{ o }})", ) ) ) env.globals["bar"] = 23 return env class TestImports: def test_context_imports(self, test_env): t = test_env.from_string('{% import "module" as m %}{{ m.test() }}') assert t.render(foo=42) == "[|23]" t = test_env.from_string( '{% import "module" as m without context %}{{ m.test() }}' ) assert t.render(foo=42) == "[|23]" t = test_env.from_string( '{% import "module" as m with context %}{{ m.test() }}' ) assert t.render(foo=42) == "[42|23]" t = test_env.from_string('{% from "module" import test %}{{ test() }}') assert t.render(foo=42) == "[|23]" t = test_env.from_string( '{% from "module" import test without context %}{{ test() }}' ) assert t.render(foo=42) == "[|23]" t = test_env.from_string( '{% from "module" import test with context %}{{ test() }}' ) assert t.render(foo=42) == "[42|23]" def test_import_needs_name(self, test_env): test_env.from_string('{% from "foo" import bar %}') test_env.from_string('{% from "foo" import bar, baz %}') with pytest.raises(TemplateSyntaxError): test_env.from_string('{% from "foo" import %}') def test_no_trailing_comma(self, test_env): with pytest.raises(TemplateSyntaxError): test_env.from_string('{% from "foo" import bar, %}') with pytest.raises(TemplateSyntaxError): test_env.from_string('{% from "foo" import bar,, %}') with pytest.raises(TemplateSyntaxError): test_env.from_string('{% from "foo" import, %}') def test_trailing_comma_with_context(self, test_env): test_env.from_string('{% from "foo" import bar, baz with context %}') test_env.from_string('{% from "foo" import bar, baz, with context %}') test_env.from_string('{% from "foo" import bar, with context %}') test_env.from_string('{% from "foo" import bar, with, context %}') test_env.from_string('{% from "foo" import bar, with with context %}') with pytest.raises(TemplateSyntaxError): test_env.from_string('{% from "foo" import bar,, with context %}') with pytest.raises(TemplateSyntaxError): test_env.from_string('{% from "foo" import bar with context, %}') def test_exports(self, test_env): m = test_env.from_string( """ {% macro toplevel() %}...{% endmacro %} {% macro __private() %}...{% endmacro %} {% set variable = 42 %} {% for item in [1] %} {% macro notthere() %}{% endmacro %} {% endfor %} """ ).module assert m.toplevel() == "..." assert not hasattr(m, "__missing") assert m.variable == 42 assert not hasattr(m, "notthere") def test_not_exported(self, test_env): t = test_env.from_string("{% from 'module' import nothing %}{{ nothing() }}") with pytest.raises(UndefinedError, match="does not export the requested name"): t.render() def test_import_with_globals(self, test_env): t = test_env.from_string( '{% import "module" as m %}{{ m.test() }}', globals={"foo": 42} ) assert t.render() == "[42|23]" t = test_env.from_string('{% import "module" as m %}{{ m.test() }}') assert t.render() == "[|23]" def test_import_with_globals_override(self, test_env): t = test_env.from_string( '{% set foo = 41 %}{% import "module" as m %}{{ m.test() }}', globals={"foo": 42}, ) assert t.render() == "[42|23]" def test_from_import_with_globals(self, test_env): t = test_env.from_string( '{% from "module" import test %}{{ test() }}', globals={"foo": 42}, ) assert t.render() == "[42|23]" class TestIncludes: def test_context_include(self, test_env): t = test_env.from_string('{% include "header" %}') assert t.render(foo=42) == "[42|23]" t = test_env.from_string('{% include "header" with context %}') assert t.render(foo=42) == "[42|23]" t = test_env.from_string('{% include "header" without context %}') assert t.render(foo=42) == "[|23]" def test_choice_includes(self, test_env): t = test_env.from_string('{% include ["missing", "header"] %}') assert t.render(foo=42) == "[42|23]" t = test_env.from_string('{% include ["missing", "missing2"] ignore missing %}') assert t.render(foo=42) == "" t = test_env.from_string('{% include ["missing", "missing2"] %}') pytest.raises(TemplateNotFound, t.render) with pytest.raises(TemplatesNotFound) as e: t.render() assert e.value.templates == ["missing", "missing2"] assert e.value.name == "missing2" def test_includes(t, **ctx): ctx["foo"] = 42 assert t.render(ctx) == "[42|23]" t = test_env.from_string('{% include ["missing", "header"] %}') test_includes(t) t = test_env.from_string("{% include x %}") test_includes(t, x=["missing", "header"]) t = test_env.from_string('{% include [x, "header"] %}') test_includes(t, x="missing") t = test_env.from_string("{% include x %}") test_includes(t, x="header") t = test_env.from_string("{% include [x] %}") test_includes(t, x="header") def test_include_ignoring_missing(self, test_env): t = test_env.from_string('{% include "missing" %}') pytest.raises(TemplateNotFound, t.render) for extra in "", "with context", "without context": t = test_env.from_string( '{% include "missing" ignore missing ' + extra + " %}" ) assert t.render() == "" def test_context_include_with_overrides(self, test_env): env = Environment( loader=DictLoader( dict( main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}", item="{{ item }}", ) ) ) assert env.get_template("main").render() == "123" def test_unoptimized_scopes(self, test_env): t = test_env.from_string( """ {% macro outer(o) %} {% macro inner() %} {% include "o_printer" %} {% endmacro %} {{ inner() }} {% endmacro %} {{ outer("FOO") }} """ ) assert t.render().strip() == "(FOO)" def test_import_from_with_context(self): env = Environment( loader=DictLoader({"a": "{% macro x() %}{{ foobar }}{% endmacro %}"}) ) t = env.from_string( "{% set foobar = 42 %}{% from 'a' import x with context %}{{ x() }}" ) assert t.render() == "42"