diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-02-10 16:44:39 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:39 +0300 |
commit | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch) | |
tree | 64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/Jinja2/py3/tests | |
parent | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff) | |
download | ydb-e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0.tar.gz |
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/Jinja2/py3/tests')
25 files changed, 5276 insertions, 5276 deletions
diff --git a/contrib/python/Jinja2/py3/tests/conftest.py b/contrib/python/Jinja2/py3/tests/conftest.py index 9eb2453e5a..218c3c77cf 100644 --- a/contrib/python/Jinja2/py3/tests/conftest.py +++ b/contrib/python/Jinja2/py3/tests/conftest.py @@ -1,50 +1,50 @@ -from pathlib import Path +from pathlib import Path import pytest from jinja2 import loaders -from jinja2.environment import Environment +from jinja2.environment import Environment @pytest.fixture def env(): - """returns a new environment.""" + """returns a new environment.""" return Environment() @pytest.fixture def dict_loader(): - """returns DictLoader""" - return loaders.DictLoader({"justdict.html": "FOO"}) + """returns DictLoader""" + return loaders.DictLoader({"justdict.html": "FOO"}) @pytest.fixture def package_loader(): - """returns PackageLoader initialized from templates""" - return loaders.PackageLoader("res", "templates") + """returns PackageLoader initialized from templates""" + return loaders.PackageLoader("res", "templates") @pytest.fixture def filesystem_loader(): - """returns FileSystemLoader initialized to res/templates directory""" - import yatest.common - here = Path(yatest.common.test_source_path()) - return loaders.FileSystemLoader(here / "res" / "templates") + """returns FileSystemLoader initialized to res/templates directory""" + import yatest.common + here = Path(yatest.common.test_source_path()) + return loaders.FileSystemLoader(here / "res" / "templates") @pytest.fixture def function_loader(): - """returns a FunctionLoader""" - return loaders.FunctionLoader({"justfunction.html": "FOO"}.get) + """returns a FunctionLoader""" + return loaders.FunctionLoader({"justfunction.html": "FOO"}.get) @pytest.fixture def choice_loader(dict_loader, package_loader): - """returns a ChoiceLoader""" + """returns a ChoiceLoader""" return loaders.ChoiceLoader([dict_loader, package_loader]) @pytest.fixture def prefix_loader(filesystem_loader, dict_loader): - """returns a PrefixLoader""" - return loaders.PrefixLoader({"a": filesystem_loader, "b": dict_loader}) + """returns a PrefixLoader""" + return loaders.PrefixLoader({"a": filesystem_loader, "b": dict_loader}) diff --git a/contrib/python/Jinja2/py3/tests/res/templates/mojibake.txt b/contrib/python/Jinja2/py3/tests/res/templates/mojibake.txt index b23f594fcf..4b94aa63dc 100644 --- a/contrib/python/Jinja2/py3/tests/res/templates/mojibake.txt +++ b/contrib/python/Jinja2/py3/tests/res/templates/mojibake.txt @@ -1 +1 @@ -文字化け +文字化け diff --git a/contrib/python/Jinja2/py3/tests/res/templates2/foo b/contrib/python/Jinja2/py3/tests/res/templates2/foo index 40e7538a91..1c4ad3e496 100644 --- a/contrib/python/Jinja2/py3/tests/res/templates2/foo +++ b/contrib/python/Jinja2/py3/tests/res/templates2/foo @@ -1,2 +1,2 @@ -Looks like the start of templates/foo/test.html -Tested by test_filesystem_loader_overlapping_names +Looks like the start of templates/foo/test.html +Tested by test_filesystem_loader_overlapping_names diff --git a/contrib/python/Jinja2/py3/tests/test_api.py b/contrib/python/Jinja2/py3/tests/test_api.py index a1e25d0a95..4db3b4a96a 100644 --- a/contrib/python/Jinja2/py3/tests/test_api.py +++ b/contrib/python/Jinja2/py3/tests/test_api.py @@ -1,86 +1,86 @@ -import shutil +import shutil import tempfile -from pathlib import Path +from pathlib import Path import pytest - -from jinja2 import ChainableUndefined -from jinja2 import DebugUndefined -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import is_undefined -from jinja2 import make_logging_undefined -from jinja2 import meta -from jinja2 import StrictUndefined -from jinja2 import Template -from jinja2 import TemplatesNotFound -from jinja2 import Undefined -from jinja2 import UndefinedError + +from jinja2 import ChainableUndefined +from jinja2 import DebugUndefined +from jinja2 import DictLoader +from jinja2 import Environment +from jinja2 import is_undefined +from jinja2 import make_logging_undefined +from jinja2 import meta +from jinja2 import StrictUndefined +from jinja2 import Template +from jinja2 import TemplatesNotFound +from jinja2 import Undefined +from jinja2 import UndefinedError from jinja2.compiler import CodeGenerator from jinja2.runtime import Context from jinja2.utils import Cycler -from jinja2.utils import pass_context -from jinja2.utils import pass_environment -from jinja2.utils import pass_eval_context +from jinja2.utils import pass_context +from jinja2.utils import pass_environment +from jinja2.utils import pass_eval_context -class TestExtendedAPI: +class TestExtendedAPI: def test_item_and_attribute(self, env): from jinja2.sandbox import SandboxedEnvironment for env in Environment(), SandboxedEnvironment(): - tmpl = env.from_string("{{ foo.items()|list }}") - assert tmpl.render(foo={"items": 42}) == "[('items', 42)]" + tmpl = env.from_string("{{ foo.items()|list }}") + assert tmpl.render(foo={"items": 42}) == "[('items', 42)]" tmpl = env.from_string('{{ foo|attr("items")()|list }}') - assert tmpl.render(foo={"items": 42}) == "[('items', 42)]" + assert tmpl.render(foo={"items": 42}) == "[('items', 42)]" tmpl = env.from_string('{{ foo["items"] }}') - assert tmpl.render(foo={"items": 42}) == "42" - - def test_finalize(self): - e = Environment(finalize=lambda v: "" if v is None else v) - t = e.from_string("{% for item in seq %}|{{ item }}{% endfor %}") - assert t.render(seq=(None, 1, "foo")) == "||1|foo" - - def test_finalize_constant_expression(self): - e = Environment(finalize=lambda v: "" if v is None else v) - t = e.from_string("<{{ none }}>") - assert t.render() == "<>" - - def test_no_finalize_template_data(self): - e = Environment(finalize=lambda v: type(v).__name__) - t = e.from_string("<{{ value }}>") - # If template data was finalized, it would print "strintstr". - assert t.render(value=123) == "<int>" - - def test_context_finalize(self): - @pass_context - def finalize(context, value): - return value * context["scale"] - - e = Environment(finalize=finalize) - t = e.from_string("{{ value }}") - assert t.render(value=5, scale=3) == "15" - - def test_eval_finalize(self): - @pass_eval_context - def finalize(eval_ctx, value): - return str(eval_ctx.autoescape) + value - - e = Environment(finalize=finalize, autoescape=True) - t = e.from_string("{{ value }}") - assert t.render(value="<script>") == "True<script>" - - def test_env_autoescape(self): - @pass_environment - def finalize(env, value): - return " ".join( - (env.variable_start_string, repr(value), env.variable_end_string) - ) - - e = Environment(finalize=finalize) - t = e.from_string("{{ value }}") - assert t.render(value="hello") == "{{ 'hello' }}" - + assert tmpl.render(foo={"items": 42}) == "42" + + def test_finalize(self): + e = Environment(finalize=lambda v: "" if v is None else v) + t = e.from_string("{% for item in seq %}|{{ item }}{% endfor %}") + assert t.render(seq=(None, 1, "foo")) == "||1|foo" + + def test_finalize_constant_expression(self): + e = Environment(finalize=lambda v: "" if v is None else v) + t = e.from_string("<{{ none }}>") + assert t.render() == "<>" + + def test_no_finalize_template_data(self): + e = Environment(finalize=lambda v: type(v).__name__) + t = e.from_string("<{{ value }}>") + # If template data was finalized, it would print "strintstr". + assert t.render(value=123) == "<int>" + + def test_context_finalize(self): + @pass_context + def finalize(context, value): + return value * context["scale"] + + e = Environment(finalize=finalize) + t = e.from_string("{{ value }}") + assert t.render(value=5, scale=3) == "15" + + def test_eval_finalize(self): + @pass_eval_context + def finalize(eval_ctx, value): + return str(eval_ctx.autoescape) + value + + e = Environment(finalize=finalize, autoescape=True) + t = e.from_string("{{ value }}") + assert t.render(value="<script>") == "True<script>" + + def test_env_autoescape(self): + @pass_environment + def finalize(env, value): + return " ".join( + (env.variable_start_string, repr(value), env.variable_end_string) + ) + + e = Environment(finalize=finalize) + t = e.from_string("{{ value }}") + assert t.render(value="hello") == "{{ 'hello' }}" + def test_cycler(self, env): items = 1, 2, 3 c = Cycler(*items) @@ -103,134 +103,134 @@ class TestExtendedAPI: assert expr(foo=42) == 84 def test_template_passthrough(self, env): - t = Template("Content") + t = Template("Content") assert env.get_template(t) is t assert env.select_template([t]) is t assert env.get_or_select_template([t]) is t assert env.get_or_select_template(t) is t - def test_get_template_undefined(self, env): - """Passing Undefined to get/select_template raises an - UndefinedError or shows the undefined message in the list. - """ - env.loader = DictLoader({}) - t = Undefined(name="no_name_1") - - with pytest.raises(UndefinedError): - env.get_template(t) - - with pytest.raises(UndefinedError): - env.get_or_select_template(t) - - with pytest.raises(UndefinedError): - env.select_template(t) - - with pytest.raises(TemplatesNotFound) as exc_info: - env.select_template([t, "no_name_2"]) - - exc_message = str(exc_info.value) - assert "'no_name_1' is undefined" in exc_message - assert "no_name_2" in exc_message - + def test_get_template_undefined(self, env): + """Passing Undefined to get/select_template raises an + UndefinedError or shows the undefined message in the list. + """ + env.loader = DictLoader({}) + t = Undefined(name="no_name_1") + + with pytest.raises(UndefinedError): + env.get_template(t) + + with pytest.raises(UndefinedError): + env.get_or_select_template(t) + + with pytest.raises(UndefinedError): + env.select_template(t) + + with pytest.raises(TemplatesNotFound) as exc_info: + env.select_template([t, "no_name_2"]) + + exc_message = str(exc_info.value) + assert "'no_name_1' is undefined" in exc_message + assert "no_name_2" in exc_message + def test_autoescape_autoselect(self, env): def select_autoescape(name): - if name is None or "." not in name: + if name is None or "." not in name: return False - return name.endswith(".html") - - env = Environment( - autoescape=select_autoescape, - loader=DictLoader({"test.txt": "{{ foo }}", "test.html": "{{ foo }}"}), - ) - t = env.get_template("test.txt") - assert t.render(foo="<foo>") == "<foo>" - t = env.get_template("test.html") - assert t.render(foo="<foo>") == "<foo>" - t = env.from_string("{{ foo }}") - assert t.render(foo="<foo>") == "<foo>" - - def test_sandbox_max_range(self, env): - from jinja2.sandbox import SandboxedEnvironment, MAX_RANGE - - env = SandboxedEnvironment() - t = env.from_string("{% for item in range(total) %}{{ item }}{% endfor %}") - - with pytest.raises(OverflowError): - t.render(total=MAX_RANGE + 1) - - -class TestMeta: + return name.endswith(".html") + + env = Environment( + autoescape=select_autoescape, + loader=DictLoader({"test.txt": "{{ foo }}", "test.html": "{{ foo }}"}), + ) + t = env.get_template("test.txt") + assert t.render(foo="<foo>") == "<foo>" + t = env.get_template("test.html") + assert t.render(foo="<foo>") == "<foo>" + t = env.from_string("{{ foo }}") + assert t.render(foo="<foo>") == "<foo>" + + def test_sandbox_max_range(self, env): + from jinja2.sandbox import SandboxedEnvironment, MAX_RANGE + + env = SandboxedEnvironment() + t = env.from_string("{% for item in range(total) %}{{ item }}{% endfor %}") + + with pytest.raises(OverflowError): + t.render(total=MAX_RANGE + 1) + + +class TestMeta: def test_find_undeclared_variables(self, env): - ast = env.parse("{% set foo = 42 %}{{ bar + foo }}") + ast = env.parse("{% set foo = 42 %}{{ bar + foo }}") x = meta.find_undeclared_variables(ast) - assert x == {"bar"} - - ast = env.parse( - "{% set foo = 42 %}{{ bar + foo }}" - "{% macro meh(x) %}{{ x }}{% endmacro %}" - "{% for item in seq %}{{ muh(item) + meh(seq) }}" - "{% endfor %}" - ) + assert x == {"bar"} + + ast = env.parse( + "{% set foo = 42 %}{{ bar + foo }}" + "{% macro meh(x) %}{{ x }}{% endmacro %}" + "{% for item in seq %}{{ muh(item) + meh(seq) }}" + "{% endfor %}" + ) x = meta.find_undeclared_variables(ast) - assert x == {"bar", "seq", "muh"} + assert x == {"bar", "seq", "muh"} + + ast = env.parse("{% for x in range(5) %}{{ x }}{% endfor %}{{ foo }}") + x = meta.find_undeclared_variables(ast) + assert x == {"foo"} - ast = env.parse("{% for x in range(5) %}{{ x }}{% endfor %}{{ foo }}") - x = meta.find_undeclared_variables(ast) - assert x == {"foo"} - def test_find_refererenced_templates(self, env): ast = env.parse('{% extends "layout.html" %}{% include helper %}') i = meta.find_referenced_templates(ast) - assert next(i) == "layout.html" + assert next(i) == "layout.html" assert next(i) is None assert list(i) == [] - ast = env.parse( - '{% extends "layout.html" %}' - '{% from "test.html" import a, b as c %}' - '{% import "meh.html" as meh %}' - '{% include "muh.html" %}' - ) + ast = env.parse( + '{% extends "layout.html" %}' + '{% from "test.html" import a, b as c %}' + '{% import "meh.html" as meh %}' + '{% include "muh.html" %}' + ) i = meta.find_referenced_templates(ast) - assert list(i) == ["layout.html", "test.html", "meh.html", "muh.html"] + assert list(i) == ["layout.html", "test.html", "meh.html", "muh.html"] def test_find_included_templates(self, env): ast = env.parse('{% include ["foo.html", "bar.html"] %}') i = meta.find_referenced_templates(ast) - assert list(i) == ["foo.html", "bar.html"] + assert list(i) == ["foo.html", "bar.html"] ast = env.parse('{% include ("foo.html", "bar.html") %}') i = meta.find_referenced_templates(ast) - assert list(i) == ["foo.html", "bar.html"] + assert list(i) == ["foo.html", "bar.html"] ast = env.parse('{% include ["foo.html", "bar.html", foo] %}') i = meta.find_referenced_templates(ast) - assert list(i) == ["foo.html", "bar.html", None] + assert list(i) == ["foo.html", "bar.html", None] ast = env.parse('{% include ("foo.html", "bar.html", foo) %}') i = meta.find_referenced_templates(ast) - assert list(i) == ["foo.html", "bar.html", None] + assert list(i) == ["foo.html", "bar.html", None] -class TestStreaming: +class TestStreaming: def test_basic_streaming(self, env): - t = env.from_string( - "<ul>{% for item in seq %}<li>{{ loop.index }} - {{ item }}</li>" - "{%- endfor %}</ul>" - ) - stream = t.stream(seq=list(range(3))) - assert next(stream) == "<ul>" - assert "".join(stream) == "<li>1 - 0</li><li>2 - 1</li><li>3 - 2</li></ul>" + t = env.from_string( + "<ul>{% for item in seq %}<li>{{ loop.index }} - {{ item }}</li>" + "{%- endfor %}</ul>" + ) + stream = t.stream(seq=list(range(3))) + assert next(stream) == "<ul>" + assert "".join(stream) == "<li>1 - 0</li><li>2 - 1</li><li>3 - 2</li></ul>" def test_buffered_streaming(self, env): - tmpl = env.from_string( - "<ul>{% for item in seq %}<li>{{ loop.index }} - {{ item }}</li>" - "{%- endfor %}</ul>" - ) - stream = tmpl.stream(seq=list(range(3))) + tmpl = env.from_string( + "<ul>{% for item in seq %}<li>{{ loop.index }} - {{ item }}</li>" + "{%- endfor %}</ul>" + ) + stream = tmpl.stream(seq=list(range(3))) stream.enable_buffering(size=3) - assert next(stream) == "<ul><li>1" - assert next(stream) == " - 0</li>" + assert next(stream) == "<ul><li>1" + assert next(stream) == " - 0</li>" def test_streaming_behavior(self, env): tmpl = env.from_string("") @@ -242,193 +242,193 @@ class TestStreaming: assert not stream.buffered def test_dump_stream(self, env): - tmp = Path(tempfile.mkdtemp()) + tmp = Path(tempfile.mkdtemp()) try: - tmpl = env.from_string("\u2713") + tmpl = env.from_string("\u2713") stream = tmpl.stream() - stream.dump(str(tmp / "dump.txt"), "utf-8") - assert (tmp / "dump.txt").read_bytes() == b"\xe2\x9c\x93" + stream.dump(str(tmp / "dump.txt"), "utf-8") + assert (tmp / "dump.txt").read_bytes() == b"\xe2\x9c\x93" finally: shutil.rmtree(tmp) -class TestUndefined: +class TestUndefined: def test_stopiteration_is_undefined(self): def test(): raise StopIteration() - - t = Template("A{{ test() }}B") - assert t.render(test=test) == "AB" - t = Template("A{{ test().missingattribute }}B") + + t = Template("A{{ test() }}B") + assert t.render(test=test) == "AB" + t = Template("A{{ test().missingattribute }}B") pytest.raises(UndefinedError, t.render, test=test) def test_undefined_and_special_attributes(self): - with pytest.raises(AttributeError): - Undefined("Foo").__dict__ - - def test_undefined_attribute_error(self): - # Django's LazyObject turns the __class__ attribute into a - # property that resolves the wrapped function. If that wrapped - # function raises an AttributeError, printing the repr of the - # object in the undefined message would cause a RecursionError. - class Error: - @property # type: ignore - def __class__(self): - raise AttributeError() - - u = Undefined(obj=Error(), name="hello") - - with pytest.raises(UndefinedError): - getattr(u, "recursion", None) - + with pytest.raises(AttributeError): + Undefined("Foo").__dict__ + + def test_undefined_attribute_error(self): + # Django's LazyObject turns the __class__ attribute into a + # property that resolves the wrapped function. If that wrapped + # function raises an AttributeError, printing the repr of the + # object in the undefined message would cause a RecursionError. + class Error: + @property # type: ignore + def __class__(self): + raise AttributeError() + + u = Undefined(obj=Error(), name="hello") + + with pytest.raises(UndefinedError): + getattr(u, "recursion", None) + def test_logging_undefined(self): _messages = [] - class DebugLogger: + class DebugLogger: def warning(self, msg, *args): - _messages.append("W:" + msg % args) + _messages.append("W:" + msg % args) def error(self, msg, *args): - _messages.append("E:" + msg % args) + _messages.append("E:" + msg % args) logging_undefined = make_logging_undefined(DebugLogger()) env = Environment(undefined=logging_undefined) - assert env.from_string("{{ missing }}").render() == "" - pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) - assert env.from_string("{{ missing|list }}").render() == "[]" - assert env.from_string("{{ missing is not defined }}").render() == "True" - assert env.from_string("{{ foo.missing }}").render(foo=42) == "" - assert env.from_string("{{ not missing }}").render() == "True" + assert env.from_string("{{ missing }}").render() == "" + pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) + assert env.from_string("{{ missing|list }}").render() == "[]" + assert env.from_string("{{ missing is not defined }}").render() == "True" + assert env.from_string("{{ foo.missing }}").render(foo=42) == "" + assert env.from_string("{{ not missing }}").render() == "True" assert _messages == [ - "W:Template variable warning: 'missing' is undefined", + "W:Template variable warning: 'missing' is undefined", "E:Template variable error: 'missing' is undefined", - "W:Template variable warning: 'missing' is undefined", - "W:Template variable warning: 'int object' has no attribute 'missing'", - "W:Template variable warning: 'missing' is undefined", + "W:Template variable warning: 'missing' is undefined", + "W:Template variable warning: 'int object' has no attribute 'missing'", + "W:Template variable warning: 'missing' is undefined", ] def test_default_undefined(self): env = Environment(undefined=Undefined) - assert env.from_string("{{ missing }}").render() == "" - pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) - assert env.from_string("{{ missing|list }}").render() == "[]" - assert env.from_string("{{ missing is not defined }}").render() == "True" - assert env.from_string("{{ foo.missing }}").render(foo=42) == "" - assert env.from_string("{{ not missing }}").render() == "True" - pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render) - assert env.from_string("{{ 'foo' in missing }}").render() == "False" - und1 = Undefined(name="x") - und2 = Undefined(name="y") - assert und1 == und2 - assert und1 != 42 - assert hash(und1) == hash(und2) == hash(Undefined()) - with pytest.raises(AttributeError): - getattr(Undefined, "__slots__") # noqa: B009 - - def test_chainable_undefined(self): - env = Environment(undefined=ChainableUndefined) - # The following tests are copied from test_default_undefined - assert env.from_string("{{ missing }}").render() == "" - assert env.from_string("{{ missing|list }}").render() == "[]" - assert env.from_string("{{ missing is not defined }}").render() == "True" - assert env.from_string("{{ foo.missing }}").render(foo=42) == "" - assert env.from_string("{{ not missing }}").render() == "True" - pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render) - with pytest.raises(AttributeError): - getattr(ChainableUndefined, "__slots__") # noqa: B009 - - # The following tests ensure subclass functionality works as expected - assert env.from_string('{{ missing.bar["baz"] }}').render() == "" - assert env.from_string('{{ foo.bar["baz"]._undefined_name }}').render() == "foo" - assert ( - env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(foo=42) - == "bar" - ) - assert ( - env.from_string('{{ foo.bar["baz"]._undefined_name }}').render( - foo={"bar": 42} - ) - == "baz" - ) - + assert env.from_string("{{ missing }}").render() == "" + pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) + assert env.from_string("{{ missing|list }}").render() == "[]" + assert env.from_string("{{ missing is not defined }}").render() == "True" + assert env.from_string("{{ foo.missing }}").render(foo=42) == "" + assert env.from_string("{{ not missing }}").render() == "True" + pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render) + assert env.from_string("{{ 'foo' in missing }}").render() == "False" + und1 = Undefined(name="x") + und2 = Undefined(name="y") + assert und1 == und2 + assert und1 != 42 + assert hash(und1) == hash(und2) == hash(Undefined()) + with pytest.raises(AttributeError): + getattr(Undefined, "__slots__") # noqa: B009 + + def test_chainable_undefined(self): + env = Environment(undefined=ChainableUndefined) + # The following tests are copied from test_default_undefined + assert env.from_string("{{ missing }}").render() == "" + assert env.from_string("{{ missing|list }}").render() == "[]" + assert env.from_string("{{ missing is not defined }}").render() == "True" + assert env.from_string("{{ foo.missing }}").render(foo=42) == "" + assert env.from_string("{{ not missing }}").render() == "True" + pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render) + with pytest.raises(AttributeError): + getattr(ChainableUndefined, "__slots__") # noqa: B009 + + # The following tests ensure subclass functionality works as expected + assert env.from_string('{{ missing.bar["baz"] }}').render() == "" + assert env.from_string('{{ foo.bar["baz"]._undefined_name }}').render() == "foo" + assert ( + env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(foo=42) + == "bar" + ) + assert ( + env.from_string('{{ foo.bar["baz"]._undefined_name }}').render( + foo={"bar": 42} + ) + == "baz" + ) + def test_debug_undefined(self): env = Environment(undefined=DebugUndefined) - assert env.from_string("{{ missing }}").render() == "{{ missing }}" - pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) - assert env.from_string("{{ missing|list }}").render() == "[]" - assert env.from_string("{{ missing is not defined }}").render() == "True" - assert ( - env.from_string("{{ foo.missing }}").render(foo=42) - == "{{ no such element: int object['missing'] }}" - ) - assert env.from_string("{{ not missing }}").render() == "True" - undefined_hint = "this is testing undefined hint of DebugUndefined" - assert ( - str(DebugUndefined(hint=undefined_hint)) - == f"{{{{ undefined value printed: {undefined_hint} }}}}" - ) - with pytest.raises(AttributeError): - getattr(DebugUndefined, "__slots__") # noqa: B009 + assert env.from_string("{{ missing }}").render() == "{{ missing }}" + pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) + assert env.from_string("{{ missing|list }}").render() == "[]" + assert env.from_string("{{ missing is not defined }}").render() == "True" + assert ( + env.from_string("{{ foo.missing }}").render(foo=42) + == "{{ no such element: int object['missing'] }}" + ) + assert env.from_string("{{ not missing }}").render() == "True" + undefined_hint = "this is testing undefined hint of DebugUndefined" + assert ( + str(DebugUndefined(hint=undefined_hint)) + == f"{{{{ undefined value printed: {undefined_hint} }}}}" + ) + with pytest.raises(AttributeError): + getattr(DebugUndefined, "__slots__") # noqa: B009 def test_strict_undefined(self): env = Environment(undefined=StrictUndefined) - pytest.raises(UndefinedError, env.from_string("{{ missing }}").render) - pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) - pytest.raises(UndefinedError, env.from_string("{{ missing|list }}").render) - pytest.raises(UndefinedError, env.from_string("{{ 'foo' in missing }}").render) - assert env.from_string("{{ missing is not defined }}").render() == "True" - pytest.raises( - UndefinedError, env.from_string("{{ foo.missing }}").render, foo=42 - ) - pytest.raises(UndefinedError, env.from_string("{{ not missing }}").render) - assert ( - env.from_string('{{ missing|default("default", true) }}').render() - == "default" - ) - with pytest.raises(AttributeError): - getattr(StrictUndefined, "__slots__") # noqa: B009 - assert env.from_string('{{ "foo" if false }}').render() == "" + pytest.raises(UndefinedError, env.from_string("{{ missing }}").render) + pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render) + pytest.raises(UndefinedError, env.from_string("{{ missing|list }}").render) + pytest.raises(UndefinedError, env.from_string("{{ 'foo' in missing }}").render) + assert env.from_string("{{ missing is not defined }}").render() == "True" + pytest.raises( + UndefinedError, env.from_string("{{ foo.missing }}").render, foo=42 + ) + pytest.raises(UndefinedError, env.from_string("{{ not missing }}").render) + assert ( + env.from_string('{{ missing|default("default", true) }}').render() + == "default" + ) + with pytest.raises(AttributeError): + getattr(StrictUndefined, "__slots__") # noqa: B009 + assert env.from_string('{{ "foo" if false }}').render() == "" def test_indexing_gives_undefined(self): t = Template("{{ var[42].foo }}") pytest.raises(UndefinedError, t.render, var=0) def test_none_gives_proper_error(self): - with pytest.raises(UndefinedError, match="'None' has no attribute 'split'"): - Environment().getattr(None, "split")() + with pytest.raises(UndefinedError, match="'None' has no attribute 'split'"): + Environment().getattr(None, "split")() def test_object_repr(self): - with pytest.raises( - UndefinedError, match="'int object' has no attribute 'upper'" - ): - Undefined(obj=42, name="upper")() + with pytest.raises( + UndefinedError, match="'int object' has no attribute 'upper'" + ): + Undefined(obj=42, name="upper")() -class TestLowLevel: +class TestLowLevel: def test_custom_code_generator(self): class CustomCodeGenerator(CodeGenerator): def visit_Const(self, node, frame=None): # This method is pure nonsense, but works fine for testing... - if node.value == "foo": - self.write(repr("bar")) + if node.value == "foo": + self.write(repr("bar")) else: - super().visit_Const(node, frame) + super().visit_Const(node, frame) class CustomEnvironment(Environment): code_generator_class = CustomCodeGenerator env = CustomEnvironment() tmpl = env.from_string('{% set foo = "foo" %}{{ foo }}') - assert tmpl.render() == "bar" + assert tmpl.render() == "bar" def test_custom_context(self): class CustomContext(Context): def resolve_or_missing(self, key): - return "resolve-" + key + return "resolve-" + key class CustomEnvironment(Environment): context_class = CustomContext env = CustomEnvironment() - tmpl = env.from_string("{{ foo }}") - assert tmpl.render() == "resolve-foo" + tmpl = env.from_string("{{ foo }}") + assert tmpl.render() == "resolve-foo" diff --git a/contrib/python/Jinja2/py3/tests/test_async.py b/contrib/python/Jinja2/py3/tests/test_async.py index e30c528562..375a7bac33 100644 --- a/contrib/python/Jinja2/py3/tests/test_async.py +++ b/contrib/python/Jinja2/py3/tests/test_async.py @@ -1,46 +1,46 @@ import asyncio -import sys - -import pytest - -from jinja2 import ChainableUndefined -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import Template -from jinja2.async_utils import auto_aiter -from jinja2.exceptions import TemplateNotFound -from jinja2.exceptions import TemplatesNotFound -from jinja2.exceptions import UndefinedError -from jinja2.nativetypes import NativeEnvironment - - -if sys.version_info < (3, 7): - - def run(coro): - loop = asyncio.get_event_loop() - return loop.run_until_complete(coro) - - -else: - - def run(coro): - return asyncio.run(coro) - - +import sys + +import pytest + +from jinja2 import ChainableUndefined +from jinja2 import DictLoader +from jinja2 import Environment +from jinja2 import Template +from jinja2.async_utils import auto_aiter +from jinja2.exceptions import TemplateNotFound +from jinja2.exceptions import TemplatesNotFound +from jinja2.exceptions import UndefinedError +from jinja2.nativetypes import NativeEnvironment + + +if sys.version_info < (3, 7): + + def run(coro): + loop = asyncio.get_event_loop() + return loop.run_until_complete(coro) + + +else: + + def run(coro): + return asyncio.run(coro) + + def test_basic_async(): - t = Template( - "{% for item in [1, 2, 3] %}[{{ item }}]{% endfor %}", enable_async=True - ) - + t = Template( + "{% for item in [1, 2, 3] %}[{{ item }}]{% endfor %}", enable_async=True + ) + async def func(): return await t.render_async() rv = run(func()) - assert rv == "[1][2][3]" + assert rv == "[1][2][3]" def test_await_on_calls(): - t = Template("{{ async_func() + normal_func() }}", enable_async=True) + t = Template("{{ async_func() + normal_func() }}", enable_async=True) async def async_func(): return 42 @@ -49,14 +49,14 @@ def test_await_on_calls(): return 23 async def func(): - return await t.render_async(async_func=async_func, normal_func=normal_func) + return await t.render_async(async_func=async_func, normal_func=normal_func) rv = run(func()) - assert rv == "65" + assert rv == "65" def test_await_on_calls_normal_render(): - t = Template("{{ async_func() + normal_func() }}", enable_async=True) + t = Template("{{ async_func() + normal_func() }}", enable_async=True) async def async_func(): return 42 @@ -64,16 +64,16 @@ def test_await_on_calls_normal_render(): def normal_func(): return 23 - rv = t.render(async_func=async_func, normal_func=normal_func) + rv = t.render(async_func=async_func, normal_func=normal_func) - assert rv == "65" + assert rv == "65" def test_await_and_macros(): - t = Template( - "{% macro foo(x) %}[{{ x }}][{{ async_func() }}]{% endmacro %}{{ foo(42) }}", - enable_async=True, - ) + t = Template( + "{% macro foo(x) %}[{{ x }}][{{ async_func() }}]{% endmacro %}{{ foo(42) }}", + enable_async=True, + ) async def async_func(): return 42 @@ -82,87 +82,87 @@ def test_await_and_macros(): return await t.render_async(async_func=async_func) rv = run(func()) - assert rv == "[42][42]" + assert rv == "[42][42]" def test_async_blocks(): - t = Template( - "{% block foo %}<Test>{% endblock %}{{ self.foo() }}", - enable_async=True, - autoescape=True, - ) - + t = Template( + "{% block foo %}<Test>{% endblock %}{{ self.foo() }}", + enable_async=True, + autoescape=True, + ) + async def func(): return await t.render_async() rv = run(func()) - assert rv == "<Test><Test>" + assert rv == "<Test><Test>" def test_async_generate(): - t = Template("{% for x in [1, 2, 3] %}{{ x }}{% endfor %}", enable_async=True) + t = Template("{% for x in [1, 2, 3] %}{{ x }}{% endfor %}", enable_async=True) rv = list(t.generate()) - assert rv == ["1", "2", "3"] + assert rv == ["1", "2", "3"] def test_async_iteration_in_templates(): - t = Template("{% for x in rng %}{{ x }}{% endfor %}", enable_async=True) - + t = Template("{% for x in rng %}{{ x }}{% endfor %}", enable_async=True) + async def async_iterator(): for item in [1, 2, 3]: yield item - + rv = list(t.generate(rng=async_iterator())) - assert rv == ["1", "2", "3"] + assert rv == ["1", "2", "3"] def test_async_iteration_in_templates_extended(): - t = Template( - "{% for x in rng %}{{ loop.index0 }}/{{ x }}{% endfor %}", enable_async=True - ) - stream = t.generate(rng=auto_aiter(range(1, 4))) - assert next(stream) == "0" - assert "".join(stream) == "/11/22/3" + t = Template( + "{% for x in rng %}{{ loop.index0 }}/{{ x }}{% endfor %}", enable_async=True + ) + stream = t.generate(rng=auto_aiter(range(1, 4))) + assert next(stream) == "0" + assert "".join(stream) == "/11/22/3" @pytest.fixture def test_env_async(): - env = Environment( - loader=DictLoader( - dict( - module="{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}", - header="[{{ foo }}|{{ 23 }}]", - o_printer="({{ o }})", - ) - ), - enable_async=True, - ) - env.globals["bar"] = 23 + env = Environment( + loader=DictLoader( + dict( + module="{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}", + header="[{{ foo }}|{{ 23 }}]", + o_printer="({{ o }})", + ) + ), + enable_async=True, + ) + env.globals["bar"] = 23 return env -class TestAsyncImports: +class TestAsyncImports: def test_context_imports(self, test_env_async): t = test_env_async.from_string('{% import "module" as m %}{{ m.test() }}') - assert t.render(foo=42) == "[|23]" + assert t.render(foo=42) == "[|23]" t = test_env_async.from_string( '{% import "module" as m without context %}{{ m.test() }}' ) - assert t.render(foo=42) == "[|23]" + assert t.render(foo=42) == "[|23]" t = test_env_async.from_string( '{% import "module" as m with context %}{{ m.test() }}' ) - assert t.render(foo=42) == "[42|23]" + assert t.render(foo=42) == "[42|23]" t = test_env_async.from_string('{% from "module" import test %}{{ test() }}') - assert t.render(foo=42) == "[|23]" + assert t.render(foo=42) == "[|23]" t = test_env_async.from_string( '{% from "module" import test without context %}{{ test() }}' ) - assert t.render(foo=42) == "[|23]" + assert t.render(foo=42) == "[|23]" t = test_env_async.from_string( '{% from "module" import test with context %}{{ test() }}' ) - assert t.render(foo=42) == "[42|23]" + assert t.render(foo=42) == "[42|23]" def test_trailing_comma(self, test_env_async): test_env_async.from_string('{% from "foo" import bar, baz with context %}') @@ -172,113 +172,113 @@ class TestAsyncImports: test_env_async.from_string('{% from "foo" import bar, with with context %}') def test_exports(self, test_env_async): - m = run( - test_env_async.from_string( - """ + m = run( + test_env_async.from_string( + """ {% macro toplevel() %}...{% endmacro %} {% macro __private() %}...{% endmacro %} {% set variable = 42 %} {% for item in [1] %} {% macro notthere() %}{% endmacro %} {% endfor %} - """ - )._get_default_module_async() - ) - assert run(m.toplevel()) == "..." - assert not hasattr(m, "__missing") + """ + )._get_default_module_async() + ) + assert run(m.toplevel()) == "..." + assert not hasattr(m, "__missing") assert m.variable == 42 - assert not hasattr(m, "notthere") - - def test_import_with_globals(self, test_env_async): - t = test_env_async.from_string( - '{% import "module" as m %}{{ m.test() }}', globals={"foo": 42} - ) - assert t.render() == "[42|23]" - - t = test_env_async.from_string('{% import "module" as m %}{{ m.test() }}') - assert t.render() == "[|23]" - - def test_import_with_globals_override(self, test_env_async): - t = test_env_async.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_async): - t = test_env_async.from_string( - '{% from "module" import test %}{{ test() }}', - globals={"foo": 42}, - ) - assert t.render() == "[42|23]" - - -class TestAsyncIncludes: + assert not hasattr(m, "notthere") + + def test_import_with_globals(self, test_env_async): + t = test_env_async.from_string( + '{% import "module" as m %}{{ m.test() }}', globals={"foo": 42} + ) + assert t.render() == "[42|23]" + + t = test_env_async.from_string('{% import "module" as m %}{{ m.test() }}') + assert t.render() == "[|23]" + + def test_import_with_globals_override(self, test_env_async): + t = test_env_async.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_async): + t = test_env_async.from_string( + '{% from "module" import test %}{{ test() }}', + globals={"foo": 42}, + ) + assert t.render() == "[42|23]" + + +class TestAsyncIncludes: def test_context_include(self, test_env_async): t = test_env_async.from_string('{% include "header" %}') - assert t.render(foo=42) == "[42|23]" + assert t.render(foo=42) == "[42|23]" t = test_env_async.from_string('{% include "header" with context %}') - assert t.render(foo=42) == "[42|23]" + assert t.render(foo=42) == "[42|23]" t = test_env_async.from_string('{% include "header" without context %}') - assert t.render(foo=42) == "[|23]" + assert t.render(foo=42) == "[|23]" def test_choice_includes(self, test_env_async): t = test_env_async.from_string('{% include ["missing", "header"] %}') - assert t.render(foo=42) == "[42|23]" + assert t.render(foo=42) == "[42|23]" t = test_env_async.from_string( '{% include ["missing", "missing2"] ignore missing %}' ) - assert t.render(foo=42) == "" + assert t.render(foo=42) == "" t = test_env_async.from_string('{% include ["missing", "missing2"] %}') pytest.raises(TemplateNotFound, t.render) - with pytest.raises(TemplatesNotFound) as e: + with pytest.raises(TemplatesNotFound) as e: t.render() - assert e.value.templates == ["missing", "missing2"] - assert e.value.name == "missing2" - + 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]" + ctx["foo"] = 42 + assert t.render(ctx) == "[42|23]" t = test_env_async.from_string('{% include ["missing", "header"] %}') test_includes(t) - t = test_env_async.from_string("{% include x %}") - test_includes(t, x=["missing", "header"]) + t = test_env_async.from_string("{% include x %}") + test_includes(t, x=["missing", "header"]) t = test_env_async.from_string('{% include [x, "header"] %}') - test_includes(t, x="missing") - t = test_env_async.from_string("{% include x %}") - test_includes(t, x="header") - t = test_env_async.from_string("{% include x %}") - test_includes(t, x="header") - t = test_env_async.from_string("{% include [x] %}") - test_includes(t, x="header") + test_includes(t, x="missing") + t = test_env_async.from_string("{% include x %}") + test_includes(t, x="header") + t = test_env_async.from_string("{% include x %}") + test_includes(t, x="header") + t = test_env_async.from_string("{% include [x] %}") + test_includes(t, x="header") def test_include_ignoring_missing(self, test_env_async): t = test_env_async.from_string('{% include "missing" %}') pytest.raises(TemplateNotFound, t.render) - for extra in "", "with context", "without context": - t = test_env_async.from_string( - '{% include "missing" ignore missing ' + extra + " %}" - ) - assert t.render() == "" + for extra in "", "with context", "without context": + t = test_env_async.from_string( + '{% include "missing" ignore missing ' + extra + " %}" + ) + assert t.render() == "" def test_context_include_with_overrides(self, test_env_async): - env = Environment( - loader=DictLoader( - dict( - main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}", - item="{{ item }}", - ) - ) - ) + 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_async): - t = test_env_async.from_string( - """ + t = test_env_async.from_string( + """ {% macro outer(o) %} {% macro inner() %} {% include "o_printer" %} @@ -286,18 +286,18 @@ class TestAsyncIncludes: {{ inner() }} {% endmacro %} {{ outer("FOO") }} - """ - ) - assert t.render().strip() == "(FOO)" + """ + ) + assert t.render().strip() == "(FOO)" def test_unoptimized_scopes_autoescape(self): - env = Environment( - loader=DictLoader({"o_printer": "({{ o }})"}), - autoescape=True, - enable_async=True, - ) - t = env.from_string( - """ + env = Environment( + loader=DictLoader({"o_printer": "({{ o }})"}), + autoescape=True, + enable_async=True, + ) + t = env.from_string( + """ {% macro outer(o) %} {% macro inner() %} {% include "o_printer" %} @@ -305,228 +305,228 @@ class TestAsyncIncludes: {{ inner() }} {% endmacro %} {{ outer("FOO") }} - """ - ) - assert t.render().strip() == "(FOO)" + """ + ) + assert t.render().strip() == "(FOO)" -class TestAsyncForLoop: +class TestAsyncForLoop: def test_simple(self, test_env_async): - tmpl = test_env_async.from_string("{% for item in seq %}{{ item }}{% endfor %}") - assert tmpl.render(seq=list(range(10))) == "0123456789" + tmpl = test_env_async.from_string("{% for item in seq %}{{ item }}{% endfor %}") + assert tmpl.render(seq=list(range(10))) == "0123456789" def test_else(self, test_env_async): tmpl = test_env_async.from_string( - "{% for item in seq %}XXX{% else %}...{% endfor %}" - ) - assert tmpl.render() == "..." + "{% for item in seq %}XXX{% else %}...{% endfor %}" + ) + assert tmpl.render() == "..." def test_empty_blocks(self, test_env_async): - tmpl = test_env_async.from_string( - "<{% for item in seq %}{% else %}{% endfor %}>" - ) - assert tmpl.render() == "<>" - - @pytest.mark.parametrize( - "transform", [lambda x: x, iter, reversed, lambda x: (i for i in x), auto_aiter] - ) - def test_context_vars(self, test_env_async, transform): - t = test_env_async.from_string( - "{% for item in seq %}{{ loop.index }}|{{ loop.index0 }}" - "|{{ loop.revindex }}|{{ loop.revindex0 }}|{{ loop.first }}" - "|{{ loop.last }}|{{ loop.length }}\n{% endfor %}" - ) - out = t.render(seq=transform([42, 24])) - assert out == "1|0|2|1|True|False|2\n2|1|1|0|False|True|2\n" + tmpl = test_env_async.from_string( + "<{% for item in seq %}{% else %}{% endfor %}>" + ) + assert tmpl.render() == "<>" + + @pytest.mark.parametrize( + "transform", [lambda x: x, iter, reversed, lambda x: (i for i in x), auto_aiter] + ) + def test_context_vars(self, test_env_async, transform): + t = test_env_async.from_string( + "{% for item in seq %}{{ loop.index }}|{{ loop.index0 }}" + "|{{ loop.revindex }}|{{ loop.revindex0 }}|{{ loop.first }}" + "|{{ loop.last }}|{{ loop.length }}\n{% endfor %}" + ) + out = t.render(seq=transform([42, 24])) + assert out == "1|0|2|1|True|False|2\n2|1|1|0|False|True|2\n" def test_cycling(self, test_env_async): - tmpl = test_env_async.from_string( - """{% for item in seq %}{{ + tmpl = test_env_async.from_string( + """{% for item in seq %}{{ loop.cycle('<1>', '<2>') }}{% endfor %}{% - for item in seq %}{{ loop.cycle(*through) }}{% endfor %}""" - ) - output = tmpl.render(seq=list(range(4)), through=("<1>", "<2>")) - assert output == "<1><2>" * 4 + for item in seq %}{{ loop.cycle(*through) }}{% endfor %}""" + ) + output = tmpl.render(seq=list(range(4)), through=("<1>", "<2>")) + assert output == "<1><2>" * 4 def test_lookaround(self, test_env_async): - tmpl = test_env_async.from_string( - """{% for item in seq -%} + tmpl = test_env_async.from_string( + """{% for item in seq -%} {{ loop.previtem|default('x') }}-{{ item }}-{{ loop.nextitem|default('x') }}| - {%- endfor %}""" - ) + {%- endfor %}""" + ) output = tmpl.render(seq=list(range(4))) - assert output == "x-0-1|0-1-2|1-2-3|2-3-x|" + assert output == "x-0-1|0-1-2|1-2-3|2-3-x|" def test_changed(self, test_env_async): - tmpl = test_env_async.from_string( - """{% for item in seq -%} + tmpl = test_env_async.from_string( + """{% for item in seq -%} {{ loop.changed(item) }}, - {%- endfor %}""" - ) + {%- endfor %}""" + ) output = tmpl.render(seq=[None, None, 1, 2, 2, 3, 4, 4, 4]) - assert output == "True,False,True,True,False,True,True,False,False," + assert output == "True,False,True,True,False,True,True,False,False," def test_scope(self, test_env_async): - tmpl = test_env_async.from_string("{% for item in seq %}{% endfor %}{{ item }}") + tmpl = test_env_async.from_string("{% for item in seq %}{% endfor %}{{ item }}") output = tmpl.render(seq=list(range(10))) assert not output def test_varlen(self, test_env_async): def inner(): - yield from range(5) - - tmpl = test_env_async.from_string( - "{% for item in iter %}{{ item }}{% endfor %}" - ) + yield from range(5) + + tmpl = test_env_async.from_string( + "{% for item in iter %}{{ item }}{% endfor %}" + ) output = tmpl.render(iter=inner()) - assert output == "01234" + assert output == "01234" def test_noniter(self, test_env_async): - tmpl = test_env_async.from_string("{% for item in none %}...{% endfor %}") + tmpl = test_env_async.from_string("{% for item in none %}...{% endfor %}") pytest.raises(TypeError, tmpl.render) def test_recursive(self, test_env_async): - tmpl = test_env_async.from_string( - """{% for item in seq recursive -%} + tmpl = test_env_async.from_string( + """{% for item in seq recursive -%} [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}""" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[1<[1][2]>][2<[1][2]>][3<[a]>]" - ) + {%- endfor %}""" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[1<[1][2]>][2<[1][2]>][3<[a]>]" + ) def test_recursive_lookaround(self, test_env_async): - tmpl = test_env_async.from_string( - """{% for item in seq recursive -%} + tmpl = test_env_async.from_string( + """{% for item in seq recursive -%} [{{ loop.previtem.a if loop.previtem is defined else 'x' }}.{{ item.a }}.{{ loop.nextitem.a if loop.nextitem is defined else 'x' }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}""" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]" - ) + {%- endfor %}""" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]" + ) def test_recursive_depth0(self, test_env_async): - tmpl = test_env_async.from_string( - "{% for item in seq recursive %}[{{ loop.depth0 }}:{{ item.a }}" - "{% if item.b %}<{{ loop(item.b) }}>{% endif %}]{% endfor %}" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]" - ) + tmpl = test_env_async.from_string( + "{% for item in seq recursive %}[{{ loop.depth0 }}:{{ item.a }}" + "{% if item.b %}<{{ loop(item.b) }}>{% endif %}]{% endfor %}" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]" + ) def test_recursive_depth(self, test_env_async): - tmpl = test_env_async.from_string( - "{% for item in seq recursive %}[{{ loop.depth }}:{{ item.a }}" - "{% if item.b %}<{{ loop(item.b) }}>{% endif %}]{% endfor %}" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]" - ) + tmpl = test_env_async.from_string( + "{% for item in seq recursive %}[{{ loop.depth }}:{{ item.a }}" + "{% if item.b %}<{{ loop(item.b) }}>{% endif %}]{% endfor %}" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]" + ) def test_looploop(self, test_env_async): - tmpl = test_env_async.from_string( - """{% for row in table %} + tmpl = test_env_async.from_string( + """{% for row in table %} {%- set rowloop = loop -%} {% for cell in row -%} [{{ rowloop.index }}|{{ loop.index }}] {%- endfor %} - {%- endfor %}""" - ) - assert tmpl.render(table=["ab", "cd"]) == "[1|1][1|2][2|1][2|2]" + {%- endfor %}""" + ) + assert tmpl.render(table=["ab", "cd"]) == "[1|1][1|2][2|1][2|2]" def test_reversed_bug(self, test_env_async): - tmpl = test_env_async.from_string( - "{% for i in items %}{{ i }}" - "{% if not loop.last %}" - ",{% endif %}{% endfor %}" - ) - assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3" + tmpl = test_env_async.from_string( + "{% for i in items %}{{ i }}" + "{% if not loop.last %}" + ",{% endif %}{% endfor %}" + ) + assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3" def test_loop_errors(self, test_env_async): - tmpl = test_env_async.from_string( - """{% for item in [1] if loop.index - == 0 %}...{% endfor %}""" - ) + tmpl = test_env_async.from_string( + """{% for item in [1] if loop.index + == 0 %}...{% endfor %}""" + ) pytest.raises(UndefinedError, tmpl.render) - tmpl = test_env_async.from_string( - """{% for item in [] %}...{% else - %}{{ loop }}{% endfor %}""" - ) - assert tmpl.render() == "" + tmpl = test_env_async.from_string( + """{% for item in [] %}...{% else + %}{{ loop }}{% endfor %}""" + ) + assert tmpl.render() == "" def test_loop_filter(self, test_env_async): - tmpl = test_env_async.from_string( - "{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}" - ) - assert tmpl.render() == "[0][2][4][6][8]" - tmpl = test_env_async.from_string( - """ + tmpl = test_env_async.from_string( + "{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}" + ) + assert tmpl.render() == "[0][2][4][6][8]" + tmpl = test_env_async.from_string( + """ {%- for item in range(10) if item is even %}[{{ - loop.index }}:{{ item }}]{% endfor %}""" - ) - assert tmpl.render() == "[1:0][2:2][3:4][4:6][5:8]" + loop.index }}:{{ item }}]{% endfor %}""" + ) + assert tmpl.render() == "[1:0][2:2][3:4][4:6][5:8]" def test_scoped_special_var(self, test_env_async): t = test_env_async.from_string( - "{% for s in seq %}[{{ loop.first }}{% for c in s %}" - "|{{ loop.first }}{% endfor %}]{% endfor %}" - ) - assert t.render(seq=("ab", "cd")) == "[True|True|False][False|True|False]" + "{% for s in seq %}[{{ loop.first }}{% for c in s %}" + "|{{ loop.first }}{% endfor %}]{% endfor %}" + ) + assert t.render(seq=("ab", "cd")) == "[True|True|False][False|True|False]" def test_scoped_loop_var(self, test_env_async): - t = test_env_async.from_string( - "{% for x in seq %}{{ loop.first }}" - "{% for y in seq %}{% endfor %}{% endfor %}" - ) - assert t.render(seq="ab") == "TrueFalse" - t = test_env_async.from_string( - "{% for x in seq %}{% for y in seq %}" - "{{ loop.first }}{% endfor %}{% endfor %}" - ) - assert t.render(seq="ab") == "TrueFalseTrueFalse" + t = test_env_async.from_string( + "{% for x in seq %}{{ loop.first }}" + "{% for y in seq %}{% endfor %}{% endfor %}" + ) + assert t.render(seq="ab") == "TrueFalse" + t = test_env_async.from_string( + "{% for x in seq %}{% for y in seq %}" + "{{ loop.first }}{% endfor %}{% endfor %}" + ) + assert t.render(seq="ab") == "TrueFalseTrueFalse" def test_recursive_empty_loop_iter(self, test_env_async): - t = test_env_async.from_string( - """ + t = test_env_async.from_string( + """ {%- for item in foo recursive -%}{%- endfor -%} - """ - ) - assert t.render(dict(foo=[])) == "" + """ + ) + assert t.render(dict(foo=[])) == "" def test_call_in_loop(self, test_env_async): - t = test_env_async.from_string( - """ + t = test_env_async.from_string( + """ {%- macro do_something() -%} [{{ caller() }}] {%- endmacro %} @@ -536,29 +536,29 @@ class TestAsyncForLoop: {{ i }} {%- endcall %} {%- endfor -%} - """ - ) - assert t.render() == "[1][2][3]" + """ + ) + assert t.render() == "[1][2][3]" def test_scoping_bug(self, test_env_async): - t = test_env_async.from_string( - """ + t = test_env_async.from_string( + """ {%- for item in foo %}...{{ item }}...{% endfor %} {%- macro item(a) %}...{{ a }}...{% endmacro %} {{- item(2) -}} - """ - ) - assert t.render(foo=(1,)) == "...1......2..." + """ + ) + assert t.render(foo=(1,)) == "...1......2..." def test_unpacking(self, test_env_async): - tmpl = test_env_async.from_string( - "{% for a, b, c in [[1, 2, 3]] %}{{ a }}|{{ b }}|{{ c }}{% endfor %}" - ) - assert tmpl.render() == "1|2|3" + tmpl = test_env_async.from_string( + "{% for a, b, c in [[1, 2, 3]] %}{{ a }}|{{ b }}|{{ c }}{% endfor %}" + ) + assert tmpl.render() == "1|2|3" def test_recursive_loop_filter(self, test_env_async): - t = test_env_async.from_string( - """ + t = test_env_async.from_string( + """ <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> {%- for page in [site.root] if page.url != this recursive %} @@ -566,95 +566,95 @@ class TestAsyncForLoop: {{- loop(page.children) }} {%- endfor %} </urlset> - """ - ) - sm = t.render( - this="/foo", - site={"root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"}]}}, - ) + """ + ) + sm = t.render( + this="/foo", + site={"root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"}]}}, + ) lines = [x.strip() for x in sm.splitlines() if x.strip()] assert lines == [ '<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">', - "<url><loc>/</loc></url>", - "<url><loc>/bar</loc></url>", - "</urlset>", + "<url><loc>/</loc></url>", + "<url><loc>/bar</loc></url>", + "</urlset>", ] def test_nonrecursive_loop_filter(self, test_env_async): - t = test_env_async.from_string( - """ + t = test_env_async.from_string( + """ <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> {%- for page in items if page.url != this %} <url><loc>{{ page.url }}</loc></url> {%- endfor %} </urlset> - """ - ) - sm = t.render( - this="/foo", items=[{"url": "/"}, {"url": "/foo"}, {"url": "/bar"}] - ) + """ + ) + sm = t.render( + this="/foo", items=[{"url": "/"}, {"url": "/foo"}, {"url": "/bar"}] + ) lines = [x.strip() for x in sm.splitlines() if x.strip()] assert lines == [ '<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">', - "<url><loc>/</loc></url>", - "<url><loc>/bar</loc></url>", - "</urlset>", + "<url><loc>/</loc></url>", + "<url><loc>/bar</loc></url>", + "</urlset>", ] def test_bare_async(self, test_env_async): t = test_env_async.from_string('{% extends "header" %}') - assert t.render(foo=42) == "[42|23]" - - def test_awaitable_property_slicing(self, test_env_async): - t = test_env_async.from_string("{% for x in a.b[:1] %}{{ x }}{% endfor %}") - assert t.render(a=dict(b=[1, 2, 3])) == "1" - - -def test_namespace_awaitable(test_env_async): - async def _test(): - t = test_env_async.from_string( - '{% set ns = namespace(foo="Bar") %}{{ ns.foo }}' - ) - actual = await t.render_async() - assert actual == "Bar" - - run(_test()) - - -def test_chainable_undefined_aiter(): - async def _test(): - t = Template( - "{% for x in a['b']['c'] %}{{ x }}{% endfor %}", - enable_async=True, - undefined=ChainableUndefined, - ) - rv = await t.render_async(a={}) - assert rv == "" - - run(_test()) - - -@pytest.fixture -def async_native_env(): - return NativeEnvironment(enable_async=True) - - -def test_native_async(async_native_env): - async def _test(): - t = async_native_env.from_string("{{ x }}") - rv = await t.render_async(x=23) - assert rv == 23 - - run(_test()) - - -def test_native_list_async(async_native_env): - async def _test(): - t = async_native_env.from_string("{{ x }}") - rv = await t.render_async(x=list(range(3))) - assert rv == [0, 1, 2] - - run(_test()) + assert t.render(foo=42) == "[42|23]" + + def test_awaitable_property_slicing(self, test_env_async): + t = test_env_async.from_string("{% for x in a.b[:1] %}{{ x }}{% endfor %}") + assert t.render(a=dict(b=[1, 2, 3])) == "1" + + +def test_namespace_awaitable(test_env_async): + async def _test(): + t = test_env_async.from_string( + '{% set ns = namespace(foo="Bar") %}{{ ns.foo }}' + ) + actual = await t.render_async() + assert actual == "Bar" + + run(_test()) + + +def test_chainable_undefined_aiter(): + async def _test(): + t = Template( + "{% for x in a['b']['c'] %}{{ x }}{% endfor %}", + enable_async=True, + undefined=ChainableUndefined, + ) + rv = await t.render_async(a={}) + assert rv == "" + + run(_test()) + + +@pytest.fixture +def async_native_env(): + return NativeEnvironment(enable_async=True) + + +def test_native_async(async_native_env): + async def _test(): + t = async_native_env.from_string("{{ x }}") + rv = await t.render_async(x=23) + assert rv == 23 + + run(_test()) + + +def test_native_list_async(async_native_env): + async def _test(): + t = async_native_env.from_string("{{ x }}") + rv = await t.render_async(x=list(range(3))) + assert rv == [0, 1, 2] + + run(_test()) diff --git a/contrib/python/Jinja2/py3/tests/test_async_filters.py b/contrib/python/Jinja2/py3/tests/test_async_filters.py index 840e516c81..5d4f332e5d 100644 --- a/contrib/python/Jinja2/py3/tests/test_async_filters.py +++ b/contrib/python/Jinja2/py3/tests/test_async_filters.py @@ -1,253 +1,253 @@ -from collections import namedtuple - -import pytest -from markupsafe import Markup - -from jinja2 import Environment -from jinja2.async_utils import auto_aiter - - -async def make_aiter(iter): - for item in iter: - yield item - - -def mark_dualiter(parameter, factory): - def decorator(f): - return pytest.mark.parametrize( - parameter, [lambda: factory(), lambda: make_aiter(factory())] - )(f) - - return decorator - - -@pytest.fixture -def env_async(): - return Environment(enable_async=True) - - -@mark_dualiter("foo", lambda: range(10)) -def test_first(env_async, foo): - tmpl = env_async.from_string("{{ foo()|first }}") - out = tmpl.render(foo=foo) - assert out == "0" - - -@mark_dualiter( - "items", - lambda: [ - {"foo": 1, "bar": 2}, - {"foo": 2, "bar": 3}, - {"foo": 1, "bar": 1}, - {"foo": 3, "bar": 4}, - ], -) -def test_groupby(env_async, items): - tmpl = env_async.from_string( - """ - {%- for grouper, list in items()|groupby('foo') -%} - {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}| - {%- endfor %}""" - ) - assert tmpl.render(items=items).split("|") == [ - "1: 1, 2: 1, 1", - "2: 2, 3", - "3: 3, 4", - "", - ] - - -@mark_dualiter("items", lambda: [("a", 1), ("a", 2), ("b", 1)]) -def test_groupby_tuple_index(env_async, items): - tmpl = env_async.from_string( - """ - {%- for grouper, list in items()|groupby(0) -%} - {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}| - {%- endfor %}""" - ) - assert tmpl.render(items=items) == "a:1:2|b:1|" - - -def make_articles(): - Date = namedtuple("Date", "day,month,year") - Article = namedtuple("Article", "title,date") - return [ - Article("aha", Date(1, 1, 1970)), - Article("interesting", Date(2, 1, 1970)), - Article("really?", Date(3, 1, 1970)), - Article("totally not", Date(1, 1, 1971)), - ] - - -@mark_dualiter("articles", make_articles) -def test_groupby_multidot(env_async, articles): - tmpl = env_async.from_string( - """ - {%- for year, list in articles()|groupby('date.year') -%} - {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}| - {%- endfor %}""" - ) - assert tmpl.render(articles=articles).split("|") == [ - "1970[aha][interesting][really?]", - "1971[totally not]", - "", - ] - - -@mark_dualiter("int_items", lambda: [1, 2, 3]) -def test_join_env_int(env_async, int_items): - tmpl = env_async.from_string('{{ items()|join("|") }}') - out = tmpl.render(items=int_items) - assert out == "1|2|3" - - -@mark_dualiter("string_items", lambda: ["<foo>", Markup("<span>foo</span>")]) -def test_join_string_list(string_items): - env2 = Environment(autoescape=True, enable_async=True) - tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}') - assert tmpl.render(items=string_items) == "<foo><span>foo</span>" - - -def make_users(): - User = namedtuple("User", "username") - return map(User, ["foo", "bar"]) - - -@mark_dualiter("users", make_users) -def test_join_attribute(env_async, users): - tmpl = env_async.from_string("""{{ users()|join(', ', 'username') }}""") - assert tmpl.render(users=users) == "foo, bar" - - -@mark_dualiter("items", lambda: [1, 2, 3, 4, 5]) -def test_simple_reject(env_async, items): - tmpl = env_async.from_string('{{ items()|reject("odd")|join("|") }}') - assert tmpl.render(items=items) == "2|4" - - -@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5]) -def test_bool_reject(env_async, items): - tmpl = env_async.from_string('{{ items()|reject|join("|") }}') - assert tmpl.render(items=items) == "None|False|0" - - -@mark_dualiter("items", lambda: [1, 2, 3, 4, 5]) -def test_simple_select(env_async, items): - tmpl = env_async.from_string('{{ items()|select("odd")|join("|") }}') - assert tmpl.render(items=items) == "1|3|5" - - -@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5]) -def test_bool_select(env_async, items): - tmpl = env_async.from_string('{{ items()|select|join("|") }}') - assert tmpl.render(items=items) == "1|2|3|4|5" - - -def make_users(): # type: ignore - User = namedtuple("User", "name,is_active") - return [ - User("john", True), - User("jane", True), - User("mike", False), - ] - - -@mark_dualiter("users", make_users) -def test_simple_select_attr(env_async, users): - tmpl = env_async.from_string( - '{{ users()|selectattr("is_active")|map(attribute="name")|join("|") }}' - ) - assert tmpl.render(users=users) == "john|jane" - - -@mark_dualiter("items", lambda: list("123")) -def test_simple_map(env_async, items): - tmpl = env_async.from_string('{{ items()|map("int")|sum }}') - assert tmpl.render(items=items) == "6" - - -def test_map_sum(env_async): # async map + async filter - tmpl = env_async.from_string('{{ [[1,2], [3], [4,5,6]]|map("sum")|list }}') - assert tmpl.render() == "[3, 3, 15]" - - -@mark_dualiter("users", make_users) -def test_attribute_map(env_async, users): - tmpl = env_async.from_string('{{ users()|map(attribute="name")|join("|") }}') - assert tmpl.render(users=users) == "john|jane|mike" - - -def test_empty_map(env_async): - tmpl = env_async.from_string('{{ none|map("upper")|list }}') - assert tmpl.render() == "[]" - - -@mark_dualiter("items", lambda: [1, 2, 3, 4, 5, 6]) -def test_sum(env_async, items): - tmpl = env_async.from_string("""{{ items()|sum }}""") - assert tmpl.render(items=items) == "21" - - -@mark_dualiter("items", lambda: [{"value": 23}, {"value": 1}, {"value": 18}]) -def test_sum_attributes(env_async, items): - tmpl = env_async.from_string("""{{ items()|sum('value') }}""") - assert tmpl.render(items=items) - - -def test_sum_attributes_nested(env_async): - tmpl = env_async.from_string("""{{ values|sum('real.value') }}""") - assert ( - tmpl.render( - values=[ - {"real": {"value": 23}}, - {"real": {"value": 1}}, - {"real": {"value": 18}}, - ] - ) - == "42" - ) - - -def test_sum_attributes_tuple(env_async): - tmpl = env_async.from_string("""{{ values.items()|sum('1') }}""") - assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18}) == "42" - - -@mark_dualiter("items", lambda: range(10)) -def test_slice(env_async, items): - tmpl = env_async.from_string( - "{{ items()|slice(3)|list }}|{{ items()|slice(3, 'X')|list }}" - ) - out = tmpl.render(items=items) - assert out == ( - "[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|" - "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]" - ) - - -def test_custom_async_filter(env_async): - async def customfilter(val): - return str(val) - - env_async.filters["customfilter"] = customfilter - tmpl = env_async.from_string("{{ 'static'|customfilter }} {{ arg|customfilter }}") - out = tmpl.render(arg="dynamic") - assert out == "static dynamic" - - -@mark_dualiter("items", lambda: range(10)) -def test_custom_async_iteratable_filter(env_async, items): - async def customfilter(iterable): - items = [] - async for item in auto_aiter(iterable): - items.append(str(item)) - if len(items) == 3: - break - return ",".join(items) - - env_async.filters["customfilter"] = customfilter - tmpl = env_async.from_string( - "{{ items()|customfilter }} .. {{ [3, 4, 5, 6]|customfilter }}" - ) - out = tmpl.render(items=items) - assert out == "0,1,2 .. 3,4,5" +from collections import namedtuple + +import pytest +from markupsafe import Markup + +from jinja2 import Environment +from jinja2.async_utils import auto_aiter + + +async def make_aiter(iter): + for item in iter: + yield item + + +def mark_dualiter(parameter, factory): + def decorator(f): + return pytest.mark.parametrize( + parameter, [lambda: factory(), lambda: make_aiter(factory())] + )(f) + + return decorator + + +@pytest.fixture +def env_async(): + return Environment(enable_async=True) + + +@mark_dualiter("foo", lambda: range(10)) +def test_first(env_async, foo): + tmpl = env_async.from_string("{{ foo()|first }}") + out = tmpl.render(foo=foo) + assert out == "0" + + +@mark_dualiter( + "items", + lambda: [ + {"foo": 1, "bar": 2}, + {"foo": 2, "bar": 3}, + {"foo": 1, "bar": 1}, + {"foo": 3, "bar": 4}, + ], +) +def test_groupby(env_async, items): + tmpl = env_async.from_string( + """ + {%- for grouper, list in items()|groupby('foo') -%} + {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}| + {%- endfor %}""" + ) + assert tmpl.render(items=items).split("|") == [ + "1: 1, 2: 1, 1", + "2: 2, 3", + "3: 3, 4", + "", + ] + + +@mark_dualiter("items", lambda: [("a", 1), ("a", 2), ("b", 1)]) +def test_groupby_tuple_index(env_async, items): + tmpl = env_async.from_string( + """ + {%- for grouper, list in items()|groupby(0) -%} + {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}| + {%- endfor %}""" + ) + assert tmpl.render(items=items) == "a:1:2|b:1|" + + +def make_articles(): + Date = namedtuple("Date", "day,month,year") + Article = namedtuple("Article", "title,date") + return [ + Article("aha", Date(1, 1, 1970)), + Article("interesting", Date(2, 1, 1970)), + Article("really?", Date(3, 1, 1970)), + Article("totally not", Date(1, 1, 1971)), + ] + + +@mark_dualiter("articles", make_articles) +def test_groupby_multidot(env_async, articles): + tmpl = env_async.from_string( + """ + {%- for year, list in articles()|groupby('date.year') -%} + {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}| + {%- endfor %}""" + ) + assert tmpl.render(articles=articles).split("|") == [ + "1970[aha][interesting][really?]", + "1971[totally not]", + "", + ] + + +@mark_dualiter("int_items", lambda: [1, 2, 3]) +def test_join_env_int(env_async, int_items): + tmpl = env_async.from_string('{{ items()|join("|") }}') + out = tmpl.render(items=int_items) + assert out == "1|2|3" + + +@mark_dualiter("string_items", lambda: ["<foo>", Markup("<span>foo</span>")]) +def test_join_string_list(string_items): + env2 = Environment(autoescape=True, enable_async=True) + tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}') + assert tmpl.render(items=string_items) == "<foo><span>foo</span>" + + +def make_users(): + User = namedtuple("User", "username") + return map(User, ["foo", "bar"]) + + +@mark_dualiter("users", make_users) +def test_join_attribute(env_async, users): + tmpl = env_async.from_string("""{{ users()|join(', ', 'username') }}""") + assert tmpl.render(users=users) == "foo, bar" + + +@mark_dualiter("items", lambda: [1, 2, 3, 4, 5]) +def test_simple_reject(env_async, items): + tmpl = env_async.from_string('{{ items()|reject("odd")|join("|") }}') + assert tmpl.render(items=items) == "2|4" + + +@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5]) +def test_bool_reject(env_async, items): + tmpl = env_async.from_string('{{ items()|reject|join("|") }}') + assert tmpl.render(items=items) == "None|False|0" + + +@mark_dualiter("items", lambda: [1, 2, 3, 4, 5]) +def test_simple_select(env_async, items): + tmpl = env_async.from_string('{{ items()|select("odd")|join("|") }}') + assert tmpl.render(items=items) == "1|3|5" + + +@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5]) +def test_bool_select(env_async, items): + tmpl = env_async.from_string('{{ items()|select|join("|") }}') + assert tmpl.render(items=items) == "1|2|3|4|5" + + +def make_users(): # type: ignore + User = namedtuple("User", "name,is_active") + return [ + User("john", True), + User("jane", True), + User("mike", False), + ] + + +@mark_dualiter("users", make_users) +def test_simple_select_attr(env_async, users): + tmpl = env_async.from_string( + '{{ users()|selectattr("is_active")|map(attribute="name")|join("|") }}' + ) + assert tmpl.render(users=users) == "john|jane" + + +@mark_dualiter("items", lambda: list("123")) +def test_simple_map(env_async, items): + tmpl = env_async.from_string('{{ items()|map("int")|sum }}') + assert tmpl.render(items=items) == "6" + + +def test_map_sum(env_async): # async map + async filter + tmpl = env_async.from_string('{{ [[1,2], [3], [4,5,6]]|map("sum")|list }}') + assert tmpl.render() == "[3, 3, 15]" + + +@mark_dualiter("users", make_users) +def test_attribute_map(env_async, users): + tmpl = env_async.from_string('{{ users()|map(attribute="name")|join("|") }}') + assert tmpl.render(users=users) == "john|jane|mike" + + +def test_empty_map(env_async): + tmpl = env_async.from_string('{{ none|map("upper")|list }}') + assert tmpl.render() == "[]" + + +@mark_dualiter("items", lambda: [1, 2, 3, 4, 5, 6]) +def test_sum(env_async, items): + tmpl = env_async.from_string("""{{ items()|sum }}""") + assert tmpl.render(items=items) == "21" + + +@mark_dualiter("items", lambda: [{"value": 23}, {"value": 1}, {"value": 18}]) +def test_sum_attributes(env_async, items): + tmpl = env_async.from_string("""{{ items()|sum('value') }}""") + assert tmpl.render(items=items) + + +def test_sum_attributes_nested(env_async): + tmpl = env_async.from_string("""{{ values|sum('real.value') }}""") + assert ( + tmpl.render( + values=[ + {"real": {"value": 23}}, + {"real": {"value": 1}}, + {"real": {"value": 18}}, + ] + ) + == "42" + ) + + +def test_sum_attributes_tuple(env_async): + tmpl = env_async.from_string("""{{ values.items()|sum('1') }}""") + assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18}) == "42" + + +@mark_dualiter("items", lambda: range(10)) +def test_slice(env_async, items): + tmpl = env_async.from_string( + "{{ items()|slice(3)|list }}|{{ items()|slice(3, 'X')|list }}" + ) + out = tmpl.render(items=items) + assert out == ( + "[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|" + "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]" + ) + + +def test_custom_async_filter(env_async): + async def customfilter(val): + return str(val) + + env_async.filters["customfilter"] = customfilter + tmpl = env_async.from_string("{{ 'static'|customfilter }} {{ arg|customfilter }}") + out = tmpl.render(arg="dynamic") + assert out == "static dynamic" + + +@mark_dualiter("items", lambda: range(10)) +def test_custom_async_iteratable_filter(env_async, items): + async def customfilter(iterable): + items = [] + async for item in auto_aiter(iterable): + items.append(str(item)) + if len(items) == 3: + break + return ",".join(items) + + env_async.filters["customfilter"] = customfilter + tmpl = env_async.from_string( + "{{ items()|customfilter }} .. {{ [3, 4, 5, 6]|customfilter }}" + ) + out = tmpl.render(items=items) + assert out == "0,1,2 .. 3,4,5" diff --git a/contrib/python/Jinja2/py3/tests/test_bytecode_cache.py b/contrib/python/Jinja2/py3/tests/test_bytecode_cache.py index 6c1009493e..5b9eb0ff69 100644 --- a/contrib/python/Jinja2/py3/tests/test_bytecode_cache.py +++ b/contrib/python/Jinja2/py3/tests/test_bytecode_cache.py @@ -1,26 +1,26 @@ import pytest from jinja2 import Environment -from jinja2.bccache import Bucket -from jinja2.bccache import FileSystemBytecodeCache -from jinja2.bccache import MemcachedBytecodeCache +from jinja2.bccache import Bucket +from jinja2.bccache import FileSystemBytecodeCache +from jinja2.bccache import MemcachedBytecodeCache from jinja2.exceptions import TemplateNotFound @pytest.fixture -def env(package_loader, tmp_path): - bytecode_cache = FileSystemBytecodeCache(str(tmp_path)) - return Environment(loader=package_loader, bytecode_cache=bytecode_cache) +def env(package_loader, tmp_path): + bytecode_cache = FileSystemBytecodeCache(str(tmp_path)) + return Environment(loader=package_loader, bytecode_cache=bytecode_cache) -class TestByteCodeCache: +class TestByteCodeCache: def test_simple(self, env): - tmpl = env.get_template("test.html") - assert tmpl.render().strip() == "BAR" - pytest.raises(TemplateNotFound, env.get_template, "missing.html") + tmpl = env.get_template("test.html") + assert tmpl.render().strip() == "BAR" + pytest.raises(TemplateNotFound, env.get_template, "missing.html") -class MockMemcached: +class MockMemcached: class Error(Exception): pass @@ -43,27 +43,27 @@ class MockMemcached: raise self.Error() -class TestMemcachedBytecodeCache: +class TestMemcachedBytecodeCache: def test_dump_load(self): memcached = MockMemcached() m = MemcachedBytecodeCache(memcached) - b = Bucket(None, "key", "") - b.code = "code" + b = Bucket(None, "key", "") + b.code = "code" m.dump_bytecode(b) - assert memcached.key == "jinja2/bytecode/key" + assert memcached.key == "jinja2/bytecode/key" - b = Bucket(None, "key", "") + b = Bucket(None, "key", "") m.load_bytecode(b) - assert b.code == "code" + assert b.code == "code" def test_exception(self): memcached = MockMemcached() memcached.get = memcached.get_side_effect memcached.set = memcached.set_side_effect m = MemcachedBytecodeCache(memcached) - b = Bucket(None, "key", "") - b.code = "code" + b = Bucket(None, "key", "") + b.code = "code" m.dump_bytecode(b) m.load_bytecode(b) diff --git a/contrib/python/Jinja2/py3/tests/test_compile.py b/contrib/python/Jinja2/py3/tests/test_compile.py index 2f84d00d72..42a773f21c 100644 --- a/contrib/python/Jinja2/py3/tests/test_compile.py +++ b/contrib/python/Jinja2/py3/tests/test_compile.py @@ -1,28 +1,28 @@ -import os -import re - -from jinja2.environment import Environment -from jinja2.loaders import DictLoader - - -def test_filters_deterministic(tmp_path): - src = "".join(f"{{{{ {i}|filter{i} }}}}" for i in range(10)) - env = Environment(loader=DictLoader({"foo": src})) - env.filters.update(dict.fromkeys((f"filter{i}" for i in range(10)), lambda: None)) - env.compile_templates(tmp_path, zip=None) - name = os.listdir(tmp_path)[0] - content = (tmp_path / name).read_text("utf8") - expect = [f"filters['filter{i}']" for i in range(10)] - found = re.findall(r"filters\['filter\d']", content) - assert found == expect - - -def test_import_as_with_context_deterministic(tmp_path): - src = "\n".join(f'{{% import "bar" as bar{i} with context %}}' for i in range(10)) - env = Environment(loader=DictLoader({"foo": src})) - env.compile_templates(tmp_path, zip=None) - name = os.listdir(tmp_path)[0] - content = (tmp_path / name).read_text("utf8") - expect = [f"'bar{i}': " for i in range(10)] - found = re.findall(r"'bar\d': ", content)[:10] - assert found == expect +import os +import re + +from jinja2.environment import Environment +from jinja2.loaders import DictLoader + + +def test_filters_deterministic(tmp_path): + src = "".join(f"{{{{ {i}|filter{i} }}}}" for i in range(10)) + env = Environment(loader=DictLoader({"foo": src})) + env.filters.update(dict.fromkeys((f"filter{i}" for i in range(10)), lambda: None)) + env.compile_templates(tmp_path, zip=None) + name = os.listdir(tmp_path)[0] + content = (tmp_path / name).read_text("utf8") + expect = [f"filters['filter{i}']" for i in range(10)] + found = re.findall(r"filters\['filter\d']", content) + assert found == expect + + +def test_import_as_with_context_deterministic(tmp_path): + src = "\n".join(f'{{% import "bar" as bar{i} with context %}}' for i in range(10)) + env = Environment(loader=DictLoader({"foo": src})) + env.compile_templates(tmp_path, zip=None) + name = os.listdir(tmp_path)[0] + content = (tmp_path / name).read_text("utf8") + expect = [f"'bar{i}': " for i in range(10)] + found = re.findall(r"'bar\d': ", content)[:10] + assert found == expect diff --git a/contrib/python/Jinja2/py3/tests/test_core_tags.py b/contrib/python/Jinja2/py3/tests/test_core_tags.py index fac103e2d8..4bb95e0240 100644 --- a/contrib/python/Jinja2/py3/tests/test_core_tags.py +++ b/contrib/python/Jinja2/py3/tests/test_core_tags.py @@ -1,10 +1,10 @@ -import pytest +import pytest -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import TemplateRuntimeError -from jinja2 import TemplateSyntaxError -from jinja2 import UndefinedError +from jinja2 import DictLoader +from jinja2 import Environment +from jinja2 import TemplateRuntimeError +from jinja2 import TemplateSyntaxError +from jinja2 import UndefinedError @pytest.fixture @@ -12,250 +12,250 @@ def env_trim(): return Environment(trim_blocks=True) -class TestForLoop: +class TestForLoop: def test_simple(self, env): - tmpl = env.from_string("{% for item in seq %}{{ item }}{% endfor %}") - assert tmpl.render(seq=list(range(10))) == "0123456789" + tmpl = env.from_string("{% for item in seq %}{{ item }}{% endfor %}") + assert tmpl.render(seq=list(range(10))) == "0123456789" def test_else(self, env): - tmpl = env.from_string("{% for item in seq %}XXX{% else %}...{% endfor %}") - assert tmpl.render() == "..." + tmpl = env.from_string("{% for item in seq %}XXX{% else %}...{% endfor %}") + assert tmpl.render() == "..." def test_else_scoping_item(self, env): - tmpl = env.from_string("{% for item in [] %}{% else %}{{ item }}{% endfor %}") - assert tmpl.render(item=42) == "42" + tmpl = env.from_string("{% for item in [] %}{% else %}{{ item }}{% endfor %}") + assert tmpl.render(item=42) == "42" def test_empty_blocks(self, env): - tmpl = env.from_string("<{% for item in seq %}{% else %}{% endfor %}>") - assert tmpl.render() == "<>" + tmpl = env.from_string("<{% for item in seq %}{% else %}{% endfor %}>") + assert tmpl.render() == "<>" def test_context_vars(self, env): slist = [42, 24] for seq in [slist, iter(slist), reversed(slist), (_ for _ in slist)]: - tmpl = env.from_string( - """{% for item in seq -%} + tmpl = env.from_string( + """{% for item in seq -%} {{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{ loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{ - loop.length }}###{% endfor %}""" - ) - one, two, _ = tmpl.render(seq=seq).split("###") - ( - one_index, - one_index0, - one_revindex, - one_revindex0, - one_first, - one_last, - one_length, - ) = one.split("|") - ( - two_index, - two_index0, - two_revindex, - two_revindex0, - two_first, - two_last, - two_length, - ) = two.split("|") + loop.length }}###{% endfor %}""" + ) + one, two, _ = tmpl.render(seq=seq).split("###") + ( + one_index, + one_index0, + one_revindex, + one_revindex0, + one_first, + one_last, + one_length, + ) = one.split("|") + ( + two_index, + two_index0, + two_revindex, + two_revindex0, + two_first, + two_last, + two_length, + ) = two.split("|") assert int(one_index) == 1 and int(two_index) == 2 assert int(one_index0) == 0 and int(two_index0) == 1 assert int(one_revindex) == 2 and int(two_revindex) == 1 assert int(one_revindex0) == 1 and int(two_revindex0) == 0 - assert one_first == "True" and two_first == "False" - assert one_last == "False" and two_last == "True" - assert one_length == two_length == "2" + assert one_first == "True" and two_first == "False" + assert one_last == "False" and two_last == "True" + assert one_length == two_length == "2" def test_cycling(self, env): - tmpl = env.from_string( - """{% for item in seq %}{{ + tmpl = env.from_string( + """{% for item in seq %}{{ loop.cycle('<1>', '<2>') }}{% endfor %}{% - for item in seq %}{{ loop.cycle(*through) }}{% endfor %}""" - ) - output = tmpl.render(seq=list(range(4)), through=("<1>", "<2>")) - assert output == "<1><2>" * 4 + for item in seq %}{{ loop.cycle(*through) }}{% endfor %}""" + ) + output = tmpl.render(seq=list(range(4)), through=("<1>", "<2>")) + assert output == "<1><2>" * 4 def test_lookaround(self, env): - tmpl = env.from_string( - """{% for item in seq -%} + tmpl = env.from_string( + """{% for item in seq -%} {{ loop.previtem|default('x') }}-{{ item }}-{{ loop.nextitem|default('x') }}| - {%- endfor %}""" - ) + {%- endfor %}""" + ) output = tmpl.render(seq=list(range(4))) - assert output == "x-0-1|0-1-2|1-2-3|2-3-x|" + assert output == "x-0-1|0-1-2|1-2-3|2-3-x|" def test_changed(self, env): - tmpl = env.from_string( - """{% for item in seq -%} + tmpl = env.from_string( + """{% for item in seq -%} {{ loop.changed(item) }}, - {%- endfor %}""" - ) + {%- endfor %}""" + ) output = tmpl.render(seq=[None, None, 1, 2, 2, 3, 4, 4, 4]) - assert output == "True,False,True,True,False,True,True,False,False," + assert output == "True,False,True,True,False,True,True,False,False," def test_scope(self, env): - tmpl = env.from_string("{% for item in seq %}{% endfor %}{{ item }}") + tmpl = env.from_string("{% for item in seq %}{% endfor %}{{ item }}") output = tmpl.render(seq=list(range(10))) assert not output def test_varlen(self, env): - tmpl = env.from_string("{% for item in iter %}{{ item }}{% endfor %}") - output = tmpl.render(iter=range(5)) - assert output == "01234" + tmpl = env.from_string("{% for item in iter %}{{ item }}{% endfor %}") + output = tmpl.render(iter=range(5)) + assert output == "01234" def test_noniter(self, env): - tmpl = env.from_string("{% for item in none %}...{% endfor %}") + tmpl = env.from_string("{% for item in none %}...{% endfor %}") pytest.raises(TypeError, tmpl.render) def test_recursive(self, env): - tmpl = env.from_string( - """{% for item in seq recursive -%} + tmpl = env.from_string( + """{% for item in seq recursive -%} [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}""" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[1<[1][2]>][2<[1][2]>][3<[a]>]" - ) + {%- endfor %}""" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[1<[1][2]>][2<[1][2]>][3<[a]>]" + ) def test_recursive_lookaround(self, env): - tmpl = env.from_string( - """{% for item in seq recursive -%} + tmpl = env.from_string( + """{% for item in seq recursive -%} [{{ loop.previtem.a if loop.previtem is defined else 'x' }}.{{ item.a }}.{{ loop.nextitem.a if loop.nextitem is defined else 'x' }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}""" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]" - ) + {%- endfor %}""" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]" + ) def test_recursive_depth0(self, env): - tmpl = env.from_string( - """{% for item in seq recursive -%} - [{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}""" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]" - ) + tmpl = env.from_string( + """{% for item in seq recursive -%} + [{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] + {%- endfor %}""" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]" + ) def test_recursive_depth(self, env): - tmpl = env.from_string( - """{% for item in seq recursive -%} - [{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] - {%- endfor %}""" - ) - assert ( - tmpl.render( - seq=[ - dict(a=1, b=[dict(a=1), dict(a=2)]), - dict(a=2, b=[dict(a=1), dict(a=2)]), - dict(a=3, b=[dict(a="a")]), - ] - ) - == "[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]" - ) + tmpl = env.from_string( + """{% for item in seq recursive -%} + [{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] + {%- endfor %}""" + ) + assert ( + tmpl.render( + seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a="a")]), + ] + ) + == "[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]" + ) def test_looploop(self, env): - tmpl = env.from_string( - """{% for row in table %} + tmpl = env.from_string( + """{% for row in table %} {%- set rowloop = loop -%} {% for cell in row -%} [{{ rowloop.index }}|{{ loop.index }}] {%- endfor %} - {%- endfor %}""" - ) - assert tmpl.render(table=["ab", "cd"]) == "[1|1][1|2][2|1][2|2]" + {%- endfor %}""" + ) + assert tmpl.render(table=["ab", "cd"]) == "[1|1][1|2][2|1][2|2]" def test_reversed_bug(self, env): - tmpl = env.from_string( - "{% for i in items %}{{ i }}" - "{% if not loop.last %}" - ",{% endif %}{% endfor %}" - ) - assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3" + tmpl = env.from_string( + "{% for i in items %}{{ i }}" + "{% if not loop.last %}" + ",{% endif %}{% endfor %}" + ) + assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3" def test_loop_errors(self, env): - tmpl = env.from_string( - """{% for item in [1] if loop.index - == 0 %}...{% endfor %}""" - ) + tmpl = env.from_string( + """{% for item in [1] if loop.index + == 0 %}...{% endfor %}""" + ) pytest.raises(UndefinedError, tmpl.render) - tmpl = env.from_string( - """{% for item in [] %}...{% else - %}{{ loop }}{% endfor %}""" - ) - assert tmpl.render() == "" + tmpl = env.from_string( + """{% for item in [] %}...{% else + %}{{ loop }}{% endfor %}""" + ) + assert tmpl.render() == "" def test_loop_filter(self, env): - tmpl = env.from_string( - "{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}" - ) - assert tmpl.render() == "[0][2][4][6][8]" - tmpl = env.from_string( - """ + tmpl = env.from_string( + "{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}" + ) + assert tmpl.render() == "[0][2][4][6][8]" + tmpl = env.from_string( + """ {%- for item in range(10) if item is even %}[{{ - loop.index }}:{{ item }}]{% endfor %}""" - ) - assert tmpl.render() == "[1:0][2:2][3:4][4:6][5:8]" + loop.index }}:{{ item }}]{% endfor %}""" + ) + assert tmpl.render() == "[1:0][2:2][3:4][4:6][5:8]" def test_loop_unassignable(self, env): - pytest.raises( - TemplateSyntaxError, env.from_string, "{% for loop in seq %}...{% endfor %}" - ) + pytest.raises( + TemplateSyntaxError, env.from_string, "{% for loop in seq %}...{% endfor %}" + ) def test_scoped_special_var(self, env): t = env.from_string( - "{% for s in seq %}[{{ loop.first }}{% for c in s %}" - "|{{ loop.first }}{% endfor %}]{% endfor %}" - ) - assert t.render(seq=("ab", "cd")) == "[True|True|False][False|True|False]" + "{% for s in seq %}[{{ loop.first }}{% for c in s %}" + "|{{ loop.first }}{% endfor %}]{% endfor %}" + ) + assert t.render(seq=("ab", "cd")) == "[True|True|False][False|True|False]" def test_scoped_loop_var(self, env): - t = env.from_string( - "{% for x in seq %}{{ loop.first }}" - "{% for y in seq %}{% endfor %}{% endfor %}" - ) - assert t.render(seq="ab") == "TrueFalse" - t = env.from_string( - "{% for x in seq %}{% for y in seq %}" - "{{ loop.first }}{% endfor %}{% endfor %}" - ) - assert t.render(seq="ab") == "TrueFalseTrueFalse" + t = env.from_string( + "{% for x in seq %}{{ loop.first }}" + "{% for y in seq %}{% endfor %}{% endfor %}" + ) + assert t.render(seq="ab") == "TrueFalse" + t = env.from_string( + "{% for x in seq %}{% for y in seq %}" + "{{ loop.first }}{% endfor %}{% endfor %}" + ) + assert t.render(seq="ab") == "TrueFalseTrueFalse" def test_recursive_empty_loop_iter(self, env): - t = env.from_string( - """ + t = env.from_string( + """ {%- for item in foo recursive -%}{%- endfor -%} - """ - ) - assert t.render(dict(foo=[])) == "" + """ + ) + assert t.render(dict(foo=[])) == "" def test_call_in_loop(self, env): - t = env.from_string( - """ + t = env.from_string( + """ {%- macro do_something() -%} [{{ caller() }}] {%- endmacro %} @@ -265,173 +265,173 @@ class TestForLoop: {{ i }} {%- endcall %} {%- endfor -%} - """ - ) - assert t.render() == "[1][2][3]" + """ + ) + assert t.render() == "[1][2][3]" def test_scoping_bug(self, env): - t = env.from_string( - """ + t = env.from_string( + """ {%- for item in foo %}...{{ item }}...{% endfor %} {%- macro item(a) %}...{{ a }}...{% endmacro %} {{- item(2) -}} - """ - ) - assert t.render(foo=(1,)) == "...1......2..." + """ + ) + assert t.render(foo=(1,)) == "...1......2..." def test_unpacking(self, env): - tmpl = env.from_string( - "{% for a, b, c in [[1, 2, 3]] %}{{ a }}|{{ b }}|{{ c }}{% endfor %}" - ) - assert tmpl.render() == "1|2|3" + tmpl = env.from_string( + "{% for a, b, c in [[1, 2, 3]] %}{{ a }}|{{ b }}|{{ c }}{% endfor %}" + ) + assert tmpl.render() == "1|2|3" def test_intended_scoping_with_set(self, env): - tmpl = env.from_string( - "{% for item in seq %}{{ x }}{% set x = item %}{{ x }}{% endfor %}" - ) - assert tmpl.render(x=0, seq=[1, 2, 3]) == "010203" + tmpl = env.from_string( + "{% for item in seq %}{{ x }}{% set x = item %}{{ x }}{% endfor %}" + ) + assert tmpl.render(x=0, seq=[1, 2, 3]) == "010203" - tmpl = env.from_string( - "{% set x = 9 %}{% for item in seq %}{{ x }}" - "{% set x = item %}{{ x }}{% endfor %}" - ) - assert tmpl.render(x=0, seq=[1, 2, 3]) == "919293" + tmpl = env.from_string( + "{% set x = 9 %}{% for item in seq %}{{ x }}" + "{% set x = item %}{{ x }}{% endfor %}" + ) + assert tmpl.render(x=0, seq=[1, 2, 3]) == "919293" -class TestIfCondition: +class TestIfCondition: def test_simple(self, env): - tmpl = env.from_string("""{% if true %}...{% endif %}""") - assert tmpl.render() == "..." + tmpl = env.from_string("""{% if true %}...{% endif %}""") + assert tmpl.render() == "..." def test_elif(self, env): - tmpl = env.from_string( - """{% if false %}XXX{% elif true - %}...{% else %}XXX{% endif %}""" - ) - assert tmpl.render() == "..." + tmpl = env.from_string( + """{% if false %}XXX{% elif true + %}...{% else %}XXX{% endif %}""" + ) + assert tmpl.render() == "..." def test_elif_deep(self, env): - elifs = "\n".join(f"{{% elif a == {i} %}}{i}" for i in range(1, 1000)) - tmpl = env.from_string(f"{{% if a == 0 %}}0{elifs}{{% else %}}x{{% endif %}}") + elifs = "\n".join(f"{{% elif a == {i} %}}{i}" for i in range(1, 1000)) + tmpl = env.from_string(f"{{% if a == 0 %}}0{elifs}{{% else %}}x{{% endif %}}") for x in (0, 10, 999): assert tmpl.render(a=x).strip() == str(x) - assert tmpl.render(a=1000).strip() == "x" + assert tmpl.render(a=1000).strip() == "x" def test_else(self, env): - tmpl = env.from_string("{% if false %}XXX{% else %}...{% endif %}") - assert tmpl.render() == "..." + tmpl = env.from_string("{% if false %}XXX{% else %}...{% endif %}") + assert tmpl.render() == "..." def test_empty(self, env): - tmpl = env.from_string("[{% if true %}{% else %}{% endif %}]") - assert tmpl.render() == "[]" + tmpl = env.from_string("[{% if true %}{% else %}{% endif %}]") + assert tmpl.render() == "[]" def test_complete(self, env): - tmpl = env.from_string( - "{% if a %}A{% elif b %}B{% elif c == d %}C{% else %}D{% endif %}" - ) - assert tmpl.render(a=0, b=False, c=42, d=42.0) == "C" + tmpl = env.from_string( + "{% if a %}A{% elif b %}B{% elif c == d %}C{% else %}D{% endif %}" + ) + assert tmpl.render(a=0, b=False, c=42, d=42.0) == "C" def test_no_scope(self, env): - tmpl = env.from_string("{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}") - assert tmpl.render(a=True) == "1" - tmpl = env.from_string("{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}") - assert tmpl.render() == "1" + tmpl = env.from_string("{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}") + assert tmpl.render(a=True) == "1" + tmpl = env.from_string("{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}") + assert tmpl.render() == "1" -class TestMacros: +class TestMacros: def test_simple(self, env_trim): - tmpl = env_trim.from_string( - """\ + tmpl = env_trim.from_string( + """\ {% macro say_hello(name) %}Hello {{ name }}!{% endmacro %} -{{ say_hello('Peter') }}""" - ) - assert tmpl.render() == "Hello Peter!" +{{ say_hello('Peter') }}""" + ) + assert tmpl.render() == "Hello Peter!" def test_scoping(self, env_trim): - tmpl = env_trim.from_string( - """\ + tmpl = env_trim.from_string( + """\ {% macro level1(data1) %} {% macro level2(data2) %}{{ data1 }}|{{ data2 }}{% endmacro %} {{ level2('bar') }}{% endmacro %} -{{ level1('foo') }}""" - ) - assert tmpl.render() == "foo|bar" +{{ level1('foo') }}""" + ) + assert tmpl.render() == "foo|bar" def test_arguments(self, env_trim): - tmpl = env_trim.from_string( - """\ + tmpl = env_trim.from_string( + """\ {% macro m(a, b, c='c', d='d') %}{{ a }}|{{ b }}|{{ c }}|{{ d }}{% endmacro %} -{{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}""" - ) - assert tmpl.render() == "||c|d|a||c|d|a|b|c|d|1|2|3|d" +{{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}""" + ) + assert tmpl.render() == "||c|d|a||c|d|a|b|c|d|1|2|3|d" def test_arguments_defaults_nonsense(self, env_trim): - pytest.raises( - TemplateSyntaxError, - env_trim.from_string, - """\ -{% macro m(a, b=1, c) %}a={{ a }}, b={{ b }}, c={{ c }}{% endmacro %}""", - ) + pytest.raises( + TemplateSyntaxError, + env_trim.from_string, + """\ +{% macro m(a, b=1, c) %}a={{ a }}, b={{ b }}, c={{ c }}{% endmacro %}""", + ) def test_caller_defaults_nonsense(self, env_trim): - pytest.raises( - TemplateSyntaxError, - env_trim.from_string, - """\ + pytest.raises( + TemplateSyntaxError, + env_trim.from_string, + """\ {% macro a() %}{{ caller() }}{% endmacro %} -{% call(x, y=1, z) a() %}{% endcall %}""", - ) +{% call(x, y=1, z) a() %}{% endcall %}""", + ) def test_varargs(self, env_trim): - tmpl = env_trim.from_string( - """\ + tmpl = env_trim.from_string( + """\ {% macro test() %}{{ varargs|join('|') }}{% endmacro %}\ -{{ test(1, 2, 3) }}""" - ) - assert tmpl.render() == "1|2|3" +{{ test(1, 2, 3) }}""" + ) + assert tmpl.render() == "1|2|3" def test_simple_call(self, env_trim): - tmpl = env_trim.from_string( - """\ + tmpl = env_trim.from_string( + """\ {% macro test() %}[[{{ caller() }}]]{% endmacro %}\ -{% call test() %}data{% endcall %}""" - ) - assert tmpl.render() == "[[data]]" +{% call test() %}data{% endcall %}""" + ) + assert tmpl.render() == "[[data]]" def test_complex_call(self, env_trim): - tmpl = env_trim.from_string( - """\ + tmpl = env_trim.from_string( + """\ {% macro test() %}[[{{ caller('data') }}]]{% endmacro %}\ -{% call(data) test() %}{{ data }}{% endcall %}""" - ) - assert tmpl.render() == "[[data]]" +{% call(data) test() %}{{ data }}{% endcall %}""" + ) + assert tmpl.render() == "[[data]]" def test_caller_undefined(self, env_trim): - tmpl = env_trim.from_string( - """\ + tmpl = env_trim.from_string( + """\ {% set caller = 42 %}\ {% macro test() %}{{ caller is not defined }}{% endmacro %}\ -{{ test() }}""" - ) - assert tmpl.render() == "True" +{{ test() }}""" + ) + assert tmpl.render() == "True" def test_include(self, env_trim): env_trim = Environment( - loader=DictLoader( - {"include": "{% macro test(foo) %}[{{ foo }}]{% endmacro %}"} - ) + loader=DictLoader( + {"include": "{% macro test(foo) %}[{{ foo }}]{% endmacro %}"} + ) ) - tmpl = env_trim.from_string('{% from "include" import test %}{{ test("foo") }}') - assert tmpl.render() == "[foo]" + tmpl = env_trim.from_string('{% from "include" import test %}{{ test("foo") }}') + assert tmpl.render() == "[foo]" def test_macro_api(self, env_trim): tmpl = env_trim.from_string( - "{% macro foo(a, b) %}{% endmacro %}" - "{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}" - "{% macro baz() %}{{ caller() }}{% endmacro %}" - ) - assert tmpl.module.foo.arguments == ("a", "b") - assert tmpl.module.foo.name == "foo" + "{% macro foo(a, b) %}{% endmacro %}" + "{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}" + "{% macro baz() %}{{ caller() }}{% endmacro %}" + ) + assert tmpl.module.foo.arguments == ("a", "b") + assert tmpl.module.foo.name == "foo" assert not tmpl.module.foo.caller assert not tmpl.module.foo.catch_kwargs assert not tmpl.module.foo.catch_varargs @@ -442,154 +442,154 @@ class TestMacros: assert tmpl.module.baz.caller def test_callself(self, env_trim): - tmpl = env_trim.from_string( - "{% macro foo(x) %}{{ x }}{% if x > 1 %}|" - "{{ foo(x - 1) }}{% endif %}{% endmacro %}" - "{{ foo(5) }}" - ) - assert tmpl.render() == "5|4|3|2|1" + tmpl = env_trim.from_string( + "{% macro foo(x) %}{{ x }}{% if x > 1 %}|" + "{{ foo(x - 1) }}{% endif %}{% endmacro %}" + "{{ foo(5) }}" + ) + assert tmpl.render() == "5|4|3|2|1" def test_macro_defaults_self_ref(self, env): - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {%- set x = 42 %} {%- macro m(a, b=x, x=23) %}{{ a }}|{{ b }}|{{ x }}{% endmacro -%} - """ - ) - assert tmpl.module.m(1) == "1||23" - assert tmpl.module.m(1, 2) == "1|2|23" - assert tmpl.module.m(1, 2, 3) == "1|2|3" - assert tmpl.module.m(1, x=7) == "1|7|7" + """ + ) + assert tmpl.module.m(1) == "1||23" + assert tmpl.module.m(1, 2) == "1|2|23" + assert tmpl.module.m(1, 2, 3) == "1|2|3" + assert tmpl.module.m(1, x=7) == "1|7|7" -class TestSet: +class TestSet: def test_normal(self, env_trim): - tmpl = env_trim.from_string("{% set foo = 1 %}{{ foo }}") - assert tmpl.render() == "1" + tmpl = env_trim.from_string("{% set foo = 1 %}{{ foo }}") + assert tmpl.render() == "1" assert tmpl.module.foo == 1 def test_block(self, env_trim): - tmpl = env_trim.from_string("{% set foo %}42{% endset %}{{ foo }}") - assert tmpl.render() == "42" - assert tmpl.module.foo == "42" + tmpl = env_trim.from_string("{% set foo %}42{% endset %}{{ foo }}") + assert tmpl.render() == "42" + assert tmpl.module.foo == "42" def test_block_escaping(self): env = Environment(autoescape=True) - tmpl = env.from_string( - "{% set foo %}<em>{{ test }}</em>{% endset %}foo: {{ foo }}" - ) - assert tmpl.render(test="<unsafe>") == "foo: <em><unsafe></em>" + tmpl = env.from_string( + "{% set foo %}<em>{{ test }}</em>{% endset %}foo: {{ foo }}" + ) + assert tmpl.render(test="<unsafe>") == "foo: <em><unsafe></em>" def test_set_invalid(self, env_trim): - pytest.raises( - TemplateSyntaxError, env_trim.from_string, "{% set foo['bar'] = 1 %}" - ) - tmpl = env_trim.from_string("{% set foo.bar = 1 %}") + pytest.raises( + TemplateSyntaxError, env_trim.from_string, "{% set foo['bar'] = 1 %}" + ) + tmpl = env_trim.from_string("{% set foo.bar = 1 %}") exc_info = pytest.raises(TemplateRuntimeError, tmpl.render, foo={}) - assert "non-namespace object" in exc_info.value.message + assert "non-namespace object" in exc_info.value.message def test_namespace_redefined(self, env_trim): - tmpl = env_trim.from_string("{% set ns = namespace() %}{% set ns.bar = 'hi' %}") - exc_info = pytest.raises(TemplateRuntimeError, tmpl.render, namespace=dict) - assert "non-namespace object" in exc_info.value.message + tmpl = env_trim.from_string("{% set ns = namespace() %}{% set ns.bar = 'hi' %}") + exc_info = pytest.raises(TemplateRuntimeError, tmpl.render, namespace=dict) + assert "non-namespace object" in exc_info.value.message def test_namespace(self, env_trim): - tmpl = env_trim.from_string( - "{% set ns = namespace() %}{% set ns.bar = '42' %}{{ ns.bar }}" - ) - assert tmpl.render() == "42" + tmpl = env_trim.from_string( + "{% set ns = namespace() %}{% set ns.bar = '42' %}{{ ns.bar }}" + ) + assert tmpl.render() == "42" def test_namespace_block(self, env_trim): - tmpl = env_trim.from_string( - "{% set ns = namespace() %}{% set ns.bar %}42{% endset %}{{ ns.bar }}" - ) - assert tmpl.render() == "42" + tmpl = env_trim.from_string( + "{% set ns = namespace() %}{% set ns.bar %}42{% endset %}{{ ns.bar }}" + ) + assert tmpl.render() == "42" def test_init_namespace(self, env_trim): - tmpl = env_trim.from_string( - "{% set ns = namespace(d, self=37) %}" - "{% set ns.b = 42 %}" - "{{ ns.a }}|{{ ns.self }}|{{ ns.b }}" - ) - assert tmpl.render(d={"a": 13}) == "13|37|42" + tmpl = env_trim.from_string( + "{% set ns = namespace(d, self=37) %}" + "{% set ns.b = 42 %}" + "{{ ns.a }}|{{ ns.self }}|{{ ns.b }}" + ) + assert tmpl.render(d={"a": 13}) == "13|37|42" def test_namespace_loop(self, env_trim): - tmpl = env_trim.from_string( - "{% set ns = namespace(found=false) %}" - "{% for x in range(4) %}" - "{% if x == v %}" - "{% set ns.found = true %}" - "{% endif %}" - "{% endfor %}" - "{{ ns.found }}" - ) - assert tmpl.render(v=3) == "True" - assert tmpl.render(v=4) == "False" + tmpl = env_trim.from_string( + "{% set ns = namespace(found=false) %}" + "{% for x in range(4) %}" + "{% if x == v %}" + "{% set ns.found = true %}" + "{% endif %}" + "{% endfor %}" + "{{ ns.found }}" + ) + assert tmpl.render(v=3) == "True" + assert tmpl.render(v=4) == "False" def test_namespace_macro(self, env_trim): - tmpl = env_trim.from_string( - "{% set ns = namespace() %}" - "{% set ns.a = 13 %}" - "{% macro magic(x) %}" - "{% set x.b = 37 %}" - "{% endmacro %}" - "{{ magic(ns) }}" - "{{ ns.a }}|{{ ns.b }}" - ) - assert tmpl.render() == "13|37" + tmpl = env_trim.from_string( + "{% set ns = namespace() %}" + "{% set ns.a = 13 %}" + "{% macro magic(x) %}" + "{% set x.b = 37 %}" + "{% endmacro %}" + "{{ magic(ns) }}" + "{{ ns.a }}|{{ ns.b }}" + ) + assert tmpl.render() == "13|37" def test_block_escaping_filtered(self): env = Environment(autoescape=True) - tmpl = env.from_string( - "{% set foo | trim %}<em>{{ test }}</em> {% endset %}foo: {{ foo }}" - ) - assert tmpl.render(test="<unsafe>") == "foo: <em><unsafe></em>" + tmpl = env.from_string( + "{% set foo | trim %}<em>{{ test }}</em> {% endset %}foo: {{ foo }}" + ) + assert tmpl.render(test="<unsafe>") == "foo: <em><unsafe></em>" def test_block_filtered(self, env_trim): tmpl = env_trim.from_string( - "{% set foo | trim | length | string %} 42 {% endset %}{{ foo }}" - ) - assert tmpl.render() == "2" - assert tmpl.module.foo == "2" + "{% set foo | trim | length | string %} 42 {% endset %}{{ foo }}" + ) + assert tmpl.render() == "2" + assert tmpl.module.foo == "2" def test_block_filtered_set(self, env_trim): def _myfilter(val, arg): - assert arg == " xxx " + assert arg == " xxx " return val - - env_trim.filters["myfilter"] = _myfilter + + env_trim.filters["myfilter"] = _myfilter tmpl = env_trim.from_string( '{% set a = " xxx " %}' - "{% set foo | myfilter(a) | trim | length | string %}" + "{% set foo | myfilter(a) | trim | length | string %}" ' {% set b = " yy " %} 42 {{ a }}{{ b }} ' - "{% endset %}" - "{{ foo }}" - ) - assert tmpl.render() == "11" - assert tmpl.module.foo == "11" + "{% endset %}" + "{{ foo }}" + ) + assert tmpl.render() == "11" + assert tmpl.module.foo == "11" -class TestWith: +class TestWith: def test_with(self, env): - tmpl = env.from_string( - """\ + tmpl = env.from_string( + """\ {% with a=42, b=23 -%} {{ a }} = {{ b }} {% endwith -%} {{ a }} = {{ b }}\ - """ - ) - assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] == [ - "42 = 23", - "1 = 2", - ] + """ + ) + assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] == [ + "42 = 23", + "1 = 2", + ] def test_with_argument_scoping(self, env): - tmpl = env.from_string( - """\ + tmpl = env.from_string( + """\ {%- with a=1, b=2, c=b, d=e, e=5 -%} {{ a }}|{{ b }}|{{ c }}|{{ d }}|{{ e }} {%- endwith -%} - """ - ) - assert tmpl.render(b=3, e=4) == "1|2|3|4|5" + """ + ) + assert tmpl.render(b=3, e=4) == "1|2|3|4|5" diff --git a/contrib/python/Jinja2/py3/tests/test_debug.py b/contrib/python/Jinja2/py3/tests/test_debug.py index 901bb525c2..1cb931cfc1 100644 --- a/contrib/python/Jinja2/py3/tests/test_debug.py +++ b/contrib/python/Jinja2/py3/tests/test_debug.py @@ -1,40 +1,40 @@ -import pickle -import re -from traceback import format_exception +import pickle +import re +from traceback import format_exception import pytest -from jinja2 import ChoiceLoader -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import TemplateSyntaxError +from jinja2 import ChoiceLoader +from jinja2 import DictLoader +from jinja2 import Environment +from jinja2 import TemplateSyntaxError @pytest.fixture def fs_env(filesystem_loader): - """returns a new environment.""" + """returns a new environment.""" return Environment(loader=filesystem_loader) -class TestDebug: +class TestDebug: def assert_traceback_matches(self, callback, expected_tb): - with pytest.raises(Exception) as exc_info: + with pytest.raises(Exception) as exc_info: callback() - tb = format_exception(exc_info.type, exc_info.value, exc_info.tb) - m = re.search(expected_tb.strip(), "".join(tb)) - assert ( - m is not None - ), "Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}" - + tb = format_exception(exc_info.type, exc_info.value, exc_info.tb) + m = re.search(expected_tb.strip(), "".join(tb)) + assert ( + m is not None + ), "Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}" + def test_runtime_error(self, fs_env): def test(): tmpl.render(fail=lambda: 1 / 0) - - tmpl = fs_env.get_template("broken.html") - self.assert_traceback_matches( - test, - r""" + + tmpl = fs_env.get_template("broken.html") + self.assert_traceback_matches( + test, + r""" File ".*?broken.html", line 2, in (top-level template code|<module>) \{\{ fail\(\) \}\}( \^{12})? @@ -42,76 +42,76 @@ class TestDebug: tmpl\.render\(fail=lambda: 1 / 0\)( ~~\^~~)? ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero -""", - ) +""", + ) def test_syntax_error(self, fs_env): - # The trailing .*? is for PyPy 2 and 3, which don't seem to - # clear the exception's original traceback, leaving the syntax - # error in the middle of other compiler frames. - self.assert_traceback_matches( - lambda: fs_env.get_template("syntaxerror.html"), - """(?sm) + # The trailing .*? is for PyPy 2 and 3, which don't seem to + # clear the exception's original traceback, leaving the syntax + # error in the middle of other compiler frames. + self.assert_traceback_matches( + lambda: fs_env.get_template("syntaxerror.html"), + """(?sm) File ".*?syntaxerror.html", line 4, in (template|<module>) - \\{% endif %\\}.*? -(jinja2\\.exceptions\\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja \ -was looking for the following tags: 'endfor' or 'else'. The innermost block that needs \ -to be closed is 'for'. - """, - ) + \\{% endif %\\}.*? +(jinja2\\.exceptions\\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja \ +was looking for the following tags: 'endfor' or 'else'. The innermost block that needs \ +to be closed is 'for'. + """, + ) def test_regular_syntax_error(self, fs_env): def test(): - raise TemplateSyntaxError("wtf", 42) - - self.assert_traceback_matches( - test, - r""" + raise TemplateSyntaxError("wtf", 42) + + self.assert_traceback_matches( + test, + r""" File ".*debug.pyc?", line \d+, in test raise TemplateSyntaxError\("wtf", 42\)( \^{36})? (jinja2\.exceptions\.)?TemplateSyntaxError: wtf - line 42""", - ) - - def test_pickleable_syntax_error(self, fs_env): - original = TemplateSyntaxError("bad template", 42, "test", "test.txt") - unpickled = pickle.loads(pickle.dumps(original)) - assert str(original) == str(unpickled) - assert original.name == unpickled.name - - def test_include_syntax_error_source(self, filesystem_loader): - e = Environment( - loader=ChoiceLoader( - [ - filesystem_loader, - DictLoader({"inc": "a\n{% include 'syntaxerror.html' %}\nb"}), - ] - ) - ) - t = e.get_template("inc") - - with pytest.raises(TemplateSyntaxError) as exc_info: - t.render() - - assert exc_info.value.source is not None - + line 42""", + ) + + def test_pickleable_syntax_error(self, fs_env): + original = TemplateSyntaxError("bad template", 42, "test", "test.txt") + unpickled = pickle.loads(pickle.dumps(original)) + assert str(original) == str(unpickled) + assert original.name == unpickled.name + + def test_include_syntax_error_source(self, filesystem_loader): + e = Environment( + loader=ChoiceLoader( + [ + filesystem_loader, + DictLoader({"inc": "a\n{% include 'syntaxerror.html' %}\nb"}), + ] + ) + ) + t = e.get_template("inc") + + with pytest.raises(TemplateSyntaxError) as exc_info: + t.render() + + assert exc_info.value.source is not None + def test_local_extraction(self): - from jinja2.debug import get_template_locals + from jinja2.debug import get_template_locals from jinja2.runtime import missing - - locals = get_template_locals( - { - "l_0_foo": 42, - "l_1_foo": 23, - "l_2_foo": 13, - "l_0_bar": 99, - "l_1_bar": missing, - "l_0_baz": missing, - } - ) - assert locals == {"foo": 13, "bar": 99} - - def test_get_corresponding_lineno_traceback(self, fs_env): - tmpl = fs_env.get_template("test.html") - assert tmpl.get_corresponding_lineno(1) == 1 + + locals = get_template_locals( + { + "l_0_foo": 42, + "l_1_foo": 23, + "l_2_foo": 13, + "l_0_bar": 99, + "l_1_bar": missing, + "l_0_baz": missing, + } + ) + assert locals == {"foo": 13, "bar": 99} + + def test_get_corresponding_lineno_traceback(self, fs_env): + tmpl = fs_env.get_template("test.html") + assert tmpl.get_corresponding_lineno(1) == 1 diff --git a/contrib/python/Jinja2/py3/tests/test_ext.py b/contrib/python/Jinja2/py3/tests/test_ext.py index 9d4369ea5e..b54e905ffd 100644 --- a/contrib/python/Jinja2/py3/tests/test_ext.py +++ b/contrib/python/Jinja2/py3/tests/test_ext.py @@ -1,191 +1,191 @@ -import re -from io import BytesIO +import re +from io import BytesIO import pytest -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import nodes -from jinja2 import pass_context +from jinja2 import DictLoader +from jinja2 import Environment +from jinja2 import nodes +from jinja2 import pass_context from jinja2.exceptions import TemplateAssertionError from jinja2.ext import Extension -from jinja2.lexer import count_newlines -from jinja2.lexer import Token +from jinja2.lexer import count_newlines +from jinja2.lexer import Token importable_object = 23 -_gettext_re = re.compile(r"_\((.*?)\)", re.DOTALL) +_gettext_re = re.compile(r"_\((.*?)\)", re.DOTALL) i18n_templates = { - "default.html": '<title>{{ page_title|default(_("missing")) }}</title>' - "{% block body %}{% endblock %}", - "child.html": '{% extends "default.html" %}{% block body %}' - "{% trans %}watch out{% endtrans %}{% endblock %}", - "plural.html": "{% trans user_count %}One user online{% pluralize %}" - "{{ user_count }} users online{% endtrans %}", - "plural2.html": "{% trans user_count=get_user_count() %}{{ user_count }}s" - "{% pluralize %}{{ user_count }}p{% endtrans %}", - "stringformat.html": '{{ _("User: %(num)s")|format(num=user_count) }}', + "default.html": '<title>{{ page_title|default(_("missing")) }}</title>' + "{% block body %}{% endblock %}", + "child.html": '{% extends "default.html" %}{% block body %}' + "{% trans %}watch out{% endtrans %}{% endblock %}", + "plural.html": "{% trans user_count %}One user online{% pluralize %}" + "{{ user_count }} users online{% endtrans %}", + "plural2.html": "{% trans user_count=get_user_count() %}{{ user_count }}s" + "{% pluralize %}{{ user_count }}p{% endtrans %}", + "stringformat.html": '{{ _("User: %(num)s")|format(num=user_count) }}', } newstyle_i18n_templates = { - "default.html": '<title>{{ page_title|default(_("missing")) }}</title>' - "{% block body %}{% endblock %}", - "child.html": '{% extends "default.html" %}{% block body %}' - "{% trans %}watch out{% endtrans %}{% endblock %}", - "plural.html": "{% trans user_count %}One user online{% pluralize %}" - "{{ user_count }} users online{% endtrans %}", - "stringformat.html": '{{ _("User: %(num)s", num=user_count) }}', - "ngettext.html": '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}', - "ngettext_long.html": "{% trans num=apples %}{{ num }} apple{% pluralize %}" - "{{ num }} apples{% endtrans %}", - "pgettext.html": '{{ pgettext("fruit", "Apple") }}', - "npgettext.html": '{{ npgettext("fruit", "%(num)s apple", "%(num)s apples",' - " apples) }}", - "transvars1.html": "{% trans %}User: {{ num }}{% endtrans %}", - "transvars2.html": "{% trans num=count %}User: {{ num }}{% endtrans %}", - "transvars3.html": "{% trans count=num %}User: {{ count }}{% endtrans %}", - "novars.html": "{% trans %}%(hello)s{% endtrans %}", - "vars.html": "{% trans %}{{ foo }}%(foo)s{% endtrans %}", - "explicitvars.html": '{% trans foo="42" %}%(foo)s{% endtrans %}', + "default.html": '<title>{{ page_title|default(_("missing")) }}</title>' + "{% block body %}{% endblock %}", + "child.html": '{% extends "default.html" %}{% block body %}' + "{% trans %}watch out{% endtrans %}{% endblock %}", + "plural.html": "{% trans user_count %}One user online{% pluralize %}" + "{{ user_count }} users online{% endtrans %}", + "stringformat.html": '{{ _("User: %(num)s", num=user_count) }}', + "ngettext.html": '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}', + "ngettext_long.html": "{% trans num=apples %}{{ num }} apple{% pluralize %}" + "{{ num }} apples{% endtrans %}", + "pgettext.html": '{{ pgettext("fruit", "Apple") }}', + "npgettext.html": '{{ npgettext("fruit", "%(num)s apple", "%(num)s apples",' + " apples) }}", + "transvars1.html": "{% trans %}User: {{ num }}{% endtrans %}", + "transvars2.html": "{% trans num=count %}User: {{ num }}{% endtrans %}", + "transvars3.html": "{% trans count=num %}User: {{ count }}{% endtrans %}", + "novars.html": "{% trans %}%(hello)s{% endtrans %}", + "vars.html": "{% trans %}{{ foo }}%(foo)s{% endtrans %}", + "explicitvars.html": '{% trans foo="42" %}%(foo)s{% endtrans %}', } languages = { - "de": { - "missing": "fehlend", - "watch out": "pass auf", - "One user online": "Ein Benutzer online", - "%(user_count)s users online": "%(user_count)s Benutzer online", - "User: %(num)s": "Benutzer: %(num)s", - "User: %(count)s": "Benutzer: %(count)s", - "Apple": {None: "Apfel", "fruit": "Apple"}, - "%(num)s apple": {None: "%(num)s Apfel", "fruit": "%(num)s Apple"}, - "%(num)s apples": {None: "%(num)s Äpfel", "fruit": "%(num)s Apples"}, + "de": { + "missing": "fehlend", + "watch out": "pass auf", + "One user online": "Ein Benutzer online", + "%(user_count)s users online": "%(user_count)s Benutzer online", + "User: %(num)s": "Benutzer: %(num)s", + "User: %(count)s": "Benutzer: %(count)s", + "Apple": {None: "Apfel", "fruit": "Apple"}, + "%(num)s apple": {None: "%(num)s Apfel", "fruit": "%(num)s Apple"}, + "%(num)s apples": {None: "%(num)s Äpfel", "fruit": "%(num)s Apples"}, } } -def _get_with_context(value, ctx=None): - if isinstance(value, dict): - return value.get(ctx, value) - - return value - - -@pass_context +def _get_with_context(value, ctx=None): + if isinstance(value, dict): + return value.get(ctx, value) + + return value + + +@pass_context def gettext(context, string): - language = context.get("LANGUAGE", "en") - value = languages.get(language, {}).get(string, string) - return _get_with_context(value) + language = context.get("LANGUAGE", "en") + value = languages.get(language, {}).get(string, string) + return _get_with_context(value) -@pass_context +@pass_context def ngettext(context, s, p, n): - language = context.get("LANGUAGE", "en") - + language = context.get("LANGUAGE", "en") + + if n != 1: + value = languages.get(language, {}).get(p, p) + return _get_with_context(value) + + value = languages.get(language, {}).get(s, s) + return _get_with_context(value) + + +@pass_context +def pgettext(context, c, s): + language = context.get("LANGUAGE", "en") + value = languages.get(language, {}).get(s, s) + return _get_with_context(value, c) + + +@pass_context +def npgettext(context, c, s, p, n): + language = context.get("LANGUAGE", "en") + if n != 1: - value = languages.get(language, {}).get(p, p) - return _get_with_context(value) - - value = languages.get(language, {}).get(s, s) - return _get_with_context(value) - - -@pass_context -def pgettext(context, c, s): - language = context.get("LANGUAGE", "en") - value = languages.get(language, {}).get(s, s) - return _get_with_context(value, c) - - -@pass_context -def npgettext(context, c, s, p, n): - language = context.get("LANGUAGE", "en") - - if n != 1: - value = languages.get(language, {}).get(p, p) - return _get_with_context(value, c) - - value = languages.get(language, {}).get(s, s) - return _get_with_context(value, c) - - + value = languages.get(language, {}).get(p, p) + return _get_with_context(value, c) + + value = languages.get(language, {}).get(s, s) + return _get_with_context(value, c) + + i18n_env = Environment( - loader=DictLoader(i18n_templates), extensions=["jinja2.ext.i18n"] + loader=DictLoader(i18n_templates), extensions=["jinja2.ext.i18n"] +) +i18n_env.globals.update( + { + "_": gettext, + "gettext": gettext, + "ngettext": ngettext, + "pgettext": pgettext, + "npgettext": npgettext, + } +) +i18n_env_trimmed = Environment(extensions=["jinja2.ext.i18n"]) + +i18n_env_trimmed.policies["ext.i18n.trimmed"] = True +i18n_env_trimmed.globals.update( + { + "_": gettext, + "gettext": gettext, + "ngettext": ngettext, + "pgettext": pgettext, + "npgettext": npgettext, + } ) -i18n_env.globals.update( - { - "_": gettext, - "gettext": gettext, - "ngettext": ngettext, - "pgettext": pgettext, - "npgettext": npgettext, - } -) -i18n_env_trimmed = Environment(extensions=["jinja2.ext.i18n"]) - -i18n_env_trimmed.policies["ext.i18n.trimmed"] = True -i18n_env_trimmed.globals.update( - { - "_": gettext, - "gettext": gettext, - "ngettext": ngettext, - "pgettext": pgettext, - "npgettext": npgettext, - } -) newstyle_i18n_env = Environment( - loader=DictLoader(newstyle_i18n_templates), extensions=["jinja2.ext.i18n"] + loader=DictLoader(newstyle_i18n_templates), extensions=["jinja2.ext.i18n"] +) +newstyle_i18n_env.install_gettext_callables( # type: ignore + gettext, ngettext, newstyle=True, pgettext=pgettext, npgettext=npgettext ) -newstyle_i18n_env.install_gettext_callables( # type: ignore - gettext, ngettext, newstyle=True, pgettext=pgettext, npgettext=npgettext -) class ExampleExtension(Extension): - tags = {"test"} + tags = {"test"} ext_attr = 42 - context_reference_node_cls = nodes.ContextReference + context_reference_node_cls = nodes.ContextReference def parse(self, parser): - return nodes.Output( - [ - self.call_method( - "_dump", - [ - nodes.EnvironmentAttribute("sandboxed"), - self.attr("ext_attr"), - nodes.ImportedName(__name__ + ".importable_object"), - self.context_reference_node_cls(), - ], - ) - ] - ).set_lineno(next(parser.stream).lineno) + return nodes.Output( + [ + self.call_method( + "_dump", + [ + nodes.EnvironmentAttribute("sandboxed"), + self.attr("ext_attr"), + nodes.ImportedName(__name__ + ".importable_object"), + self.context_reference_node_cls(), + ], + ) + ] + ).set_lineno(next(parser.stream).lineno) def _dump(self, sandboxed, ext_attr, imported_object, context): - return ( - f"{sandboxed}|{ext_attr}|{imported_object}|{context.blocks}" - f"|{context.get('test_var')}" + return ( + f"{sandboxed}|{ext_attr}|{imported_object}|{context.blocks}" + f"|{context.get('test_var')}" ) -class DerivedExampleExtension(ExampleExtension): - context_reference_node_cls = nodes.DerivedContextReference # type: ignore - - +class DerivedExampleExtension(ExampleExtension): + context_reference_node_cls = nodes.DerivedContextReference # type: ignore + + class PreprocessorExtension(Extension): def preprocess(self, source, name, filename=None): - return source.replace("[[TEST]]", "({{ foo }})") + return source.replace("[[TEST]]", "({{ foo }})") class StreamFilterExtension(Extension): def filter_stream(self, stream): for token in stream: - if token.type == "data": - yield from self.interpolate(token) + if token.type == "data": + yield from self.interpolate(token) else: yield token @@ -193,101 +193,101 @@ class StreamFilterExtension(Extension): pos = 0 end = len(token.value) lineno = token.lineno - while True: + while True: match = _gettext_re.search(token.value, pos) if match is None: break - value = token.value[pos : match.start()] + value = token.value[pos : match.start()] if value: - yield Token(lineno, "data", value) + yield Token(lineno, "data", value) lineno += count_newlines(token.value) - yield Token(lineno, "variable_begin", None) - yield Token(lineno, "name", "gettext") - yield Token(lineno, "lparen", None) - yield Token(lineno, "string", match.group(1)) - yield Token(lineno, "rparen", None) - yield Token(lineno, "variable_end", None) + yield Token(lineno, "variable_begin", None) + yield Token(lineno, "name", "gettext") + yield Token(lineno, "lparen", None) + yield Token(lineno, "string", match.group(1)) + yield Token(lineno, "rparen", None) + yield Token(lineno, "variable_end", None) pos = match.end() if pos < end: - yield Token(lineno, "data", token.value[pos:]) + yield Token(lineno, "data", token.value[pos:]) -class TestExtensions: +class TestExtensions: def test_extend_late(self): env = Environment() - t = env.from_string('{% autoescape true %}{{ "<test>" }}{% endautoescape %}') - assert t.render() == "<test>" + t = env.from_string('{% autoescape true %}{{ "<test>" }}{% endautoescape %}') + assert t.render() == "<test>" def test_loop_controls(self): - env = Environment(extensions=["jinja2.ext.loopcontrols"]) + env = Environment(extensions=["jinja2.ext.loopcontrols"]) - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {%- for item in [1, 2, 3, 4] %} {%- if item % 2 == 0 %}{% continue %}{% endif -%} {{ item }} - {%- endfor %}""" - ) - assert tmpl.render() == "13" + {%- endfor %}""" + ) + assert tmpl.render() == "13" - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {%- for item in [1, 2, 3, 4] %} {%- if item > 2 %}{% break %}{% endif -%} {{ item }} - {%- endfor %}""" - ) - assert tmpl.render() == "12" + {%- endfor %}""" + ) + assert tmpl.render() == "12" def test_do(self): - env = Environment(extensions=["jinja2.ext.do"]) - tmpl = env.from_string( - """ + env = Environment(extensions=["jinja2.ext.do"]) + tmpl = env.from_string( + """ {%- set items = [] %} {%- for char in "foo" %} {%- do items.append(loop.index0 ~ char) %} - {%- endfor %}{{ items|join(', ') }}""" - ) - assert tmpl.render() == "0f, 1o, 2o" + {%- endfor %}{{ items|join(', ') }}""" + ) + assert tmpl.render() == "0f, 1o, 2o" def test_extension_nodes(self): env = Environment(extensions=[ExampleExtension]) - tmpl = env.from_string("{% test %}") - assert tmpl.render() == "False|42|23|{}|None" - - def test_contextreference_node_passes_context(self): - env = Environment(extensions=[ExampleExtension]) - tmpl = env.from_string('{% set test_var="test_content" %}{% test %}') - assert tmpl.render() == "False|42|23|{}|test_content" - - def test_contextreference_node_can_pass_locals(self): - env = Environment(extensions=[DerivedExampleExtension]) - tmpl = env.from_string( - '{% for test_var in ["test_content"] %}{% test %}{% endfor %}' - ) - assert tmpl.render() == "False|42|23|{}|test_content" - + tmpl = env.from_string("{% test %}") + assert tmpl.render() == "False|42|23|{}|None" + + def test_contextreference_node_passes_context(self): + env = Environment(extensions=[ExampleExtension]) + tmpl = env.from_string('{% set test_var="test_content" %}{% test %}') + assert tmpl.render() == "False|42|23|{}|test_content" + + def test_contextreference_node_can_pass_locals(self): + env = Environment(extensions=[DerivedExampleExtension]) + tmpl = env.from_string( + '{% for test_var in ["test_content"] %}{% test %}{% endfor %}' + ) + assert tmpl.render() == "False|42|23|{}|test_content" + def test_identifier(self): - assert ExampleExtension.identifier == __name__ + ".ExampleExtension" + assert ExampleExtension.identifier == __name__ + ".ExampleExtension" def test_rebinding(self): original = Environment(extensions=[ExampleExtension]) overlay = original.overlay() for env in original, overlay: - for ext in env.extensions.values(): + for ext in env.extensions.values(): assert ext.environment is env def test_preprocessor_extension(self): env = Environment(extensions=[PreprocessorExtension]) - tmpl = env.from_string("{[[TEST]]}") - assert tmpl.render(foo=42) == "{(42)}" + tmpl = env.from_string("{[[TEST]]}") + assert tmpl.render(foo=42) == "{(42)}" def test_streamfilter_extension(self): env = Environment(extensions=[StreamFilterExtension]) - env.globals["gettext"] = lambda x: x.upper() - tmpl = env.from_string("Foo _(bar) Baz") + env.globals["gettext"] = lambda x: x.upper() + tmpl = env.from_string("Foo _(bar) Baz") out = tmpl.render() - assert out == "Foo BAR Baz" + assert out == "Foo BAR Baz" def test_extension_ordering(self): class T1(Extension): @@ -295,374 +295,374 @@ class TestExtensions: class T2(Extension): priority = 2 - + env = Environment(extensions=[T1, T2]) ext = list(env.iter_extensions()) assert ext[0].__class__ is T1 assert ext[1].__class__ is T2 - def test_debug(self): - env = Environment(extensions=["jinja2.ext.debug"]) - t = env.from_string("Hello\n{% debug %}\nGoodbye") - out = t.render() + def test_debug(self): + env = Environment(extensions=["jinja2.ext.debug"]) + t = env.from_string("Hello\n{% debug %}\nGoodbye") + out = t.render() + + for value in ("context", "cycler", "filters", "abs", "tests", "!="): + assert f"'{value}'" in out + - for value in ("context", "cycler", "filters", "abs", "tests", "!="): - assert f"'{value}'" in out - - -class TestInternationalization: +class TestInternationalization: def test_trans(self): - tmpl = i18n_env.get_template("child.html") - assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf" + tmpl = i18n_env.get_template("child.html") + assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf" def test_trans_plural(self): - tmpl = i18n_env.get_template("plural.html") - assert tmpl.render(LANGUAGE="de", user_count=1) == "Ein Benutzer online" - assert tmpl.render(LANGUAGE="de", user_count=2) == "2 Benutzer online" + tmpl = i18n_env.get_template("plural.html") + assert tmpl.render(LANGUAGE="de", user_count=1) == "Ein Benutzer online" + assert tmpl.render(LANGUAGE="de", user_count=2) == "2 Benutzer online" def test_trans_plural_with_functions(self): - tmpl = i18n_env.get_template("plural2.html") + tmpl = i18n_env.get_template("plural2.html") def get_user_count(): get_user_count.called += 1 return 1 - + get_user_count.called = 0 - assert tmpl.render(LANGUAGE="de", get_user_count=get_user_count) == "1s" + assert tmpl.render(LANGUAGE="de", get_user_count=get_user_count) == "1s" assert get_user_count.called == 1 def test_complex_plural(self): tmpl = i18n_env.from_string( - "{% trans foo=42, count=2 %}{{ count }} item{% " - "pluralize count %}{{ count }} items{% endtrans %}" - ) - assert tmpl.render() == "2 items" - pytest.raises( - TemplateAssertionError, - i18n_env.from_string, - "{% trans foo %}...{% pluralize bar %}...{% endtrans %}", - ) + "{% trans foo=42, count=2 %}{{ count }} item{% " + "pluralize count %}{{ count }} items{% endtrans %}" + ) + assert tmpl.render() == "2 items" + pytest.raises( + TemplateAssertionError, + i18n_env.from_string, + "{% trans foo %}...{% pluralize bar %}...{% endtrans %}", + ) def test_trans_stringformatting(self): - tmpl = i18n_env.get_template("stringformat.html") - assert tmpl.render(LANGUAGE="de", user_count=5) == "Benutzer: 5" + tmpl = i18n_env.get_template("stringformat.html") + assert tmpl.render(LANGUAGE="de", user_count=5) == "Benutzer: 5" def test_trimmed(self): tmpl = i18n_env.from_string( - "{%- trans trimmed %} hello\n world {% endtrans -%}" - ) - assert tmpl.render() == "hello world" + "{%- trans trimmed %} hello\n world {% endtrans -%}" + ) + assert tmpl.render() == "hello world" def test_trimmed_policy(self): - s = "{%- trans %} hello\n world {% endtrans -%}" + s = "{%- trans %} hello\n world {% endtrans -%}" tmpl = i18n_env.from_string(s) trimmed_tmpl = i18n_env_trimmed.from_string(s) - assert tmpl.render() == " hello\n world " - assert trimmed_tmpl.render() == "hello world" + assert tmpl.render() == " hello\n world " + assert trimmed_tmpl.render() == "hello world" def test_trimmed_policy_override(self): tmpl = i18n_env_trimmed.from_string( - "{%- trans notrimmed %} hello\n world {% endtrans -%}" - ) - assert tmpl.render() == " hello\n world " + "{%- trans notrimmed %} hello\n world {% endtrans -%}" + ) + assert tmpl.render() == " hello\n world " def test_trimmed_vars(self): tmpl = i18n_env.from_string( - '{%- trans trimmed x="world" %} hello\n {{ x }} {% endtrans -%}' - ) - assert tmpl.render() == "hello world" + '{%- trans trimmed x="world" %} hello\n {{ x }} {% endtrans -%}' + ) + assert tmpl.render() == "hello world" def test_trimmed_varname_trimmed(self): # unlikely variable name, but when used as a variable # it should not enable trimming tmpl = i18n_env.from_string( - "{%- trans trimmed = 'world' %} hello\n {{ trimmed }} {% endtrans -%}" - ) - assert tmpl.render() == " hello\n world " + "{%- trans trimmed = 'world' %} hello\n {{ trimmed }} {% endtrans -%}" + ) + assert tmpl.render() == " hello\n world " def test_extract(self): from jinja2.ext import babel_extract - - source = BytesIO( - b""" - {{ gettext('Hello World') }} - {% trans %}Hello World{% endtrans %} - {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} - """ - ) - assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], {})) == [ - (2, "gettext", "Hello World", []), - (3, "gettext", "Hello World", []), - (4, "ngettext", ("%(users)s user", "%(users)s users", None), []), + + source = BytesIO( + b""" + {{ gettext('Hello World') }} + {% trans %}Hello World{% endtrans %} + {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} + """ + ) + assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], {})) == [ + (2, "gettext", "Hello World", []), + (3, "gettext", "Hello World", []), + (4, "ngettext", ("%(users)s user", "%(users)s users", None), []), ] def test_extract_trimmed(self): from jinja2.ext import babel_extract - - source = BytesIO( - b""" - {{ gettext(' Hello \n World') }} - {% trans trimmed %} Hello \n World{% endtrans %} - {% trans trimmed %}{{ users }} \n user - {%- pluralize %}{{ users }} \n users{% endtrans %} - """ - ) - assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], {})) == [ - (2, "gettext", " Hello \n World", []), - (4, "gettext", "Hello World", []), - (6, "ngettext", ("%(users)s user", "%(users)s users", None), []), + + source = BytesIO( + b""" + {{ gettext(' Hello \n World') }} + {% trans trimmed %} Hello \n World{% endtrans %} + {% trans trimmed %}{{ users }} \n user + {%- pluralize %}{{ users }} \n users{% endtrans %} + """ + ) + assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], {})) == [ + (2, "gettext", " Hello \n World", []), + (4, "gettext", "Hello World", []), + (6, "ngettext", ("%(users)s user", "%(users)s users", None), []), ] def test_extract_trimmed_option(self): from jinja2.ext import babel_extract - - source = BytesIO( - b""" - {{ gettext(' Hello \n World') }} - {% trans %} Hello \n World{% endtrans %} - {% trans %}{{ users }} \n user - {%- pluralize %}{{ users }} \n users{% endtrans %} - """ - ) - opts = {"trimmed": "true"} - assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], opts)) == [ - (2, "gettext", " Hello \n World", []), - (4, "gettext", "Hello World", []), - (6, "ngettext", ("%(users)s user", "%(users)s users", None), []), + + source = BytesIO( + b""" + {{ gettext(' Hello \n World') }} + {% trans %} Hello \n World{% endtrans %} + {% trans %}{{ users }} \n user + {%- pluralize %}{{ users }} \n users{% endtrans %} + """ + ) + opts = {"trimmed": "true"} + assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], opts)) == [ + (2, "gettext", " Hello \n World", []), + (4, "gettext", "Hello World", []), + (6, "ngettext", ("%(users)s user", "%(users)s users", None), []), ] def test_comment_extract(self): from jinja2.ext import babel_extract - - source = BytesIO( - b""" - {# trans first #} - {{ gettext('Hello World') }} - {% trans %}Hello World{% endtrans %}{# trans second #} - {#: third #} - {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} - """ - ) - assert list( - babel_extract(source, ("gettext", "ngettext", "_"), ["trans", ":"], {}) - ) == [ - (3, "gettext", "Hello World", ["first"]), - (4, "gettext", "Hello World", ["second"]), - (6, "ngettext", ("%(users)s user", "%(users)s users", None), ["third"]), + + source = BytesIO( + b""" + {# trans first #} + {{ gettext('Hello World') }} + {% trans %}Hello World{% endtrans %}{# trans second #} + {#: third #} + {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} + """ + ) + assert list( + babel_extract(source, ("gettext", "ngettext", "_"), ["trans", ":"], {}) + ) == [ + (3, "gettext", "Hello World", ["first"]), + (4, "gettext", "Hello World", ["second"]), + (6, "ngettext", ("%(users)s user", "%(users)s users", None), ["third"]), + ] + + def test_extract_context(self): + from jinja2.ext import babel_extract + + source = BytesIO( + b""" + {{ pgettext("babel", "Hello World") }} + {{ npgettext("babel", "%(users)s user", "%(users)s users", users) }} + """ + ) + assert list(babel_extract(source, ("pgettext", "npgettext", "_"), [], {})) == [ + (2, "pgettext", ("babel", "Hello World"), []), + (3, "npgettext", ("babel", "%(users)s user", "%(users)s users", None), []), ] - def test_extract_context(self): - from jinja2.ext import babel_extract - - source = BytesIO( - b""" - {{ pgettext("babel", "Hello World") }} - {{ npgettext("babel", "%(users)s user", "%(users)s users", users) }} - """ - ) - assert list(babel_extract(source, ("pgettext", "npgettext", "_"), [], {})) == [ - (2, "pgettext", ("babel", "Hello World"), []), - (3, "npgettext", ("babel", "%(users)s user", "%(users)s users", None), []), - ] - - -class TestScope: + +class TestScope: def test_basic_scope_behavior(self): # This is what the old with statement compiled down to class ScopeExt(Extension): - tags = {"scope"} + tags = {"scope"} def parse(self, parser): node = nodes.Scope(lineno=next(parser.stream).lineno) assignments = [] - while parser.stream.current.type != "block_end": + while parser.stream.current.type != "block_end": lineno = parser.stream.current.lineno if assignments: - parser.stream.expect("comma") + parser.stream.expect("comma") target = parser.parse_assign_target() - parser.stream.expect("assign") + parser.stream.expect("assign") expr = parser.parse_expression() assignments.append(nodes.Assign(target, expr, lineno=lineno)) - node.body = assignments + list( - parser.parse_statements(("name:endscope",), drop_needle=True) - ) + node.body = assignments + list( + parser.parse_statements(("name:endscope",), drop_needle=True) + ) return node env = Environment(extensions=[ScopeExt]) - tmpl = env.from_string( - """\ + tmpl = env.from_string( + """\ {%- scope a=1, b=2, c=b, d=e, e=5 -%} {{ a }}|{{ b }}|{{ c }}|{{ d }}|{{ e }} {%- endscope -%} - """ - ) - assert tmpl.render(b=3, e=4) == "1|2|2|4|5" + """ + ) + assert tmpl.render(b=3, e=4) == "1|2|2|4|5" -class TestNewstyleInternationalization: +class TestNewstyleInternationalization: def test_trans(self): - tmpl = newstyle_i18n_env.get_template("child.html") - assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf" + tmpl = newstyle_i18n_env.get_template("child.html") + assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf" def test_trans_plural(self): - tmpl = newstyle_i18n_env.get_template("plural.html") - assert tmpl.render(LANGUAGE="de", user_count=1) == "Ein Benutzer online" - assert tmpl.render(LANGUAGE="de", user_count=2) == "2 Benutzer online" + tmpl = newstyle_i18n_env.get_template("plural.html") + assert tmpl.render(LANGUAGE="de", user_count=1) == "Ein Benutzer online" + assert tmpl.render(LANGUAGE="de", user_count=2) == "2 Benutzer online" def test_complex_plural(self): tmpl = newstyle_i18n_env.from_string( - "{% trans foo=42, count=2 %}{{ count }} item{% " - "pluralize count %}{{ count }} items{% endtrans %}" - ) - assert tmpl.render() == "2 items" - pytest.raises( - TemplateAssertionError, - i18n_env.from_string, - "{% trans foo %}...{% pluralize bar %}...{% endtrans %}", - ) + "{% trans foo=42, count=2 %}{{ count }} item{% " + "pluralize count %}{{ count }} items{% endtrans %}" + ) + assert tmpl.render() == "2 items" + pytest.raises( + TemplateAssertionError, + i18n_env.from_string, + "{% trans foo %}...{% pluralize bar %}...{% endtrans %}", + ) def test_trans_stringformatting(self): - tmpl = newstyle_i18n_env.get_template("stringformat.html") - assert tmpl.render(LANGUAGE="de", user_count=5) == "Benutzer: 5" + tmpl = newstyle_i18n_env.get_template("stringformat.html") + assert tmpl.render(LANGUAGE="de", user_count=5) == "Benutzer: 5" def test_newstyle_plural(self): - tmpl = newstyle_i18n_env.get_template("ngettext.html") - assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apfel" - assert tmpl.render(LANGUAGE="de", apples=5) == "5 Äpfel" + tmpl = newstyle_i18n_env.get_template("ngettext.html") + assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apfel" + assert tmpl.render(LANGUAGE="de", apples=5) == "5 Äpfel" def test_autoescape_support(self): - env = Environment(extensions=["jinja2.ext.i18n"]) + env = Environment(extensions=["jinja2.ext.i18n"]) env.install_gettext_callables( - lambda x: "<strong>Wert: %(name)s</strong>", - lambda s, p, n: s, - newstyle=True, - ) - t = env.from_string( - '{% autoescape ae %}{{ gettext("foo", name=' - '"<test>") }}{% endautoescape %}' - ) - assert t.render(ae=True) == "<strong>Wert: <test></strong>" - assert t.render(ae=False) == "<strong>Wert: <test></strong>" + lambda x: "<strong>Wert: %(name)s</strong>", + lambda s, p, n: s, + newstyle=True, + ) + t = env.from_string( + '{% autoescape ae %}{{ gettext("foo", name=' + '"<test>") }}{% endautoescape %}' + ) + assert t.render(ae=True) == "<strong>Wert: <test></strong>" + assert t.render(ae=False) == "<strong>Wert: <test></strong>" def test_autoescape_macros(self): - env = Environment(autoescape=False) + env = Environment(autoescape=False) template = ( - "{% macro m() %}<html>{% endmacro %}" - "{% autoescape true %}{{ m() }}{% endautoescape %}" + "{% macro m() %}<html>{% endmacro %}" + "{% autoescape true %}{{ m() }}{% endautoescape %}" ) - assert env.from_string(template).render() == "<html>" + assert env.from_string(template).render() == "<html>" def test_num_used_twice(self): - tmpl = newstyle_i18n_env.get_template("ngettext_long.html") - assert tmpl.render(apples=5, LANGUAGE="de") == "5 Äpfel" + tmpl = newstyle_i18n_env.get_template("ngettext_long.html") + assert tmpl.render(apples=5, LANGUAGE="de") == "5 Äpfel" def test_num_called_num(self): - source = newstyle_i18n_env.compile( - """ + source = newstyle_i18n_env.compile( + """ {% trans num=3 %}{{ num }} apple{% pluralize %}{{ num }} apples{% endtrans %} - """, - raw=True, - ) + """, + raw=True, + ) # quite hacky, but the only way to properly test that. The idea is # that the generated code does not pass num twice (although that # would work) for better performance. This only works on the # newstyle gettext of course - assert ( - re.search(r"u?'%\(num\)s apple', u?'%\(num\)s apples', 3", source) - is not None - ) + assert ( + re.search(r"u?'%\(num\)s apple', u?'%\(num\)s apples', 3", source) + is not None + ) def test_trans_vars(self): - t1 = newstyle_i18n_env.get_template("transvars1.html") - t2 = newstyle_i18n_env.get_template("transvars2.html") - t3 = newstyle_i18n_env.get_template("transvars3.html") - assert t1.render(num=1, LANGUAGE="de") == "Benutzer: 1" - assert t2.render(count=23, LANGUAGE="de") == "Benutzer: 23" - assert t3.render(num=42, LANGUAGE="de") == "Benutzer: 42" + t1 = newstyle_i18n_env.get_template("transvars1.html") + t2 = newstyle_i18n_env.get_template("transvars2.html") + t3 = newstyle_i18n_env.get_template("transvars3.html") + assert t1.render(num=1, LANGUAGE="de") == "Benutzer: 1" + assert t2.render(count=23, LANGUAGE="de") == "Benutzer: 23" + assert t3.render(num=42, LANGUAGE="de") == "Benutzer: 42" def test_novars_vars_escaping(self): - t = newstyle_i18n_env.get_template("novars.html") - assert t.render() == "%(hello)s" - t = newstyle_i18n_env.get_template("vars.html") - assert t.render(foo="42") == "42%(foo)s" - t = newstyle_i18n_env.get_template("explicitvars.html") - assert t.render() == "%(foo)s" - - def test_context(self): - tmpl = newstyle_i18n_env.get_template("pgettext.html") - assert tmpl.render(LANGUAGE="de") == "Apple" - - def test_context_newstyle_plural(self): - tmpl = newstyle_i18n_env.get_template("npgettext.html") - assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apple" - assert tmpl.render(LANGUAGE="de", apples=5) == "5 Apples" - - -class TestAutoEscape: + t = newstyle_i18n_env.get_template("novars.html") + assert t.render() == "%(hello)s" + t = newstyle_i18n_env.get_template("vars.html") + assert t.render(foo="42") == "42%(foo)s" + t = newstyle_i18n_env.get_template("explicitvars.html") + assert t.render() == "%(foo)s" + + def test_context(self): + tmpl = newstyle_i18n_env.get_template("pgettext.html") + assert tmpl.render(LANGUAGE="de") == "Apple" + + def test_context_newstyle_plural(self): + tmpl = newstyle_i18n_env.get_template("npgettext.html") + assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apple" + assert tmpl.render(LANGUAGE="de", apples=5) == "5 Apples" + + +class TestAutoEscape: def test_scoped_setting(self): - env = Environment(autoescape=True) - tmpl = env.from_string( - """ + env = Environment(autoescape=True) + tmpl = env.from_string( + """ {{ "<HelloWorld>" }} {% autoescape false %} {{ "<HelloWorld>" }} {% endautoescape %} {{ "<HelloWorld>" }} - """ - ) - assert tmpl.render().split() == [ - "<HelloWorld>", - "<HelloWorld>", - "<HelloWorld>", - ] - - env = Environment(autoescape=False) - tmpl = env.from_string( - """ + """ + ) + assert tmpl.render().split() == [ + "<HelloWorld>", + "<HelloWorld>", + "<HelloWorld>", + ] + + env = Environment(autoescape=False) + tmpl = env.from_string( + """ {{ "<HelloWorld>" }} {% autoescape true %} {{ "<HelloWorld>" }} {% endautoescape %} {{ "<HelloWorld>" }} - """ - ) - assert tmpl.render().split() == [ - "<HelloWorld>", - "<HelloWorld>", - "<HelloWorld>", - ] + """ + ) + assert tmpl.render().split() == [ + "<HelloWorld>", + "<HelloWorld>", + "<HelloWorld>", + ] def test_nonvolatile(self): - env = Environment(autoescape=True) + env = Environment(autoescape=True) tmpl = env.from_string('{{ {"foo": "<test>"}|xmlattr|escape }}') assert tmpl.render() == ' foo="<test>"' - tmpl = env.from_string( - '{% autoescape false %}{{ {"foo": "<test>"}' - "|xmlattr|escape }}{% endautoescape %}" - ) - assert tmpl.render() == " foo="&lt;test&gt;"" + tmpl = env.from_string( + '{% autoescape false %}{{ {"foo": "<test>"}' + "|xmlattr|escape }}{% endautoescape %}" + ) + assert tmpl.render() == " foo="&lt;test&gt;"" def test_volatile(self): - env = Environment(autoescape=True) - tmpl = env.from_string( - '{% autoescape foo %}{{ {"foo": "<test>"}' - "|xmlattr|escape }}{% endautoescape %}" - ) - assert tmpl.render(foo=False) == " foo="&lt;test&gt;"" + env = Environment(autoescape=True) + tmpl = env.from_string( + '{% autoescape foo %}{{ {"foo": "<test>"}' + "|xmlattr|escape }}{% endautoescape %}" + ) + assert tmpl.render(foo=False) == " foo="&lt;test&gt;"" assert tmpl.render(foo=True) == ' foo="<test>"' def test_scoping(self): - env = Environment() + env = Environment() tmpl = env.from_string( '{% autoescape true %}{% set x = "<x>" %}{{ x }}' - '{% endautoescape %}{{ x }}{{ "<y>" }}' - ) - assert tmpl.render(x=1) == "<x>1<y>" + '{% endautoescape %}{{ x }}{{ "<y>" }}' + ) + assert tmpl.render(x=1) == "<x>1<y>" def test_volatile_scoping(self): - env = Environment() - tmplsource = """ + env = Environment() + tmplsource = """ {% autoescape val %} {% macro foo(x) %} [{{ x }}] @@ -670,45 +670,45 @@ class TestAutoEscape: {{ foo().__class__.__name__ }} {% endautoescape %} {{ '<testing>' }} - """ + """ tmpl = env.from_string(tmplsource) - assert tmpl.render(val=True).split()[0] == "Markup" - assert tmpl.render(val=False).split()[0] == "str" + assert tmpl.render(val=True).split()[0] == "Markup" + assert tmpl.render(val=False).split()[0] == "str" # looking at the source we should see <testing> there in raw # (and then escaped as well) - env = Environment() + env = Environment() pysource = env.compile(tmplsource, raw=True) - assert "<testing>\\n" in pysource + assert "<testing>\\n" in pysource - env = Environment(autoescape=True) + env = Environment(autoescape=True) pysource = env.compile(tmplsource, raw=True) - assert "<testing>\\n" in pysource + assert "<testing>\\n" in pysource def test_overlay_scopes(self): class MagicScopeExtension(Extension): - tags = {"overlay"} - + tags = {"overlay"} + def parse(self, parser): node = nodes.OverlayScope(lineno=next(parser.stream).lineno) - node.body = list( - parser.parse_statements(("name:endoverlay",), drop_needle=True) - ) - node.context = self.call_method("get_scope") + node.body = list( + parser.parse_statements(("name:endoverlay",), drop_needle=True) + ) + node.context = self.call_method("get_scope") return node - + def get_scope(self): - return {"x": [1, 2, 3]} + return {"x": [1, 2, 3]} env = Environment(extensions=[MagicScopeExtension]) - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {{- x }}|{% set z = 99 %} {%- overlay %} {{- y }}|{{ z }}|{% for item in x %}[{{ item }}]{% endfor %} {%- endoverlay %}| {{- x -}} - """ - ) - assert tmpl.render(x=42, y=23) == "42|23|99|[1][2][3]|42" + """ + ) + assert tmpl.render(x=42, y=23) == "42|23|99|[1][2][3]|42" diff --git a/contrib/python/Jinja2/py3/tests/test_features.py b/contrib/python/Jinja2/py3/tests/test_features.py index c8184c1592..4f36458a7f 100644 --- a/contrib/python/Jinja2/py3/tests/test_features.py +++ b/contrib/python/Jinja2/py3/tests/test_features.py @@ -1,14 +1,14 @@ import pytest -from jinja2 import Template +from jinja2 import Template -# Python < 3.7 +# Python < 3.7 def test_generator_stop(): - class X: + class X: def __getattr__(self, name): raise StopIteration() - t = Template("a{{ bad.bar() }}b") + t = Template("a{{ bad.bar() }}b") with pytest.raises(RuntimeError): t.render(bad=X()) diff --git a/contrib/python/Jinja2/py3/tests/test_filters.py b/contrib/python/Jinja2/py3/tests/test_filters.py index 25f0aee892..2195157c4f 100644 --- a/contrib/python/Jinja2/py3/tests/test_filters.py +++ b/contrib/python/Jinja2/py3/tests/test_filters.py @@ -1,260 +1,260 @@ -import random -from collections import namedtuple +import random +from collections import namedtuple import pytest -from markupsafe import Markup +from markupsafe import Markup -from jinja2 import Environment -from jinja2 import StrictUndefined -from jinja2 import TemplateRuntimeError -from jinja2 import UndefinedError -from jinja2.exceptions import TemplateAssertionError +from jinja2 import Environment +from jinja2 import StrictUndefined +from jinja2 import TemplateRuntimeError +from jinja2 import UndefinedError +from jinja2.exceptions import TemplateAssertionError - -class Magic: + +class Magic: def __init__(self, value): self.value = value def __str__(self): - return str(self.value) - - -class Magic2: - def __init__(self, value1, value2): - self.value1 = value1 - self.value2 = value2 - - def __str__(self): - return f"({self.value1},{self.value2})" - - -class TestFilter: + return str(self.value) + + +class Magic2: + def __init__(self, value1, value2): + self.value1 = value1 + self.value2 = value2 + + def __str__(self): + return f"({self.value1},{self.value2})" + + +class TestFilter: def test_filter_calling(self, env): - rv = env.call_filter("sum", [1, 2, 3]) + rv = env.call_filter("sum", [1, 2, 3]) assert rv == 6 def test_capitalize(self, env): tmpl = env.from_string('{{ "foo bar"|capitalize }}') - assert tmpl.render() == "Foo bar" + assert tmpl.render() == "Foo bar" def test_center(self, env): tmpl = env.from_string('{{ "foo"|center(9) }}') - assert tmpl.render() == " foo " + assert tmpl.render() == " foo " def test_default(self, env): tmpl = env.from_string( "{{ missing|default('no') }}|{{ false|default('no') }}|" "{{ false|default('no', true) }}|{{ given|default('no') }}" ) - assert tmpl.render(given="yes") == "no|False|no|yes" - - @pytest.mark.parametrize( - "args,expect", - ( - ("", "[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]"), - ("true", "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]"), - ('by="value"', "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]"), - ("reverse=true", "[('c', 2), ('b', 1), ('AB', 3), ('aa', 0)]"), - ), - ) + assert tmpl.render(given="yes") == "no|False|no|yes" + + @pytest.mark.parametrize( + "args,expect", + ( + ("", "[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]"), + ("true", "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]"), + ('by="value"', "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]"), + ("reverse=true", "[('c', 2), ('b', 1), ('AB', 3), ('aa', 0)]"), + ), + ) def test_dictsort(self, env, args, expect): - t = env.from_string(f"{{{{ foo|dictsort({args}) }}}}") + t = env.from_string(f"{{{{ foo|dictsort({args}) }}}}") out = t.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3}) assert out == expect def test_batch(self, env): - tmpl = env.from_string("{{ foo|batch(3)|list }}|{{ foo|batch(3, 'X')|list }}") + tmpl = env.from_string("{{ foo|batch(3)|list }}|{{ foo|batch(3, 'X')|list }}") out = tmpl.render(foo=list(range(10))) - assert out == ( - "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|" - "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]" - ) + assert out == ( + "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|" + "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]" + ) def test_slice(self, env): - tmpl = env.from_string("{{ foo|slice(3)|list }}|{{ foo|slice(3, 'X')|list }}") + tmpl = env.from_string("{{ foo|slice(3)|list }}|{{ foo|slice(3, 'X')|list }}") out = tmpl.render(foo=list(range(10))) - assert out == ( - "[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|" - "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]" - ) + assert out == ( + "[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|" + "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]" + ) def test_escape(self, env): - tmpl = env.from_string("""{{ '<">&'|escape }}""") + tmpl = env.from_string("""{{ '<">&'|escape }}""") out = tmpl.render() - assert out == "<">&" - - @pytest.mark.parametrize( - ("chars", "expect"), [(None, "..stays.."), (".", " ..stays"), (" .", "stays")] - ) - def test_trim(self, env, chars, expect): - tmpl = env.from_string("{{ foo|trim(chars) }}") - out = tmpl.render(foo=" ..stays..", chars=chars) - assert out == expect - + assert out == "<">&" + + @pytest.mark.parametrize( + ("chars", "expect"), [(None, "..stays.."), (".", " ..stays"), (" .", "stays")] + ) + def test_trim(self, env, chars, expect): + tmpl = env.from_string("{{ foo|trim(chars) }}") + out = tmpl.render(foo=" ..stays..", chars=chars) + assert out == expect + def test_striptags(self, env): - tmpl = env.from_string("""{{ foo|striptags }}""") - out = tmpl.render( - foo=' <p>just a small \n <a href="#">' - "example</a> link</p>\n<p>to a webpage</p> " - "<!-- <p>and some commented stuff</p> -->" - ) - assert out == "just a small example link to a webpage" + tmpl = env.from_string("""{{ foo|striptags }}""") + out = tmpl.render( + foo=' <p>just a small \n <a href="#">' + "example</a> link</p>\n<p>to a webpage</p> " + "<!-- <p>and some commented stuff</p> -->" + ) + assert out == "just a small example link to a webpage" def test_filesizeformat(self, env): tmpl = env.from_string( - "{{ 100|filesizeformat }}|" - "{{ 1000|filesizeformat }}|" - "{{ 1000000|filesizeformat }}|" - "{{ 1000000000|filesizeformat }}|" - "{{ 1000000000000|filesizeformat }}|" - "{{ 100|filesizeformat(true) }}|" - "{{ 1000|filesizeformat(true) }}|" - "{{ 1000000|filesizeformat(true) }}|" - "{{ 1000000000|filesizeformat(true) }}|" - "{{ 1000000000000|filesizeformat(true) }}" + "{{ 100|filesizeformat }}|" + "{{ 1000|filesizeformat }}|" + "{{ 1000000|filesizeformat }}|" + "{{ 1000000000|filesizeformat }}|" + "{{ 1000000000000|filesizeformat }}|" + "{{ 100|filesizeformat(true) }}|" + "{{ 1000|filesizeformat(true) }}|" + "{{ 1000000|filesizeformat(true) }}|" + "{{ 1000000000|filesizeformat(true) }}|" + "{{ 1000000000000|filesizeformat(true) }}" ) out = tmpl.render() assert out == ( - "100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|" - "1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB" + "100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|" + "1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB" ) def test_filesizeformat_issue59(self, env): tmpl = env.from_string( - "{{ 300|filesizeformat }}|" - "{{ 3000|filesizeformat }}|" - "{{ 3000000|filesizeformat }}|" - "{{ 3000000000|filesizeformat }}|" - "{{ 3000000000000|filesizeformat }}|" - "{{ 300|filesizeformat(true) }}|" - "{{ 3000|filesizeformat(true) }}|" - "{{ 3000000|filesizeformat(true) }}" + "{{ 300|filesizeformat }}|" + "{{ 3000|filesizeformat }}|" + "{{ 3000000|filesizeformat }}|" + "{{ 3000000000|filesizeformat }}|" + "{{ 3000000000000|filesizeformat }}|" + "{{ 300|filesizeformat(true) }}|" + "{{ 3000|filesizeformat(true) }}|" + "{{ 3000000|filesizeformat(true) }}" ) out = tmpl.render() assert out == ( - "300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|2.9 KiB|2.9 MiB" + "300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|2.9 KiB|2.9 MiB" ) def test_first(self, env): - tmpl = env.from_string("{{ foo|first }}") + tmpl = env.from_string("{{ foo|first }}") out = tmpl.render(foo=list(range(10))) - assert out == "0" - - @pytest.mark.parametrize( - ("value", "expect"), (("42", "42.0"), ("abc", "0.0"), ("32.32", "32.32")) - ) - def test_float(self, env, value, expect): - t = env.from_string("{{ value|float }}") - assert t.render(value=value) == expect - - def test_float_default(self, env): - t = env.from_string("{{ value|float(default=1.0) }}") - assert t.render(value="abc") == "1.0" - + assert out == "0" + + @pytest.mark.parametrize( + ("value", "expect"), (("42", "42.0"), ("abc", "0.0"), ("32.32", "32.32")) + ) + def test_float(self, env, value, expect): + t = env.from_string("{{ value|float }}") + assert t.render(value=value) == expect + + def test_float_default(self, env): + t = env.from_string("{{ value|float(default=1.0) }}") + assert t.render(value="abc") == "1.0" + def test_format(self, env): - tmpl = env.from_string("{{ '%s|%s'|format('a', 'b') }}") + tmpl = env.from_string("{{ '%s|%s'|format('a', 'b') }}") out = tmpl.render() - assert out == "a|b" - - @staticmethod - def _test_indent_multiline_template(env, markup=False): - text = "\n".join(["", "foo bar", '"baz"', ""]) - if markup: - text = Markup(text) - t = env.from_string("{{ foo|indent(2, false, false) }}") - assert t.render(foo=text) == '\n foo bar\n "baz"\n' - t = env.from_string("{{ foo|indent(2, false, true) }}") - assert t.render(foo=text) == '\n foo bar\n "baz"\n ' - t = env.from_string("{{ foo|indent(2, true, false) }}") - assert t.render(foo=text) == ' \n foo bar\n "baz"\n' - t = env.from_string("{{ foo|indent(2, true, true) }}") - assert t.render(foo=text) == ' \n foo bar\n "baz"\n ' - + assert out == "a|b" + + @staticmethod + def _test_indent_multiline_template(env, markup=False): + text = "\n".join(["", "foo bar", '"baz"', ""]) + if markup: + text = Markup(text) + t = env.from_string("{{ foo|indent(2, false, false) }}") + assert t.render(foo=text) == '\n foo bar\n "baz"\n' + t = env.from_string("{{ foo|indent(2, false, true) }}") + assert t.render(foo=text) == '\n foo bar\n "baz"\n ' + t = env.from_string("{{ foo|indent(2, true, false) }}") + assert t.render(foo=text) == ' \n foo bar\n "baz"\n' + t = env.from_string("{{ foo|indent(2, true, true) }}") + assert t.render(foo=text) == ' \n foo bar\n "baz"\n ' + def test_indent(self, env): - self._test_indent_multiline_template(env) + self._test_indent_multiline_template(env) t = env.from_string('{{ "jinja"|indent }}') - assert t.render() == "jinja" + assert t.render() == "jinja" t = env.from_string('{{ "jinja"|indent(first=true) }}') - assert t.render() == " jinja" + assert t.render() == " jinja" t = env.from_string('{{ "jinja"|indent(blank=true) }}') - assert t.render() == "jinja" - - def test_indent_markup_input(self, env): - """ - Tests cases where the filter input is a Markup type - """ - self._test_indent_multiline_template(env, markup=True) - - def test_indent_width_string(self, env): - t = env.from_string("{{ 'jinja\nflask'|indent(width='>>> ', first=True) }}") - assert t.render() == ">>> jinja\n>>> flask" - - @pytest.mark.parametrize( - ("value", "expect"), - ( - ("42", "42"), - ("abc", "0"), - ("32.32", "32"), - ("12345678901234567890", "12345678901234567890"), - ), - ) - def test_int(self, env, value, expect): - t = env.from_string("{{ value|int }}") - assert t.render(value=value) == expect - - @pytest.mark.parametrize( - ("value", "base", "expect"), - (("0x4d32", 16, "19762"), ("011", 8, "9"), ("0x33Z", 16, "0")), - ) - def test_int_base(self, env, value, base, expect): - t = env.from_string("{{ value|int(base=base) }}") - assert t.render(value=value, base=base) == expect - - def test_int_default(self, env): - t = env.from_string("{{ value|int(default=1) }}") - assert t.render(value="abc") == "1" - - def test_int_special_method(self, env): - class IntIsh: + assert t.render() == "jinja" + + def test_indent_markup_input(self, env): + """ + Tests cases where the filter input is a Markup type + """ + self._test_indent_multiline_template(env, markup=True) + + def test_indent_width_string(self, env): + t = env.from_string("{{ 'jinja\nflask'|indent(width='>>> ', first=True) }}") + assert t.render() == ">>> jinja\n>>> flask" + + @pytest.mark.parametrize( + ("value", "expect"), + ( + ("42", "42"), + ("abc", "0"), + ("32.32", "32"), + ("12345678901234567890", "12345678901234567890"), + ), + ) + def test_int(self, env, value, expect): + t = env.from_string("{{ value|int }}") + assert t.render(value=value) == expect + + @pytest.mark.parametrize( + ("value", "base", "expect"), + (("0x4d32", 16, "19762"), ("011", 8, "9"), ("0x33Z", 16, "0")), + ) + def test_int_base(self, env, value, base, expect): + t = env.from_string("{{ value|int(base=base) }}") + assert t.render(value=value, base=base) == expect + + def test_int_default(self, env): + t = env.from_string("{{ value|int(default=1) }}") + assert t.render(value="abc") == "1" + + def test_int_special_method(self, env): + class IntIsh: def __int__(self): return 42 - t = env.from_string("{{ value|int }}") - assert t.render(value=IntIsh()) == "42" + t = env.from_string("{{ value|int }}") + assert t.render(value=IntIsh()) == "42" def test_join(self, env): tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}') out = tmpl.render() - assert out == "1|2|3" + assert out == "1|2|3" env2 = Environment(autoescape=True) - tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}') - assert tmpl.render() == "<foo><span>foo</span>" + tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}') + assert tmpl.render() == "<foo><span>foo</span>" def test_join_attribute(self, env): - User = namedtuple("User", "username") - tmpl = env.from_string("""{{ users|join(', ', 'username') }}""") - assert tmpl.render(users=map(User, ["foo", "bar"])) == "foo, bar" + User = namedtuple("User", "username") + tmpl = env.from_string("""{{ users|join(', ', 'username') }}""") + assert tmpl.render(users=map(User, ["foo", "bar"])) == "foo, bar" def test_last(self, env): - tmpl = env.from_string("""{{ foo|last }}""") + tmpl = env.from_string("""{{ foo|last }}""") out = tmpl.render(foo=list(range(10))) - assert out == "9" + assert out == "9" def test_length(self, env): - tmpl = env.from_string("""{{ "hello world"|length }}""") + tmpl = env.from_string("""{{ "hello world"|length }}""") out = tmpl.render() - assert out == "11" + assert out == "11" def test_lower(self, env): - tmpl = env.from_string("""{{ "FOO"|lower }}""") + tmpl = env.from_string("""{{ "FOO"|lower }}""") out = tmpl.render() - assert out == "foo" + assert out == "foo" def test_pprint(self, env): from pprint import pformat - - tmpl = env.from_string("""{{ data|pprint }}""") + + tmpl = env.from_string("""{{ data|pprint }}""") data = list(range(1000)) assert tmpl.render(data=data) == pformat(data) @@ -263,200 +263,200 @@ class TestFilter: state = random.getstate() request.addfinalizer(lambda: random.setstate(state)) # generate the random values from a known seed - random.seed("jinja") - expected = [random.choice("1234567890") for _ in range(10)] + random.seed("jinja") + expected = [random.choice("1234567890") for _ in range(10)] # check that the random sequence is generated again by a template # ensures that filter result is not constant folded - random.seed("jinja") + random.seed("jinja") t = env.from_string('{{ "1234567890"|random }}') for value in expected: assert t.render() == value def test_reverse(self, env): - tmpl = env.from_string( - "{{ 'foobar'|reverse|join }}|{{ [1, 2, 3]|reverse|list }}" - ) - assert tmpl.render() == "raboof|[3, 2, 1]" + tmpl = env.from_string( + "{{ 'foobar'|reverse|join }}|{{ [1, 2, 3]|reverse|list }}" + ) + assert tmpl.render() == "raboof|[3, 2, 1]" def test_string(self, env): x = [1, 2, 3, 4, 5] - tmpl = env.from_string("""{{ obj|string }}""") - assert tmpl.render(obj=x) == str(x) + tmpl = env.from_string("""{{ obj|string }}""") + assert tmpl.render(obj=x) == str(x) def test_title(self, env): - tmpl = env.from_string("""{{ "foo bar"|title }}""") + tmpl = env.from_string("""{{ "foo bar"|title }}""") assert tmpl.render() == "Foo Bar" - tmpl = env.from_string("""{{ "foo's bar"|title }}""") + tmpl = env.from_string("""{{ "foo's bar"|title }}""") assert tmpl.render() == "Foo's Bar" - tmpl = env.from_string("""{{ "foo bar"|title }}""") + tmpl = env.from_string("""{{ "foo bar"|title }}""") assert tmpl.render() == "Foo Bar" - tmpl = env.from_string("""{{ "f bar f"|title }}""") + tmpl = env.from_string("""{{ "f bar f"|title }}""") assert tmpl.render() == "F Bar F" - tmpl = env.from_string("""{{ "foo-bar"|title }}""") + tmpl = env.from_string("""{{ "foo-bar"|title }}""") assert tmpl.render() == "Foo-Bar" - tmpl = env.from_string("""{{ "foo\tbar"|title }}""") + tmpl = env.from_string("""{{ "foo\tbar"|title }}""") assert tmpl.render() == "Foo\tBar" - tmpl = env.from_string("""{{ "FOO\tBAR"|title }}""") + tmpl = env.from_string("""{{ "FOO\tBAR"|title }}""") assert tmpl.render() == "Foo\tBar" - tmpl = env.from_string("""{{ "foo (bar)"|title }}""") + tmpl = env.from_string("""{{ "foo (bar)"|title }}""") assert tmpl.render() == "Foo (Bar)" - tmpl = env.from_string("""{{ "foo {bar}"|title }}""") + tmpl = env.from_string("""{{ "foo {bar}"|title }}""") assert tmpl.render() == "Foo {Bar}" - tmpl = env.from_string("""{{ "foo [bar]"|title }}""") + tmpl = env.from_string("""{{ "foo [bar]"|title }}""") assert tmpl.render() == "Foo [Bar]" - tmpl = env.from_string("""{{ "foo <bar>"|title }}""") + tmpl = env.from_string("""{{ "foo <bar>"|title }}""") assert tmpl.render() == "Foo <Bar>" class Foo: def __str__(self): - return "foo-bar" + return "foo-bar" - tmpl = env.from_string("""{{ data|title }}""") + tmpl = env.from_string("""{{ data|title }}""") out = tmpl.render(data=Foo()) - assert out == "Foo-Bar" + assert out == "Foo-Bar" def test_truncate(self, env): tmpl = env.from_string( '{{ data|truncate(15, true, ">>>") }}|' '{{ data|truncate(15, false, ">>>") }}|' - "{{ smalldata|truncate(15) }}" + "{{ smalldata|truncate(15) }}" ) - out = tmpl.render(data="foobar baz bar" * 1000, smalldata="foobar baz bar") - assert out == "foobar baz b>>>|foobar baz>>>|foobar baz bar" + out = tmpl.render(data="foobar baz bar" * 1000, smalldata="foobar baz bar") + assert out == "foobar baz b>>>|foobar baz>>>|foobar baz bar" def test_truncate_very_short(self, env): tmpl = env.from_string( - '{{ "foo bar baz"|truncate(9) }}|{{ "foo bar baz"|truncate(9, true) }}' + '{{ "foo bar baz"|truncate(9) }}|{{ "foo bar baz"|truncate(9, true) }}' ) out = tmpl.render() - assert out == "foo bar baz|foo bar baz" + assert out == "foo bar baz|foo bar baz" def test_truncate_end_length(self, env): tmpl = env.from_string('{{ "Joel is a slug"|truncate(7, true) }}') out = tmpl.render() - assert out == "Joel..." + assert out == "Joel..." def test_upper(self, env): tmpl = env.from_string('{{ "foo"|upper }}') - assert tmpl.render() == "FOO" + assert tmpl.render() == "FOO" def test_urlize(self, env): - tmpl = env.from_string('{{ "foo example.org bar"|urlize }}') - assert tmpl.render() == ( - 'foo <a href="https://example.org" rel="noopener">' "example.org</a> bar" - ) - tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}') + tmpl = env.from_string('{{ "foo example.org bar"|urlize }}') + assert tmpl.render() == ( + 'foo <a href="https://example.org" rel="noopener">' "example.org</a> bar" + ) + tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}') assert tmpl.render() == ( 'foo <a href="http://www.example.com/" rel="noopener">' - "http://www.example.com/</a> bar" + "http://www.example.com/</a> bar" + ) + tmpl = env.from_string('{{ "foo mailto:email@example.com bar"|urlize }}') + assert tmpl.render() == ( + 'foo <a href="mailto:email@example.com">email@example.com</a> bar' + ) + tmpl = env.from_string('{{ "foo email@example.com bar"|urlize }}') + assert tmpl.render() == ( + 'foo <a href="mailto:email@example.com">email@example.com</a> bar' ) - tmpl = env.from_string('{{ "foo mailto:email@example.com bar"|urlize }}') - assert tmpl.render() == ( - 'foo <a href="mailto:email@example.com">email@example.com</a> bar' - ) - tmpl = env.from_string('{{ "foo email@example.com bar"|urlize }}') - assert tmpl.render() == ( - 'foo <a href="mailto:email@example.com">email@example.com</a> bar' - ) def test_urlize_rel_policy(self): env = Environment() - env.policies["urlize.rel"] = None - tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}') + env.policies["urlize.rel"] = None + tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}') assert tmpl.render() == ( - 'foo <a href="http://www.example.com/">http://www.example.com/</a> bar' + 'foo <a href="http://www.example.com/">http://www.example.com/</a> bar' ) def test_urlize_target_parameter(self, env): tmpl = env.from_string( '{{ "foo http://www.example.com/ bar"|urlize(target="_blank") }}' ) - assert ( - tmpl.render() - == 'foo <a href="http://www.example.com/" rel="noopener" target="_blank">' - "http://www.example.com/</a> bar" - ) - - def test_urlize_extra_schemes_parameter(self, env): - tmpl = env.from_string( - '{{ "foo tel:+1-514-555-1234 ftp://localhost bar"|' - 'urlize(extra_schemes=["tel:", "ftp:"]) }}' - ) - assert tmpl.render() == ( - 'foo <a href="tel:+1-514-555-1234" rel="noopener">' - 'tel:+1-514-555-1234</a> <a href="ftp://localhost" rel="noopener">' - "ftp://localhost</a> bar" - ) - + assert ( + tmpl.render() + == 'foo <a href="http://www.example.com/" rel="noopener" target="_blank">' + "http://www.example.com/</a> bar" + ) + + def test_urlize_extra_schemes_parameter(self, env): + tmpl = env.from_string( + '{{ "foo tel:+1-514-555-1234 ftp://localhost bar"|' + 'urlize(extra_schemes=["tel:", "ftp:"]) }}' + ) + assert tmpl.render() == ( + 'foo <a href="tel:+1-514-555-1234" rel="noopener">' + 'tel:+1-514-555-1234</a> <a href="ftp://localhost" rel="noopener">' + "ftp://localhost</a> bar" + ) + def test_wordcount(self, env): tmpl = env.from_string('{{ "foo bar baz"|wordcount }}') - assert tmpl.render() == "3" + assert tmpl.render() == "3" + + strict_env = Environment(undefined=StrictUndefined) + t = strict_env.from_string("{{ s|wordcount }}") + with pytest.raises(UndefinedError): + t.render() - strict_env = Environment(undefined=StrictUndefined) - t = strict_env.from_string("{{ s|wordcount }}") - with pytest.raises(UndefinedError): - t.render() - def test_block(self, env): - tmpl = env.from_string("{% filter lower|escape %}<HEHE>{% endfilter %}") - assert tmpl.render() == "<hehe>" + tmpl = env.from_string("{% filter lower|escape %}<HEHE>{% endfilter %}") + assert tmpl.render() == "<hehe>" def test_chaining(self, env): - tmpl = env.from_string("""{{ ['<foo>', '<bar>']|first|upper|escape }}""") - assert tmpl.render() == "<FOO>" + tmpl = env.from_string("""{{ ['<foo>', '<bar>']|first|upper|escape }}""") + assert tmpl.render() == "<FOO>" def test_sum(self, env): - tmpl = env.from_string("""{{ [1, 2, 3, 4, 5, 6]|sum }}""") - assert tmpl.render() == "21" + tmpl = env.from_string("""{{ [1, 2, 3, 4, 5, 6]|sum }}""") + assert tmpl.render() == "21" def test_sum_attributes(self, env): - tmpl = env.from_string("""{{ values|sum('value') }}""") - assert tmpl.render(values=[{"value": 23}, {"value": 1}, {"value": 18}]) == "42" + tmpl = env.from_string("""{{ values|sum('value') }}""") + assert tmpl.render(values=[{"value": 23}, {"value": 1}, {"value": 18}]) == "42" def test_sum_attributes_nested(self, env): - tmpl = env.from_string("""{{ values|sum('real.value') }}""") - assert ( - tmpl.render( - values=[ - {"real": {"value": 23}}, - {"real": {"value": 1}}, - {"real": {"value": 18}}, - ] - ) - == "42" - ) + tmpl = env.from_string("""{{ values|sum('real.value') }}""") + assert ( + tmpl.render( + values=[ + {"real": {"value": 23}}, + {"real": {"value": 1}}, + {"real": {"value": 18}}, + ] + ) + == "42" + ) def test_sum_attributes_tuple(self, env): - tmpl = env.from_string("""{{ values.items()|sum('1') }}""") - assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18}) == "42" + tmpl = env.from_string("""{{ values.items()|sum('1') }}""") + assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18}) == "42" def test_abs(self, env): - tmpl = env.from_string("""{{ -1|abs }}|{{ 1|abs }}""") - assert tmpl.render() == "1|1", tmpl.render() + tmpl = env.from_string("""{{ -1|abs }}|{{ 1|abs }}""") + assert tmpl.render() == "1|1", tmpl.render() def test_round_positive(self, env): - tmpl = env.from_string( - "{{ 2.7|round }}|{{ 2.1|round }}|" - "{{ 2.1234|round(3, 'floor') }}|" - "{{ 2.1|round(0, 'ceil') }}" - ) - assert tmpl.render() == "3.0|2.0|2.123|3.0", tmpl.render() + tmpl = env.from_string( + "{{ 2.7|round }}|{{ 2.1|round }}|" + "{{ 2.1234|round(3, 'floor') }}|" + "{{ 2.1|round(0, 'ceil') }}" + ) + assert tmpl.render() == "3.0|2.0|2.123|3.0", tmpl.render() def test_round_negative(self, env): - tmpl = env.from_string( - "{{ 21.3|round(-1)}}|" - "{{ 21.3|round(-1, 'ceil')}}|" - "{{ 21.3|round(-1, 'floor')}}" - ) - assert tmpl.render() == "20.0|30.0|20.0", tmpl.render() + tmpl = env.from_string( + "{{ 21.3|round(-1)}}|" + "{{ 21.3|round(-1, 'ceil')}}|" + "{{ 21.3|round(-1, 'floor')}}" + ) + assert tmpl.render() == "20.0|30.0|20.0", tmpl.render() def test_xmlattr(self, env): tmpl = env.from_string( "{{ {'foo': 42, 'bar': 23, 'fish': none, " - "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}" - ) + "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}" + ) out = tmpl.render().split() assert len(out) == 3 assert 'foo="42"' in out @@ -464,61 +464,61 @@ class TestFilter: assert 'blub:blub="<?>"' in out def test_sort1(self, env): - tmpl = env.from_string("{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}") - assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]" + tmpl = env.from_string("{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}") + assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]" def test_sort2(self, env): tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}') - assert tmpl.render() == "AbcD" + assert tmpl.render() == "AbcD" def test_sort3(self, env): - tmpl = env.from_string("""{{ ['foo', 'Bar', 'blah']|sort }}""") + tmpl = env.from_string("""{{ ['foo', 'Bar', 'blah']|sort }}""") assert tmpl.render() == "['Bar', 'blah', 'foo']" def test_sort4(self, env): - tmpl = env.from_string("""{{ items|sort(attribute='value')|join }}""") - assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == "1234" - - def test_sort5(self, env): - tmpl = env.from_string("""{{ items|sort(attribute='value.0')|join }}""") - assert tmpl.render(items=map(Magic, [[3], [2], [4], [1]])) == "[1][2][3][4]" - - def test_sort6(self, env): - tmpl = env.from_string("""{{ items|sort(attribute='value1,value2')|join }}""") - assert ( - tmpl.render( - items=map( - lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)] - ) - ) - == "(2,1)(2,2)(2,5)(3,1)" - ) - - def test_sort7(self, env): - tmpl = env.from_string("""{{ items|sort(attribute='value2,value1')|join }}""") - assert ( - tmpl.render( - items=map( - lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)] - ) - ) - == "(2,1)(3,1)(2,2)(2,5)" - ) - - def test_sort8(self, env): - tmpl = env.from_string( - """{{ items|sort(attribute='value1.0,value2.0')|join }}""" - ) - assert ( - tmpl.render( - items=map( - lambda x: Magic2(x[0], x[1]), - [([3], [1]), ([2], [2]), ([2], [1]), ([2], [5])], - ) - ) - == "([2],[1])([2],[2])([2],[5])([3],[1])" - ) - + tmpl = env.from_string("""{{ items|sort(attribute='value')|join }}""") + assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == "1234" + + def test_sort5(self, env): + tmpl = env.from_string("""{{ items|sort(attribute='value.0')|join }}""") + assert tmpl.render(items=map(Magic, [[3], [2], [4], [1]])) == "[1][2][3][4]" + + def test_sort6(self, env): + tmpl = env.from_string("""{{ items|sort(attribute='value1,value2')|join }}""") + assert ( + tmpl.render( + items=map( + lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)] + ) + ) + == "(2,1)(2,2)(2,5)(3,1)" + ) + + def test_sort7(self, env): + tmpl = env.from_string("""{{ items|sort(attribute='value2,value1')|join }}""") + assert ( + tmpl.render( + items=map( + lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)] + ) + ) + == "(2,1)(3,1)(2,2)(2,5)" + ) + + def test_sort8(self, env): + tmpl = env.from_string( + """{{ items|sort(attribute='value1.0,value2.0')|join }}""" + ) + assert ( + tmpl.render( + items=map( + lambda x: Magic2(x[0], x[1]), + [([3], [1]), ([2], [2]), ([2], [1]), ([2], [5])], + ) + ) + == "([2],[1])([2],[2])([2],[5])([3],[1])" + ) + def test_unique(self, env): t = env.from_string('{{ "".join(["b", "A", "a", "b"]|unique) }}') assert t.render() == "bA" @@ -529,315 +529,315 @@ class TestFilter: def test_unique_attribute(self, env): t = env.from_string("{{ items|unique(attribute='value')|join }}") - assert t.render(items=map(Magic, [3, 2, 4, 1, 2])) == "3241" - - @pytest.mark.parametrize( - "source,expect", - ( - ('{{ ["a", "B"]|min }}', "a"), - ('{{ ["a", "B"]|min(case_sensitive=true) }}', "B"), - ("{{ []|min }}", ""), - ('{{ ["a", "B"]|max }}', "B"), - ('{{ ["a", "B"]|max(case_sensitive=true) }}', "a"), - ("{{ []|max }}", ""), - ), - ) + assert t.render(items=map(Magic, [3, 2, 4, 1, 2])) == "3241" + + @pytest.mark.parametrize( + "source,expect", + ( + ('{{ ["a", "B"]|min }}', "a"), + ('{{ ["a", "B"]|min(case_sensitive=true) }}', "B"), + ("{{ []|min }}", ""), + ('{{ ["a", "B"]|max }}', "B"), + ('{{ ["a", "B"]|max(case_sensitive=true) }}', "a"), + ("{{ []|max }}", ""), + ), + ) def test_min_max(self, env, source, expect): t = env.from_string(source) assert t.render() == expect - @pytest.mark.parametrize(("name", "expect"), [("min", "1"), ("max", "9")]) + @pytest.mark.parametrize(("name", "expect"), [("min", "1"), ("max", "9")]) def test_min_max_attribute(self, env, name, expect): - t = env.from_string("{{ items|" + name + '(attribute="value") }}') + t = env.from_string("{{ items|" + name + '(attribute="value") }}') assert t.render(items=map(Magic, [5, 1, 9])) == expect def test_groupby(self, env): - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {%- for grouper, list in [{'foo': 1, 'bar': 2}, {'foo': 2, 'bar': 3}, {'foo': 1, 'bar': 1}, {'foo': 3, 'bar': 4}]|groupby('foo') -%} {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}| - {%- endfor %}""" - ) - assert tmpl.render().split("|") == ["1: 1, 2: 1, 1", "2: 2, 3", "3: 3, 4", ""] + {%- endfor %}""" + ) + assert tmpl.render().split("|") == ["1: 1, 2: 1, 1", "2: 2, 3", "3: 3, 4", ""] def test_groupby_tuple_index(self, env): - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%} {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}| - {%- endfor %}""" - ) - assert tmpl.render() == "a:1:2|b:1|" + {%- endfor %}""" + ) + assert tmpl.render() == "a:1:2|b:1|" def test_groupby_multidot(self, env): - Date = namedtuple("Date", "day,month,year") - Article = namedtuple("Article", "title,date") + Date = namedtuple("Date", "day,month,year") + Article = namedtuple("Article", "title,date") articles = [ - Article("aha", Date(1, 1, 1970)), - Article("interesting", Date(2, 1, 1970)), - Article("really?", Date(3, 1, 1970)), - Article("totally not", Date(1, 1, 1971)), + Article("aha", Date(1, 1, 1970)), + Article("interesting", Date(2, 1, 1970)), + Article("really?", Date(3, 1, 1970)), + Article("totally not", Date(1, 1, 1971)), ] - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {%- for year, list in articles|groupby('date.year') -%} {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}| - {%- endfor %}""" - ) - assert tmpl.render(articles=articles).split("|") == [ - "1970[aha][interesting][really?]", - "1971[totally not]", - "", + {%- endfor %}""" + ) + assert tmpl.render(articles=articles).split("|") == [ + "1970[aha][interesting][really?]", + "1971[totally not]", + "", ] - def test_groupby_default(self, env): - tmpl = env.from_string( - "{% for city, items in users|groupby('city', default='NY') %}" - "{{ city }}: {{ items|map(attribute='name')|join(', ') }}\n" - "{% endfor %}" - ) - out = tmpl.render( - users=[ - {"name": "emma", "city": "NY"}, - {"name": "smith", "city": "WA"}, - {"name": "john"}, - ] - ) - assert out == "NY: emma, john\nWA: smith\n" - + def test_groupby_default(self, env): + tmpl = env.from_string( + "{% for city, items in users|groupby('city', default='NY') %}" + "{{ city }}: {{ items|map(attribute='name')|join(', ') }}\n" + "{% endfor %}" + ) + out = tmpl.render( + users=[ + {"name": "emma", "city": "NY"}, + {"name": "smith", "city": "WA"}, + {"name": "john"}, + ] + ) + assert out == "NY: emma, john\nWA: smith\n" + def test_filtertag(self, env): - tmpl = env.from_string( - "{% filter upper|replace('FOO', 'foo') %}foobar{% endfilter %}" - ) - assert tmpl.render() == "fooBAR" + tmpl = env.from_string( + "{% filter upper|replace('FOO', 'foo') %}foobar{% endfilter %}" + ) + assert tmpl.render() == "fooBAR" def test_replace(self, env): env = Environment() tmpl = env.from_string('{{ string|replace("o", 42) }}') - assert tmpl.render(string="<foo>") == "<f4242>" + assert tmpl.render(string="<foo>") == "<f4242>" env = Environment(autoescape=True) tmpl = env.from_string('{{ string|replace("o", 42) }}') - assert tmpl.render(string="<foo>") == "<f4242>" + assert tmpl.render(string="<foo>") == "<f4242>" tmpl = env.from_string('{{ string|replace("<", 42) }}') - assert tmpl.render(string="<foo>") == "42foo>" + assert tmpl.render(string="<foo>") == "42foo>" tmpl = env.from_string('{{ string|replace("o", ">x<") }}') - assert tmpl.render(string=Markup("foo")) == "f>x<>x<" + assert tmpl.render(string=Markup("foo")) == "f>x<>x<" def test_forceescape(self, env): - tmpl = env.from_string("{{ x|forceescape }}") - assert tmpl.render(x=Markup("<div />")) == "<div />" + tmpl = env.from_string("{{ x|forceescape }}") + assert tmpl.render(x=Markup("<div />")) == "<div />" def test_safe(self, env): env = Environment(autoescape=True) tmpl = env.from_string('{{ "<div>foo</div>"|safe }}') - assert tmpl.render() == "<div>foo</div>" + assert tmpl.render() == "<div>foo</div>" tmpl = env.from_string('{{ "<div>foo</div>" }}') - assert tmpl.render() == "<div>foo</div>" - - @pytest.mark.parametrize( - ("value", "expect"), - [ - ("Hello, world!", "Hello%2C%20world%21"), - ("Hello, world\u203d", "Hello%2C%20world%E2%80%BD"), - ({"f": 1}, "f=1"), - ([("f", 1), ("z", 2)], "f=1&z=2"), - ({"\u203d": 1}, "%E2%80%BD=1"), - ({0: 1}, "0=1"), - ([("a b/c", "a b/c")], "a+b%2Fc=a+b%2Fc"), - ("a b/c", "a%20b/c"), - ], - ) - def test_urlencode(self, value, expect): - e = Environment(autoescape=True) - t = e.from_string("{{ value|urlencode }}") - assert t.render(value=value) == expect + assert tmpl.render() == "<div>foo</div>" + + @pytest.mark.parametrize( + ("value", "expect"), + [ + ("Hello, world!", "Hello%2C%20world%21"), + ("Hello, world\u203d", "Hello%2C%20world%E2%80%BD"), + ({"f": 1}, "f=1"), + ([("f", 1), ("z", 2)], "f=1&z=2"), + ({"\u203d": 1}, "%E2%80%BD=1"), + ({0: 1}, "0=1"), + ([("a b/c", "a b/c")], "a+b%2Fc=a+b%2Fc"), + ("a b/c", "a%20b/c"), + ], + ) + def test_urlencode(self, value, expect): + e = Environment(autoescape=True) + t = e.from_string("{{ value|urlencode }}") + assert t.render(value=value) == expect def test_simple_map(self, env): env = Environment() tmpl = env.from_string('{{ ["1", "2", "3"]|map("int")|sum }}') - assert tmpl.render() == "6" + assert tmpl.render() == "6" + + def test_map_sum(self, env): + tmpl = env.from_string('{{ [[1,2], [3], [4,5,6]]|map("sum")|list }}') + assert tmpl.render() == "[3, 3, 15]" - def test_map_sum(self, env): - tmpl = env.from_string('{{ [[1,2], [3], [4,5,6]]|map("sum")|list }}') - assert tmpl.render() == "[3, 3, 15]" - def test_attribute_map(self, env): - User = namedtuple("User", "name") + User = namedtuple("User", "name") env = Environment() users = [ - User("john"), - User("jane"), - User("mike"), + User("john"), + User("jane"), + User("mike"), ] tmpl = env.from_string('{{ users|map(attribute="name")|join("|") }}') - assert tmpl.render(users=users) == "john|jane|mike" + assert tmpl.render(users=users) == "john|jane|mike" def test_empty_map(self, env): env = Environment() tmpl = env.from_string('{{ none|map("upper")|list }}') - assert tmpl.render() == "[]" - - def test_map_default(self, env): - Fullname = namedtuple("Fullname", "firstname,lastname") - Firstname = namedtuple("Firstname", "firstname") - env = Environment() - tmpl = env.from_string( - '{{ users|map(attribute="lastname", default="smith")|join(", ") }}' - ) - test_list = env.from_string( - '{{ users|map(attribute="lastname", default=["smith","x"])|join(", ") }}' - ) - test_str = env.from_string( - '{{ users|map(attribute="lastname", default="")|join(", ") }}' - ) - users = [ - Fullname("john", "lennon"), - Fullname("jane", "edwards"), - Fullname("jon", None), - Firstname("mike"), - ] - assert tmpl.render(users=users) == "lennon, edwards, None, smith" - assert test_list.render(users=users) == "lennon, edwards, None, ['smith', 'x']" - assert test_str.render(users=users) == "lennon, edwards, None, " - + assert tmpl.render() == "[]" + + def test_map_default(self, env): + Fullname = namedtuple("Fullname", "firstname,lastname") + Firstname = namedtuple("Firstname", "firstname") + env = Environment() + tmpl = env.from_string( + '{{ users|map(attribute="lastname", default="smith")|join(", ") }}' + ) + test_list = env.from_string( + '{{ users|map(attribute="lastname", default=["smith","x"])|join(", ") }}' + ) + test_str = env.from_string( + '{{ users|map(attribute="lastname", default="")|join(", ") }}' + ) + users = [ + Fullname("john", "lennon"), + Fullname("jane", "edwards"), + Fullname("jon", None), + Firstname("mike"), + ] + assert tmpl.render(users=users) == "lennon, edwards, None, smith" + assert test_list.render(users=users) == "lennon, edwards, None, ['smith', 'x']" + assert test_str.render(users=users) == "lennon, edwards, None, " + def test_simple_select(self, env): env = Environment() tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|select("odd")|join("|") }}') - assert tmpl.render() == "1|3|5" + assert tmpl.render() == "1|3|5" def test_bool_select(self, env): env = Environment() - tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|select|join("|") }}') - assert tmpl.render() == "1|2|3|4|5" + tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|select|join("|") }}') + assert tmpl.render() == "1|2|3|4|5" def test_simple_reject(self, env): env = Environment() tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|reject("odd")|join("|") }}') - assert tmpl.render() == "2|4" + assert tmpl.render() == "2|4" def test_bool_reject(self, env): env = Environment() - tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|reject|join("|") }}') - assert tmpl.render() == "None|False|0" + tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|reject|join("|") }}') + assert tmpl.render() == "None|False|0" def test_simple_select_attr(self, env): - User = namedtuple("User", "name,is_active") + User = namedtuple("User", "name,is_active") env = Environment() users = [ - User("john", True), - User("jane", True), - User("mike", False), + User("john", True), + User("jane", True), + User("mike", False), ] tmpl = env.from_string( - '{{ users|selectattr("is_active")|map(attribute="name")|join("|") }}' + '{{ users|selectattr("is_active")|map(attribute="name")|join("|") }}' ) - assert tmpl.render(users=users) == "john|jane" + assert tmpl.render(users=users) == "john|jane" def test_simple_reject_attr(self, env): - User = namedtuple("User", "name,is_active") + User = namedtuple("User", "name,is_active") env = Environment() users = [ - User("john", True), - User("jane", True), - User("mike", False), + User("john", True), + User("jane", True), + User("mike", False), ] - tmpl = env.from_string( - '{{ users|rejectattr("is_active")|map(attribute="name")|join("|") }}' - ) - assert tmpl.render(users=users) == "mike" + tmpl = env.from_string( + '{{ users|rejectattr("is_active")|map(attribute="name")|join("|") }}' + ) + assert tmpl.render(users=users) == "mike" def test_func_select_attr(self, env): - User = namedtuple("User", "id,name") + User = namedtuple("User", "id,name") env = Environment() users = [ - User(1, "john"), - User(2, "jane"), - User(3, "mike"), + User(1, "john"), + User(2, "jane"), + User(3, "mike"), ] - tmpl = env.from_string( - '{{ users|selectattr("id", "odd")|map(attribute="name")|join("|") }}' - ) - assert tmpl.render(users=users) == "john|mike" + tmpl = env.from_string( + '{{ users|selectattr("id", "odd")|map(attribute="name")|join("|") }}' + ) + assert tmpl.render(users=users) == "john|mike" def test_func_reject_attr(self, env): - User = namedtuple("User", "id,name") + User = namedtuple("User", "id,name") env = Environment() users = [ - User(1, "john"), - User(2, "jane"), - User(3, "mike"), + User(1, "john"), + User(2, "jane"), + User(3, "mike"), ] - tmpl = env.from_string( - '{{ users|rejectattr("id", "odd")|map(attribute="name")|join("|") }}' - ) - assert tmpl.render(users=users) == "jane" + tmpl = env.from_string( + '{{ users|rejectattr("id", "odd")|map(attribute="name")|join("|") }}' + ) + assert tmpl.render(users=users) == "jane" def test_json_dump(self): env = Environment(autoescape=True) - t = env.from_string("{{ x|tojson }}") - assert t.render(x={"foo": "bar"}) == '{"foo": "bar"}' - assert t.render(x="\"ba&r'") == r'"\"ba\u0026r\u0027"' - assert t.render(x="<bar>") == r'"\u003cbar\u003e"' + t = env.from_string("{{ x|tojson }}") + assert t.render(x={"foo": "bar"}) == '{"foo": "bar"}' + assert t.render(x="\"ba&r'") == r'"\"ba\u0026r\u0027"' + assert t.render(x="<bar>") == r'"\u003cbar\u003e"' def my_dumps(value, **options): - assert options == {"foo": "bar"} - return "42" - - env.policies["json.dumps_function"] = my_dumps - env.policies["json.dumps_kwargs"] = {"foo": "bar"} - assert t.render(x=23) == "42" - - def test_wordwrap(self, env): - env.newline_sequence = "\n" - t = env.from_string("{{ s|wordwrap(20) }}") - result = t.render(s="Hello!\nThis is Jinja saying something.") - assert result == "Hello!\nThis is Jinja saying\nsomething." - - def test_filter_undefined(self, env): - with pytest.raises(TemplateAssertionError, match="No filter named 'f'"): - env.from_string("{{ var|f }}") - - def test_filter_undefined_in_if(self, env): - t = env.from_string("{%- if x is defined -%}{{ x|f }}{%- else -%}x{% endif %}") - assert t.render() == "x" - with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): - t.render(x=42) - - def test_filter_undefined_in_elif(self, env): - t = env.from_string( - "{%- if x is defined -%}{{ x }}{%- elif y is defined -%}" - "{{ y|f }}{%- else -%}foo{%- endif -%}" - ) - assert t.render() == "foo" - with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): - t.render(y=42) - - def test_filter_undefined_in_else(self, env): - t = env.from_string( - "{%- if x is not defined -%}foo{%- else -%}{{ x|f }}{%- endif -%}" - ) - assert t.render() == "foo" - with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): - t.render(x=42) - - def test_filter_undefined_in_nested_if(self, env): - t = env.from_string( - "{%- if x is not defined -%}foo{%- else -%}{%- if y " - "is defined -%}{{ y|f }}{%- endif -%}{{ x }}{%- endif -%}" - ) - assert t.render() == "foo" - assert t.render(x=42) == "42" - with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): - t.render(x=24, y=42) - - def test_filter_undefined_in_condexpr(self, env): - t1 = env.from_string("{{ x|f if x is defined else 'foo' }}") - t2 = env.from_string("{{ 'foo' if x is not defined else x|f }}") - assert t1.render() == t2.render() == "foo" - - with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): - t1.render(x=42) - t2.render(x=42) + assert options == {"foo": "bar"} + return "42" + + env.policies["json.dumps_function"] = my_dumps + env.policies["json.dumps_kwargs"] = {"foo": "bar"} + assert t.render(x=23) == "42" + + def test_wordwrap(self, env): + env.newline_sequence = "\n" + t = env.from_string("{{ s|wordwrap(20) }}") + result = t.render(s="Hello!\nThis is Jinja saying something.") + assert result == "Hello!\nThis is Jinja saying\nsomething." + + def test_filter_undefined(self, env): + with pytest.raises(TemplateAssertionError, match="No filter named 'f'"): + env.from_string("{{ var|f }}") + + def test_filter_undefined_in_if(self, env): + t = env.from_string("{%- if x is defined -%}{{ x|f }}{%- else -%}x{% endif %}") + assert t.render() == "x" + with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): + t.render(x=42) + + def test_filter_undefined_in_elif(self, env): + t = env.from_string( + "{%- if x is defined -%}{{ x }}{%- elif y is defined -%}" + "{{ y|f }}{%- else -%}foo{%- endif -%}" + ) + assert t.render() == "foo" + with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): + t.render(y=42) + + def test_filter_undefined_in_else(self, env): + t = env.from_string( + "{%- if x is not defined -%}foo{%- else -%}{{ x|f }}{%- endif -%}" + ) + assert t.render() == "foo" + with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): + t.render(x=42) + + def test_filter_undefined_in_nested_if(self, env): + t = env.from_string( + "{%- if x is not defined -%}foo{%- else -%}{%- if y " + "is defined -%}{{ y|f }}{%- endif -%}{{ x }}{%- endif -%}" + ) + assert t.render() == "foo" + assert t.render(x=42) == "42" + with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): + t.render(x=24, y=42) + + def test_filter_undefined_in_condexpr(self, env): + t1 = env.from_string("{{ x|f if x is defined else 'foo' }}") + t2 = env.from_string("{{ 'foo' if x is not defined else x|f }}") + assert t1.render() == t2.render() == "foo" + + with pytest.raises(TemplateRuntimeError, match="No filter named 'f'"): + t1.render(x=42) + t2.render(x=42) diff --git a/contrib/python/Jinja2/py3/tests/test_idtracking.py b/contrib/python/Jinja2/py3/tests/test_idtracking.py index 5573375cd4..4e1d2c3d45 100644 --- a/contrib/python/Jinja2/py3/tests/test_idtracking.py +++ b/contrib/python/Jinja2/py3/tests/test_idtracking.py @@ -4,287 +4,287 @@ from jinja2.idtracking import symbols_for_node def test_basics(): for_loop = nodes.For( - nodes.Name("foo", "store"), - nodes.Name("seq", "load"), - [nodes.Output([nodes.Name("foo", "load")])], - [], - None, - False, - ) - tmpl = nodes.Template( - [nodes.Assign(nodes.Name("foo", "store"), nodes.Name("bar", "load")), for_loop] - ) + nodes.Name("foo", "store"), + nodes.Name("seq", "load"), + [nodes.Output([nodes.Name("foo", "load")])], + [], + None, + False, + ) + tmpl = nodes.Template( + [nodes.Assign(nodes.Name("foo", "store"), nodes.Name("bar", "load")), for_loop] + ) sym = symbols_for_node(tmpl) assert sym.refs == { - "foo": "l_0_foo", - "bar": "l_0_bar", - "seq": "l_0_seq", + "foo": "l_0_foo", + "bar": "l_0_bar", + "seq": "l_0_seq", } assert sym.loads == { - "l_0_foo": ("undefined", None), - "l_0_bar": ("resolve", "bar"), - "l_0_seq": ("resolve", "seq"), + "l_0_foo": ("undefined", None), + "l_0_bar": ("resolve", "bar"), + "l_0_seq": ("resolve", "seq"), } sym = symbols_for_node(for_loop, sym) assert sym.refs == { - "foo": "l_1_foo", + "foo": "l_1_foo", } assert sym.loads == { - "l_1_foo": ("param", None), + "l_1_foo": ("param", None), } def test_complex(): - title_block = nodes.Block( - "title", [nodes.Output([nodes.TemplateData("Page Title")])], False, False - ) + title_block = nodes.Block( + "title", [nodes.Output([nodes.TemplateData("Page Title")])], False, False + ) - render_title_macro = nodes.Macro( - "render_title", - [nodes.Name("title", "param")], - [], - [ - nodes.Output( - [ - nodes.TemplateData('\n <div class="title">\n <h1>'), - nodes.Name("title", "load"), - nodes.TemplateData("</h1>\n <p>"), - nodes.Name("subtitle", "load"), - nodes.TemplateData("</p>\n "), - ] - ), - nodes.Assign( - nodes.Name("subtitle", "store"), nodes.Const("something else") - ), - nodes.Output( - [ - nodes.TemplateData("\n <p>"), - nodes.Name("subtitle", "load"), - nodes.TemplateData("</p>\n </div>\n"), - nodes.If( - nodes.Name("something", "load"), - [ - nodes.Assign( - nodes.Name("title_upper", "store"), - nodes.Filter( - nodes.Name("title", "load"), - "upper", - [], - [], - None, - None, - ), - ), - nodes.Output( - [ - nodes.Name("title_upper", "load"), - nodes.Call( - nodes.Name("render_title", "load"), - [nodes.Const("Aha")], - [], - None, - None, - ), - ] - ), - ], - [], - [], - ), - ] - ), - ], - ) + render_title_macro = nodes.Macro( + "render_title", + [nodes.Name("title", "param")], + [], + [ + nodes.Output( + [ + nodes.TemplateData('\n <div class="title">\n <h1>'), + nodes.Name("title", "load"), + nodes.TemplateData("</h1>\n <p>"), + nodes.Name("subtitle", "load"), + nodes.TemplateData("</p>\n "), + ] + ), + nodes.Assign( + nodes.Name("subtitle", "store"), nodes.Const("something else") + ), + nodes.Output( + [ + nodes.TemplateData("\n <p>"), + nodes.Name("subtitle", "load"), + nodes.TemplateData("</p>\n </div>\n"), + nodes.If( + nodes.Name("something", "load"), + [ + nodes.Assign( + nodes.Name("title_upper", "store"), + nodes.Filter( + nodes.Name("title", "load"), + "upper", + [], + [], + None, + None, + ), + ), + nodes.Output( + [ + nodes.Name("title_upper", "load"), + nodes.Call( + nodes.Name("render_title", "load"), + [nodes.Const("Aha")], + [], + None, + None, + ), + ] + ), + ], + [], + [], + ), + ] + ), + ], + ) for_loop = nodes.For( - nodes.Name("item", "store"), - nodes.Name("seq", "load"), - [ - nodes.Output( - [ - nodes.TemplateData("\n <li>"), - nodes.Name("item", "load"), - nodes.TemplateData("</li>\n <span>"), - ] - ), - nodes.Include(nodes.Const("helper.html"), True, False), - nodes.Output([nodes.TemplateData("</span>\n ")]), - ], - [], - None, - False, - ) + nodes.Name("item", "store"), + nodes.Name("seq", "load"), + [ + nodes.Output( + [ + nodes.TemplateData("\n <li>"), + nodes.Name("item", "load"), + nodes.TemplateData("</li>\n <span>"), + ] + ), + nodes.Include(nodes.Const("helper.html"), True, False), + nodes.Output([nodes.TemplateData("</span>\n ")]), + ], + [], + None, + False, + ) - body_block = nodes.Block( - "body", - [ - nodes.Output( - [ - nodes.TemplateData("\n "), - nodes.Call( - nodes.Name("render_title", "load"), - [nodes.Name("item", "load")], - [], - None, - None, - ), - nodes.TemplateData("\n <ul>\n "), - ] - ), - for_loop, - nodes.Output([nodes.TemplateData("\n </ul>\n")]), - ], - False, - False, - ) + body_block = nodes.Block( + "body", + [ + nodes.Output( + [ + nodes.TemplateData("\n "), + nodes.Call( + nodes.Name("render_title", "load"), + [nodes.Name("item", "load")], + [], + None, + None, + ), + nodes.TemplateData("\n <ul>\n "), + ] + ), + for_loop, + nodes.Output([nodes.TemplateData("\n </ul>\n")]), + ], + False, + False, + ) - tmpl = nodes.Template( - [ - nodes.Extends(nodes.Const("layout.html")), - title_block, - render_title_macro, - body_block, - ] - ) + tmpl = nodes.Template( + [ + nodes.Extends(nodes.Const("layout.html")), + title_block, + render_title_macro, + body_block, + ] + ) tmpl_sym = symbols_for_node(tmpl) assert tmpl_sym.refs == { - "render_title": "l_0_render_title", + "render_title": "l_0_render_title", } assert tmpl_sym.loads == { - "l_0_render_title": ("undefined", None), + "l_0_render_title": ("undefined", None), } - assert tmpl_sym.stores == {"render_title"} + assert tmpl_sym.stores == {"render_title"} assert tmpl_sym.dump_stores() == { - "render_title": "l_0_render_title", + "render_title": "l_0_render_title", } macro_sym = symbols_for_node(render_title_macro, tmpl_sym) assert macro_sym.refs == { - "subtitle": "l_1_subtitle", - "something": "l_1_something", - "title": "l_1_title", - "title_upper": "l_1_title_upper", + "subtitle": "l_1_subtitle", + "something": "l_1_something", + "title": "l_1_title", + "title_upper": "l_1_title_upper", } assert macro_sym.loads == { - "l_1_subtitle": ("resolve", "subtitle"), - "l_1_something": ("resolve", "something"), - "l_1_title": ("param", None), - "l_1_title_upper": ("resolve", "title_upper"), + "l_1_subtitle": ("resolve", "subtitle"), + "l_1_something": ("resolve", "something"), + "l_1_title": ("param", None), + "l_1_title_upper": ("resolve", "title_upper"), } - assert macro_sym.stores == {"title", "title_upper", "subtitle"} - assert macro_sym.find_ref("render_title") == "l_0_render_title" + assert macro_sym.stores == {"title", "title_upper", "subtitle"} + assert macro_sym.find_ref("render_title") == "l_0_render_title" assert macro_sym.dump_stores() == { - "title": "l_1_title", - "title_upper": "l_1_title_upper", - "subtitle": "l_1_subtitle", - "render_title": "l_0_render_title", + "title": "l_1_title", + "title_upper": "l_1_title_upper", + "subtitle": "l_1_subtitle", + "render_title": "l_0_render_title", } body_sym = symbols_for_node(body_block) assert body_sym.refs == { - "item": "l_0_item", - "seq": "l_0_seq", - "render_title": "l_0_render_title", + "item": "l_0_item", + "seq": "l_0_seq", + "render_title": "l_0_render_title", } assert body_sym.loads == { - "l_0_item": ("resolve", "item"), - "l_0_seq": ("resolve", "seq"), - "l_0_render_title": ("resolve", "render_title"), + "l_0_item": ("resolve", "item"), + "l_0_seq": ("resolve", "seq"), + "l_0_render_title": ("resolve", "render_title"), } - assert body_sym.stores == set() + assert body_sym.stores == set() for_sym = symbols_for_node(for_loop, body_sym) assert for_sym.refs == { - "item": "l_1_item", + "item": "l_1_item", } assert for_sym.loads == { - "l_1_item": ("param", None), + "l_1_item": ("param", None), } - assert for_sym.stores == {"item"} + assert for_sym.stores == {"item"} assert for_sym.dump_stores() == { - "item": "l_1_item", + "item": "l_1_item", } def test_if_branching_stores(): - tmpl = nodes.Template( - [ - nodes.If( - nodes.Name("expression", "load"), - [nodes.Assign(nodes.Name("variable", "store"), nodes.Const(42))], - [], - [], - ) - ] - ) + tmpl = nodes.Template( + [ + nodes.If( + nodes.Name("expression", "load"), + [nodes.Assign(nodes.Name("variable", "store"), nodes.Const(42))], + [], + [], + ) + ] + ) sym = symbols_for_node(tmpl) - assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"} - assert sym.stores == {"variable"} + assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"} + assert sym.stores == {"variable"} assert sym.loads == { - "l_0_variable": ("resolve", "variable"), - "l_0_expression": ("resolve", "expression"), + "l_0_variable": ("resolve", "variable"), + "l_0_expression": ("resolve", "expression"), } assert sym.dump_stores() == { - "variable": "l_0_variable", + "variable": "l_0_variable", } def test_if_branching_stores_undefined(): - tmpl = nodes.Template( - [ - nodes.Assign(nodes.Name("variable", "store"), nodes.Const(23)), - nodes.If( - nodes.Name("expression", "load"), - [nodes.Assign(nodes.Name("variable", "store"), nodes.Const(42))], - [], - [], - ), - ] - ) + tmpl = nodes.Template( + [ + nodes.Assign(nodes.Name("variable", "store"), nodes.Const(23)), + nodes.If( + nodes.Name("expression", "load"), + [nodes.Assign(nodes.Name("variable", "store"), nodes.Const(42))], + [], + [], + ), + ] + ) sym = symbols_for_node(tmpl) - assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"} - assert sym.stores == {"variable"} + assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"} + assert sym.stores == {"variable"} assert sym.loads == { - "l_0_variable": ("undefined", None), - "l_0_expression": ("resolve", "expression"), + "l_0_variable": ("undefined", None), + "l_0_expression": ("resolve", "expression"), } assert sym.dump_stores() == { - "variable": "l_0_variable", + "variable": "l_0_variable", } def test_if_branching_multi_scope(): - for_loop = nodes.For( - nodes.Name("item", "store"), - nodes.Name("seq", "load"), - [ - nodes.If( - nodes.Name("expression", "load"), - [nodes.Assign(nodes.Name("x", "store"), nodes.Const(42))], - [], - [], - ), - nodes.Include(nodes.Const("helper.html"), True, False), - ], - [], - None, - False, - ) + for_loop = nodes.For( + nodes.Name("item", "store"), + nodes.Name("seq", "load"), + [ + nodes.If( + nodes.Name("expression", "load"), + [nodes.Assign(nodes.Name("x", "store"), nodes.Const(42))], + [], + [], + ), + nodes.Include(nodes.Const("helper.html"), True, False), + ], + [], + None, + False, + ) - tmpl = nodes.Template( - [nodes.Assign(nodes.Name("x", "store"), nodes.Const(23)), for_loop] - ) + tmpl = nodes.Template( + [nodes.Assign(nodes.Name("x", "store"), nodes.Const(23)), for_loop] + ) tmpl_sym = symbols_for_node(tmpl) for_sym = symbols_for_node(for_loop, tmpl_sym) - assert for_sym.stores == {"item", "x"} + assert for_sym.stores == {"item", "x"} assert for_sym.loads == { - "l_1_x": ("alias", "l_0_x"), - "l_1_item": ("param", None), - "l_1_expression": ("resolve", "expression"), + "l_1_x": ("alias", "l_0_x"), + "l_1_item": ("param", None), + "l_1_expression": ("resolve", "expression"), } diff --git a/contrib/python/Jinja2/py3/tests/test_imports.py b/contrib/python/Jinja2/py3/tests/test_imports.py index 601ee6e743..b59fb49dde 100644 --- a/contrib/python/Jinja2/py3/tests/test_imports.py +++ b/contrib/python/Jinja2/py3/tests/test_imports.py @@ -1,50 +1,50 @@ 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 +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 + 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: +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]" + 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]" + 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]" + assert t.render(foo=42) == "[42|23]" t = test_env.from_string('{% from "module" import test %}{{ test() }}') - assert t.render(foo=42) == "[|23]" + assert t.render(foo=42) == "[|23]" t = test_env.from_string( '{% from "module" import test without context %}{{ test() }}' ) - assert t.render(foo=42) == "[|23]" + 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]" + assert t.render(foo=42) == "[42|23]" def test_import_needs_name(self, test_env): test_env.from_string('{% from "foo" import bar %}') @@ -77,113 +77,113 @@ class TestImports: test_env.from_string('{% from "foo" import bar with context, %}') def test_exports(self, test_env): - m = test_env.from_string( - """ + 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") + """ + ).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: + 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]" + assert t.render(foo=42) == "[42|23]" t = test_env.from_string('{% include "header" with context %}') - assert t.render(foo=42) == "[42|23]" + assert t.render(foo=42) == "[42|23]" t = test_env.from_string('{% include "header" without context %}') - assert t.render(foo=42) == "[|23]" + 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]" + 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"] 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: + with pytest.raises(TemplatesNotFound) as e: t.render() - assert e.value.templates == ["missing", "missing2"] - assert e.value.name == "missing2" - + 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]" + 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 %}") + 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") + 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() == "" + 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 }}", - ) - ) - ) + 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( - """ + t = test_env.from_string( + """ {% macro outer(o) %} {% macro inner() %} {% include "o_printer" %} @@ -191,15 +191,15 @@ class TestIncludes: {{ inner() }} {% endmacro %} {{ outer("FOO") }} - """ - ) - assert t.render().strip() == "(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" + 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" diff --git a/contrib/python/Jinja2/py3/tests/test_inheritance.py b/contrib/python/Jinja2/py3/tests/test_inheritance.py index b230646463..0c20d4da7d 100644 --- a/contrib/python/Jinja2/py3/tests/test_inheritance.py +++ b/contrib/python/Jinja2/py3/tests/test_inheritance.py @@ -1,38 +1,38 @@ import pytest -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import TemplateRuntimeError -from jinja2 import TemplateSyntaxError +from jinja2 import DictLoader +from jinja2 import Environment +from jinja2 import TemplateRuntimeError +from jinja2 import TemplateSyntaxError -LAYOUTTEMPLATE = """\ +LAYOUTTEMPLATE = """\ |{% block block1 %}block 1 from layout{% endblock %} |{% block block2 %}block 2 from layout{% endblock %} |{% block block3 %} {% block block4 %}nested block 4 from layout{% endblock %} -{% endblock %}|""" +{% endblock %}|""" -LEVEL1TEMPLATE = """\ +LEVEL1TEMPLATE = """\ {% extends "layout" %} -{% block block1 %}block 1 from level1{% endblock %}""" +{% block block1 %}block 1 from level1{% endblock %}""" -LEVEL2TEMPLATE = """\ +LEVEL2TEMPLATE = """\ {% extends "level1" %} {% block block2 %}{% block block5 %}nested block 5 from level2{% -endblock %}{% endblock %}""" +endblock %}{% endblock %}""" -LEVEL3TEMPLATE = """\ +LEVEL3TEMPLATE = """\ {% extends "level2" %} {% block block5 %}block 5 from level3{% endblock %} {% block block4 %}block 4 from level3{% endblock %} -""" +""" -LEVEL4TEMPLATE = """\ +LEVEL4TEMPLATE = """\ {% extends "level3" %} {% block block3 %}block 3 from level4{% endblock %} -""" +""" -WORKINGTEMPLATE = """\ +WORKINGTEMPLATE = """\ {% extends "layout" %} {% block block1 %} {% if false %} @@ -41,9 +41,9 @@ WORKINGTEMPLATE = """\ {% endblock %} {% endif %} {% endblock %} -""" +""" -DOUBLEEXTENDS = """\ +DOUBLEEXTENDS = """\ {% extends "layout" %} {% extends "layout" %} {% block block1 %} @@ -53,168 +53,168 @@ DOUBLEEXTENDS = """\ {% endblock %} {% endif %} {% endblock %} -""" +""" @pytest.fixture def env(): - return Environment( - loader=DictLoader( - { - "layout": LAYOUTTEMPLATE, - "level1": LEVEL1TEMPLATE, - "level2": LEVEL2TEMPLATE, - "level3": LEVEL3TEMPLATE, - "level4": LEVEL4TEMPLATE, - "working": WORKINGTEMPLATE, - "doublee": DOUBLEEXTENDS, - } - ), - trim_blocks=True, - ) - - -class TestInheritance: + return Environment( + loader=DictLoader( + { + "layout": LAYOUTTEMPLATE, + "level1": LEVEL1TEMPLATE, + "level2": LEVEL2TEMPLATE, + "level3": LEVEL3TEMPLATE, + "level4": LEVEL4TEMPLATE, + "working": WORKINGTEMPLATE, + "doublee": DOUBLEEXTENDS, + } + ), + trim_blocks=True, + ) + + +class TestInheritance: def test_layout(self, env): - tmpl = env.get_template("layout") - assert tmpl.render() == ( - "|block 1 from layout|block 2 from layout|nested block 4 from layout|" - ) + tmpl = env.get_template("layout") + assert tmpl.render() == ( + "|block 1 from layout|block 2 from layout|nested block 4 from layout|" + ) def test_level1(self, env): - tmpl = env.get_template("level1") - assert tmpl.render() == ( - "|block 1 from level1|block 2 from layout|nested block 4 from layout|" - ) + tmpl = env.get_template("level1") + assert tmpl.render() == ( + "|block 1 from level1|block 2 from layout|nested block 4 from layout|" + ) def test_level2(self, env): - tmpl = env.get_template("level2") - assert tmpl.render() == ( - "|block 1 from level1|nested block 5 from " - "level2|nested block 4 from layout|" - ) + tmpl = env.get_template("level2") + assert tmpl.render() == ( + "|block 1 from level1|nested block 5 from " + "level2|nested block 4 from layout|" + ) def test_level3(self, env): - tmpl = env.get_template("level3") - assert tmpl.render() == ( - "|block 1 from level1|block 5 from level3|block 4 from level3|" - ) + tmpl = env.get_template("level3") + assert tmpl.render() == ( + "|block 1 from level1|block 5 from level3|block 4 from level3|" + ) def test_level4(self, env): - tmpl = env.get_template("level4") - assert tmpl.render() == ( - "|block 1 from level1|block 5 from level3|block 3 from level4|" - ) + tmpl = env.get_template("level4") + assert tmpl.render() == ( + "|block 1 from level1|block 5 from level3|block 3 from level4|" + ) def test_super(self, env): - env = Environment( - loader=DictLoader( - { - "a": "{% block intro %}INTRO{% endblock %}|" - "BEFORE|{% block data %}INNER{% endblock %}|AFTER", - "b": '{% extends "a" %}{% block data %}({{ ' - "super() }}){% endblock %}", - "c": '{% extends "b" %}{% block intro %}--{{ ' - "super() }}--{% endblock %}\n{% block data " - "%}[{{ super() }}]{% endblock %}", - } - ) - ) - tmpl = env.get_template("c") - assert tmpl.render() == "--INTRO--|BEFORE|[(INNER)]|AFTER" + env = Environment( + loader=DictLoader( + { + "a": "{% block intro %}INTRO{% endblock %}|" + "BEFORE|{% block data %}INNER{% endblock %}|AFTER", + "b": '{% extends "a" %}{% block data %}({{ ' + "super() }}){% endblock %}", + "c": '{% extends "b" %}{% block intro %}--{{ ' + "super() }}--{% endblock %}\n{% block data " + "%}[{{ super() }}]{% endblock %}", + } + ) + ) + tmpl = env.get_template("c") + assert tmpl.render() == "--INTRO--|BEFORE|[(INNER)]|AFTER" def test_working(self, env): - env.get_template("working") + env.get_template("working") def test_reuse_blocks(self, env): - tmpl = env.from_string( - "{{ self.foo() }}|{% block foo %}42{% endblock %}|{{ self.foo() }}" - ) - assert tmpl.render() == "42|42|42" + tmpl = env.from_string( + "{{ self.foo() }}|{% block foo %}42{% endblock %}|{{ self.foo() }}" + ) + assert tmpl.render() == "42|42|42" def test_preserve_blocks(self, env): - env = Environment( - loader=DictLoader( - { - "a": "{% if false %}{% block x %}A{% endblock %}" - "{% endif %}{{ self.x() }}", - "b": '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}', - } - ) - ) - tmpl = env.get_template("b") - assert tmpl.render() == "BA" + env = Environment( + loader=DictLoader( + { + "a": "{% if false %}{% block x %}A{% endblock %}" + "{% endif %}{{ self.x() }}", + "b": '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}', + } + ) + ) + tmpl = env.get_template("b") + assert tmpl.render() == "BA" def test_dynamic_inheritance(self, env): - env = Environment( - loader=DictLoader( - { - "default1": "DEFAULT1{% block x %}{% endblock %}", - "default2": "DEFAULT2{% block x %}{% endblock %}", - "child": "{% extends default %}{% block x %}CHILD{% endblock %}", - } - ) - ) - tmpl = env.get_template("child") + env = Environment( + loader=DictLoader( + { + "default1": "DEFAULT1{% block x %}{% endblock %}", + "default2": "DEFAULT2{% block x %}{% endblock %}", + "child": "{% extends default %}{% block x %}CHILD{% endblock %}", + } + ) + ) + tmpl = env.get_template("child") for m in range(1, 3): - assert tmpl.render(default=f"default{m}") == f"DEFAULT{m}CHILD" + assert tmpl.render(default=f"default{m}") == f"DEFAULT{m}CHILD" def test_multi_inheritance(self, env): - env = Environment( - loader=DictLoader( - { - "default1": "DEFAULT1{% block x %}{% endblock %}", - "default2": "DEFAULT2{% block x %}{% endblock %}", - "child": ( - "{% if default %}{% extends default %}{% else %}" - "{% extends 'default1' %}{% endif %}" - "{% block x %}CHILD{% endblock %}" - ), - } - ) - ) - tmpl = env.get_template("child") - assert tmpl.render(default="default2") == "DEFAULT2CHILD" - assert tmpl.render(default="default1") == "DEFAULT1CHILD" - assert tmpl.render() == "DEFAULT1CHILD" + env = Environment( + loader=DictLoader( + { + "default1": "DEFAULT1{% block x %}{% endblock %}", + "default2": "DEFAULT2{% block x %}{% endblock %}", + "child": ( + "{% if default %}{% extends default %}{% else %}" + "{% extends 'default1' %}{% endif %}" + "{% block x %}CHILD{% endblock %}" + ), + } + ) + ) + tmpl = env.get_template("child") + assert tmpl.render(default="default2") == "DEFAULT2CHILD" + assert tmpl.render(default="default1") == "DEFAULT1CHILD" + assert tmpl.render() == "DEFAULT1CHILD" def test_scoped_block(self, env): - env = Environment( - loader=DictLoader( - { - "default.html": "{% for item in seq %}[{% block item scoped %}" - "{% endblock %}]{% endfor %}" - } - ) - ) - t = env.from_string( - "{% extends 'default.html' %}{% block item %}{{ item }}{% endblock %}" - ) - assert t.render(seq=list(range(5))) == "[0][1][2][3][4]" + env = Environment( + loader=DictLoader( + { + "default.html": "{% for item in seq %}[{% block item scoped %}" + "{% endblock %}]{% endfor %}" + } + ) + ) + t = env.from_string( + "{% extends 'default.html' %}{% block item %}{{ item }}{% endblock %}" + ) + assert t.render(seq=list(range(5))) == "[0][1][2][3][4]" def test_super_in_scoped_block(self, env): - env = Environment( - loader=DictLoader( - { - "default.html": "{% for item in seq %}[{% block item scoped %}" - "{{ item }}{% endblock %}]{% endfor %}" - } - ) - ) - t = env.from_string( - '{% extends "default.html" %}{% block item %}' - "{{ super() }}|{{ item * 2 }}{% endblock %}" - ) - assert t.render(seq=list(range(5))) == "[0|0][1|2][2|4][3|6][4|8]" + env = Environment( + loader=DictLoader( + { + "default.html": "{% for item in seq %}[{% block item scoped %}" + "{{ item }}{% endblock %}]{% endfor %}" + } + ) + ) + t = env.from_string( + '{% extends "default.html" %}{% block item %}' + "{{ super() }}|{{ item * 2 }}{% endblock %}" + ) + assert t.render(seq=list(range(5))) == "[0|0][1|2][2|4][3|6][4|8]" def test_scoped_block_after_inheritance(self, env): - env = Environment( - loader=DictLoader( - { - "layout.html": """ + env = Environment( + loader=DictLoader( + { + "layout.html": """ {% block useless %}{% endblock %} - """, - "index.html": """ + """, + "index.html": """ {%- extends 'layout.html' %} {% from 'helpers.html' import foo with context %} {% block useless %} @@ -224,141 +224,141 @@ class TestInheritance: {% endblock %} {% endfor %} {% endblock %} - """, - "helpers.html": """ + """, + "helpers.html": """ {% macro foo(x) %}{{ the_foo + x }}{% endmacro %} - """, - } - ) - ) - rv = env.get_template("index.html").render(the_foo=42).split() - assert rv == ["43", "44", "45"] - - def test_level1_required(self, env): - env = Environment( - loader=DictLoader( - { - "default": "{% block x required %}{# comment #}\n {% endblock %}", - "level1": "{% extends 'default' %}{% block x %}[1]{% endblock %}", - } - ) - ) - rv = env.get_template("level1").render() - assert rv == "[1]" - - def test_level2_required(self, env): - env = Environment( - loader=DictLoader( - { - "default": "{% block x required %}{% endblock %}", - "level1": "{% extends 'default' %}{% block x %}[1]{% endblock %}", - "level2": "{% extends 'default' %}{% block x %}[2]{% endblock %}", - } - ) - ) - rv1 = env.get_template("level1").render() - rv2 = env.get_template("level2").render() - - assert rv1 == "[1]" - assert rv2 == "[2]" - - def test_level3_required(self, env): - env = Environment( - loader=DictLoader( - { - "default": "{% block x required %}{% endblock %}", - "level1": "{% extends 'default' %}", - "level2": "{% extends 'level1' %}{% block x %}[2]{% endblock %}", - "level3": "{% extends 'level2' %}", - } - ) - ) - t1 = env.get_template("level1") - t2 = env.get_template("level2") - t3 = env.get_template("level3") - - with pytest.raises(TemplateRuntimeError, match="Required block 'x' not found"): - assert t1.render() - - assert t2.render() == "[2]" - assert t3.render() == "[2]" - - def test_invalid_required(self, env): - env = Environment( - loader=DictLoader( - { - "default": "{% block x required %}data {# #}{% endblock %}", - "default1": "{% block x required %}{% block y %}" - "{% endblock %} {% endblock %}", - "default2": "{% block x required %}{% if true %}" - "{% endif %} {% endblock %}", - "level1": "{% if default %}{% extends default %}" - "{% else %}{% extends 'default' %}{% endif %}" - "{%- block x %}CHILD{% endblock %}", - } - ) - ) - t = env.get_template("level1") - - with pytest.raises( - TemplateSyntaxError, - match="Required blocks can only contain comments or whitespace", - ): - assert t.render(default="default") - assert t.render(default="default2") - assert t.render(default="default3") - - def test_required_with_scope(self, env): - env = Environment( - loader=DictLoader( - { - "default1": "{% for item in seq %}[{% block item scoped required %}" - "{% endblock %}]{% endfor %}", - "child1": "{% extends 'default1' %}{% block item %}" - "{{ item }}{% endblock %}", - "default2": "{% for item in seq %}[{% block item required scoped %}" - "{% endblock %}]{% endfor %}", - "child2": "{% extends 'default2' %}{% block item %}" - "{{ item }}{% endblock %}", - } - ) - ) - t1 = env.get_template("child1") - t2 = env.get_template("child2") - - assert t1.render(seq=list(range(3))) == "[0][1][2]" - - # scoped must come before required - with pytest.raises(TemplateSyntaxError): - t2.render(seq=list(range(3))) - - def test_duplicate_required_or_scoped(self, env): - env = Environment( - loader=DictLoader( - { - "default1": "{% for item in seq %}[{% block item " - "scoped scoped %}}{{% endblock %}}]{{% endfor %}}", - "default2": "{% for item in seq %}[{% block item " - "required required %}}{{% endblock %}}]{{% endfor %}}", - "child": "{% if default %}{% extends default %}{% else %}" - "{% extends 'default1' %}{% endif %}{%- block x %}" - "CHILD{% endblock %}", - } - ) - ) - tmpl = env.get_template("child") - with pytest.raises(TemplateSyntaxError): - tmpl.render(default="default1", seq=list(range(3))) - tmpl.render(default="default2", seq=list(range(3))) - - -class TestBugFix: + """, + } + ) + ) + rv = env.get_template("index.html").render(the_foo=42).split() + assert rv == ["43", "44", "45"] + + def test_level1_required(self, env): + env = Environment( + loader=DictLoader( + { + "default": "{% block x required %}{# comment #}\n {% endblock %}", + "level1": "{% extends 'default' %}{% block x %}[1]{% endblock %}", + } + ) + ) + rv = env.get_template("level1").render() + assert rv == "[1]" + + def test_level2_required(self, env): + env = Environment( + loader=DictLoader( + { + "default": "{% block x required %}{% endblock %}", + "level1": "{% extends 'default' %}{% block x %}[1]{% endblock %}", + "level2": "{% extends 'default' %}{% block x %}[2]{% endblock %}", + } + ) + ) + rv1 = env.get_template("level1").render() + rv2 = env.get_template("level2").render() + + assert rv1 == "[1]" + assert rv2 == "[2]" + + def test_level3_required(self, env): + env = Environment( + loader=DictLoader( + { + "default": "{% block x required %}{% endblock %}", + "level1": "{% extends 'default' %}", + "level2": "{% extends 'level1' %}{% block x %}[2]{% endblock %}", + "level3": "{% extends 'level2' %}", + } + ) + ) + t1 = env.get_template("level1") + t2 = env.get_template("level2") + t3 = env.get_template("level3") + + with pytest.raises(TemplateRuntimeError, match="Required block 'x' not found"): + assert t1.render() + + assert t2.render() == "[2]" + assert t3.render() == "[2]" + + def test_invalid_required(self, env): + env = Environment( + loader=DictLoader( + { + "default": "{% block x required %}data {# #}{% endblock %}", + "default1": "{% block x required %}{% block y %}" + "{% endblock %} {% endblock %}", + "default2": "{% block x required %}{% if true %}" + "{% endif %} {% endblock %}", + "level1": "{% if default %}{% extends default %}" + "{% else %}{% extends 'default' %}{% endif %}" + "{%- block x %}CHILD{% endblock %}", + } + ) + ) + t = env.get_template("level1") + + with pytest.raises( + TemplateSyntaxError, + match="Required blocks can only contain comments or whitespace", + ): + assert t.render(default="default") + assert t.render(default="default2") + assert t.render(default="default3") + + def test_required_with_scope(self, env): + env = Environment( + loader=DictLoader( + { + "default1": "{% for item in seq %}[{% block item scoped required %}" + "{% endblock %}]{% endfor %}", + "child1": "{% extends 'default1' %}{% block item %}" + "{{ item }}{% endblock %}", + "default2": "{% for item in seq %}[{% block item required scoped %}" + "{% endblock %}]{% endfor %}", + "child2": "{% extends 'default2' %}{% block item %}" + "{{ item }}{% endblock %}", + } + ) + ) + t1 = env.get_template("child1") + t2 = env.get_template("child2") + + assert t1.render(seq=list(range(3))) == "[0][1][2]" + + # scoped must come before required + with pytest.raises(TemplateSyntaxError): + t2.render(seq=list(range(3))) + + def test_duplicate_required_or_scoped(self, env): + env = Environment( + loader=DictLoader( + { + "default1": "{% for item in seq %}[{% block item " + "scoped scoped %}}{{% endblock %}}]{{% endfor %}}", + "default2": "{% for item in seq %}[{% block item " + "required required %}}{{% endblock %}}]{{% endfor %}}", + "child": "{% if default %}{% extends default %}{% else %}" + "{% extends 'default1' %}{% endif %}{%- block x %}" + "CHILD{% endblock %}", + } + ) + ) + tmpl = env.get_template("child") + with pytest.raises(TemplateSyntaxError): + tmpl.render(default="default1", seq=list(range(3))) + tmpl.render(default="default2", seq=list(range(3))) + + +class TestBugFix: def test_fixed_macro_scoping_bug(self, env): - assert ( - Environment( - loader=DictLoader( - { - "test.html": """\ + assert ( + Environment( + loader=DictLoader( + { + "test.html": """\ {% extends 'details.html' %} {% macro my_macro() %} @@ -368,8 +368,8 @@ class TestBugFix: {% block inner_box %} {{ my_macro() }} {% endblock %} - """, - "details.html": """\ + """, + "details.html": """\ {% extends 'standard.html' %} {% macro my_macro() %} @@ -384,22 +384,22 @@ class TestBugFix: {% endblock %} {% endblock %} {% endblock %} - """, - "standard.html": """ + """, + "standard.html": """ {% block content %} {% endblock %} - """, - } - ) - ) - .get_template("test.html") - .render() - .split() - == ["outer_box", "my_macro"] - ) + """, + } + ) + ) + .get_template("test.html") + .render() + .split() + == ["outer_box", "my_macro"] + ) def test_double_extends(self, env): """Ensures that a template with more than 1 {% extends ... %} usage raises a ``TemplateError``. """ - with pytest.raises(TemplateRuntimeError, match="extended multiple times"): - env.get_template("doublee").render() + with pytest.raises(TemplateRuntimeError, match="extended multiple times"): + env.get_template("doublee").render() diff --git a/contrib/python/Jinja2/py3/tests/test_lexnparse.py b/contrib/python/Jinja2/py3/tests/test_lexnparse.py index 2f6c6754ef..c02adad5a9 100644 --- a/contrib/python/Jinja2/py3/tests/test_lexnparse.py +++ b/contrib/python/Jinja2/py3/tests/test_lexnparse.py @@ -1,22 +1,22 @@ import pytest -from jinja2 import Environment -from jinja2 import nodes -from jinja2 import Template -from jinja2 import TemplateSyntaxError -from jinja2 import UndefinedError -from jinja2.lexer import Token -from jinja2.lexer import TOKEN_BLOCK_BEGIN -from jinja2.lexer import TOKEN_BLOCK_END -from jinja2.lexer import TOKEN_EOF -from jinja2.lexer import TokenStream - - -class TestTokenStream: - test_tokens = [ - Token(1, TOKEN_BLOCK_BEGIN, ""), - Token(2, TOKEN_BLOCK_END, ""), - ] +from jinja2 import Environment +from jinja2 import nodes +from jinja2 import Template +from jinja2 import TemplateSyntaxError +from jinja2 import UndefinedError +from jinja2.lexer import Token +from jinja2.lexer import TOKEN_BLOCK_BEGIN +from jinja2.lexer import TOKEN_BLOCK_END +from jinja2.lexer import TOKEN_EOF +from jinja2.lexer import TokenStream + + +class TestTokenStream: + test_tokens = [ + Token(1, TOKEN_BLOCK_BEGIN, ""), + Token(2, TOKEN_BLOCK_END, ""), + ] def test_simple(self, env): ts = TokenStream(self.test_tokens, "foo", "bar") @@ -33,998 +33,998 @@ class TestTokenStream: assert bool(ts.eos) def test_iter(self, env): - token_types = [t.type for t in TokenStream(self.test_tokens, "foo", "bar")] - assert token_types == [ - "block_begin", - "block_end", + token_types = [t.type for t in TokenStream(self.test_tokens, "foo", "bar")] + assert token_types == [ + "block_begin", + "block_end", ] -class TestLexer: +class TestLexer: def test_raw1(self, env): tmpl = env.from_string( - "{% raw %}foo{% endraw %}|" - "{%raw%}{{ bar }}|{% baz %}{% endraw %}" - ) - assert tmpl.render() == "foo|{{ bar }}|{% baz %}" + "{% raw %}foo{% endraw %}|" + "{%raw%}{{ bar }}|{% baz %}{% endraw %}" + ) + assert tmpl.render() == "foo|{{ bar }}|{% baz %}" def test_raw2(self, env): - tmpl = env.from_string("1 {%- raw -%} 2 {%- endraw -%} 3") - assert tmpl.render() == "123" - - def test_raw3(self, env): - # The second newline after baz exists because it is AFTER the - # {% raw %} and is ignored. - env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string("bar\n{% raw %}\n {{baz}}2 spaces\n{% endraw %}\nfoo") - assert tmpl.render(baz="test") == "bar\n\n {{baz}}2 spaces\nfoo" - - def test_raw4(self, env): - # The trailing dash of the {% raw -%} cleans both the spaces and - # newlines up to the first character of data. - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string( - "bar\n{%- raw -%}\n\n \n 2 spaces\n space{%- endraw -%}\nfoo" - ) - assert tmpl.render() == "bar2 spaces\n spacefoo" - + tmpl = env.from_string("1 {%- raw -%} 2 {%- endraw -%} 3") + assert tmpl.render() == "123" + + def test_raw3(self, env): + # The second newline after baz exists because it is AFTER the + # {% raw %} and is ignored. + env = Environment(lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string("bar\n{% raw %}\n {{baz}}2 spaces\n{% endraw %}\nfoo") + assert tmpl.render(baz="test") == "bar\n\n {{baz}}2 spaces\nfoo" + + def test_raw4(self, env): + # The trailing dash of the {% raw -%} cleans both the spaces and + # newlines up to the first character of data. + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string( + "bar\n{%- raw -%}\n\n \n 2 spaces\n space{%- endraw -%}\nfoo" + ) + assert tmpl.render() == "bar2 spaces\n spacefoo" + def test_balancing(self, env): - env = Environment("{%", "%}", "${", "}") - tmpl = env.from_string( - """{% for item in seq - %}${{'foo': item}|upper}{% endfor %}""" - ) - assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}" + env = Environment("{%", "%}", "${", "}") + tmpl = env.from_string( + """{% for item in seq + %}${{'foo': item}|upper}{% endfor %}""" + ) + assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}" def test_comments(self, env): - env = Environment("<!--", "-->", "{", "}") - tmpl = env.from_string( - """\ + env = Environment("<!--", "-->", "{", "}") + tmpl = env.from_string( + """\ <ul> <!--- for item in seq --> <li>{item}</li> <!--- endfor --> -</ul>""" - ) - assert tmpl.render(seq=list(range(3))) == ( - "<ul>\n <li>0</li>\n <li>1</li>\n <li>2</li>\n</ul>" - ) +</ul>""" + ) + assert tmpl.render(seq=list(range(3))) == ( + "<ul>\n <li>0</li>\n <li>1</li>\n <li>2</li>\n</ul>" + ) def test_string_escapes(self, env): - for char in "\0", "\u2668", "\xe4", "\t", "\r", "\n": - tmpl = env.from_string(f"{{{{ {char!r} }}}}") + for char in "\0", "\u2668", "\xe4", "\t", "\r", "\n": + tmpl = env.from_string(f"{{{{ {char!r} }}}}") assert tmpl.render() == char - assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == "\u2668" + assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == "\u2668" def test_bytefallback(self, env): from pprint import pformat - tmpl = env.from_string("""{{ 'foo'|pprint }}|{{ 'bär'|pprint }}""") - assert tmpl.render() == pformat("foo") + "|" + pformat("bär") - + tmpl = env.from_string("""{{ 'foo'|pprint }}|{{ 'bär'|pprint }}""") + assert tmpl.render() == pformat("foo") + "|" + pformat("bär") + def test_operators(self, env): from jinja2.lexer import operators - - for test, expect in operators.items(): - if test in "([{}])": + + for test, expect in operators.items(): + if test in "([{}])": continue - stream = env.lexer.tokenize(f"{{{{ {test} }}}}") + stream = env.lexer.tokenize(f"{{{{ {test} }}}}") next(stream) assert stream.current.type == expect def test_normalizing(self, env): - for seq in "\r", "\r\n", "\n": + for seq in "\r", "\r\n", "\n": env = Environment(newline_sequence=seq) - tmpl = env.from_string("1\n2\r\n3\n4\n") + tmpl = env.from_string("1\n2\r\n3\n4\n") result = tmpl.render() - assert result.replace(seq, "X") == "1X2X3X4" + assert result.replace(seq, "X") == "1X2X3X4" def test_trailing_newline(self, env): for keep in [True, False]: env = Environment(keep_trailing_newline=keep) for template, expected in [ - ("", {}), - ("no\nnewline", {}), - ("with\nnewline\n", {False: "with\nnewline"}), - ("with\nseveral\n\n\n", {False: "with\nseveral\n\n"}), - ]: + ("", {}), + ("no\nnewline", {}), + ("with\nnewline\n", {False: "with\nnewline"}), + ("with\nseveral\n\n\n", {False: "with\nseveral\n\n"}), + ]: tmpl = env.from_string(template) expect = expected.get(keep, template) result = tmpl.render() assert result == expect, (keep, template, result, expect) - @pytest.mark.parametrize( - ("name", "valid"), - [ - ("foo", True), - ("föö", True), - ("き", True), - ("_", True), - ("1a", False), # invalid ascii start - ("a-", False), # invalid ascii continue - ("\U0001f40da", False), # invalid unicode start - ("a🐍\U0001f40d", False), # invalid unicode continue - # start characters not matched by \w - ("\u1885", True), - ("\u1886", True), - ("\u2118", True), - ("\u212e", True), - # continue character not matched by \w - ("\xb7", False), - ("a\xb7", True), - ], - ) - def test_name(self, env, name, valid): - t = "{{ " + name + " }}" - - if valid: + @pytest.mark.parametrize( + ("name", "valid"), + [ + ("foo", True), + ("föö", True), + ("き", True), + ("_", True), + ("1a", False), # invalid ascii start + ("a-", False), # invalid ascii continue + ("\U0001f40da", False), # invalid unicode start + ("a🐍\U0001f40d", False), # invalid unicode continue + # start characters not matched by \w + ("\u1885", True), + ("\u1886", True), + ("\u2118", True), + ("\u212e", True), + # continue character not matched by \w + ("\xb7", False), + ("a\xb7", True), + ], + ) + def test_name(self, env, name, valid): + t = "{{ " + name + " }}" + + if valid: # valid for version being tested, shouldn't raise env.from_string(t) else: pytest.raises(TemplateSyntaxError, env.from_string, t) - def test_lineno_with_strip(self, env): - tokens = env.lex( - """\ -<html> - <body> - {%- block content -%} - <hr> - {{ item }} - {% endblock %} - </body> -</html>""" - ) - for tok in tokens: - lineno, token_type, value = tok - if token_type == "name" and value == "item": - assert lineno == 5 - break - - -class TestParser: + def test_lineno_with_strip(self, env): + tokens = env.lex( + """\ +<html> + <body> + {%- block content -%} + <hr> + {{ item }} + {% endblock %} + </body> +</html>""" + ) + for tok in tokens: + lineno, token_type, value = tok + if token_type == "name" and value == "item": + assert lineno == 5 + break + + +class TestParser: def test_php_syntax(self, env): - env = Environment("<?", "?>", "<?=", "?>", "<!--", "-->") - tmpl = env.from_string( - """\ + env = Environment("<?", "?>", "<?=", "?>", "<!--", "-->") + tmpl = env.from_string( + """\ <!-- I'm a comment, I'm not interesting -->\ <? for item in seq -?> <?= item ?> -<?- endfor ?>""" - ) - assert tmpl.render(seq=list(range(5))) == "01234" +<?- endfor ?>""" + ) + assert tmpl.render(seq=list(range(5))) == "01234" def test_erb_syntax(self, env): - env = Environment("<%", "%>", "<%=", "%>", "<%#", "%>") - tmpl = env.from_string( - """\ + env = Environment("<%", "%>", "<%=", "%>", "<%#", "%>") + tmpl = env.from_string( + """\ <%# I'm a comment, I'm not interesting %>\ <% for item in seq -%> <%= item %> -<%- endfor %>""" - ) - assert tmpl.render(seq=list(range(5))) == "01234" +<%- endfor %>""" + ) + assert tmpl.render(seq=list(range(5))) == "01234" def test_comment_syntax(self, env): - env = Environment("<!--", "-->", "${", "}", "<!--#", "-->") - tmpl = env.from_string( - """\ + env = Environment("<!--", "-->", "${", "}", "<!--#", "-->") + tmpl = env.from_string( + """\ <!--# I'm a comment, I'm not interesting -->\ <!-- for item in seq ---> ${item} -<!--- endfor -->""" - ) - assert tmpl.render(seq=list(range(5))) == "01234" +<!--- endfor -->""" + ) + assert tmpl.render(seq=list(range(5))) == "01234" def test_balancing(self, env): - tmpl = env.from_string("""{{{'foo':'bar'}.foo}}""") - assert tmpl.render() == "bar" + tmpl = env.from_string("""{{{'foo':'bar'}.foo}}""") + assert tmpl.render() == "bar" def test_start_comment(self, env): - tmpl = env.from_string( - """{# foo comment + tmpl = env.from_string( + """{# foo comment and bar comment #} {% macro blub() %}foo{% endmacro %} -{{ blub() }}""" - ) - assert tmpl.render().strip() == "foo" +{{ blub() }}""" + ) + assert tmpl.render().strip() == "foo" def test_line_syntax(self, env): - env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%") - tmpl = env.from_string( - """\ + env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%") + tmpl = env.from_string( + """\ <%# regular comment %> % for item in seq: ${item} -% endfor""" - ) +% endfor""" + ) assert [ int(x.strip()) for x in tmpl.render(seq=list(range(5))).split() ] == list(range(5)) - env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%", "##") - tmpl = env.from_string( - """\ + env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%", "##") + tmpl = env.from_string( + """\ <%# regular comment %> % for item in seq: ${item} ## the rest of the stuff -% endfor""" - ) +% endfor""" + ) assert [ int(x.strip()) for x in tmpl.render(seq=list(range(5))).split() ] == list(range(5)) def test_line_syntax_priority(self, env): # XXX: why is the whitespace there in front of the newline? - env = Environment("{%", "%}", "${", "}", "/*", "*/", "##", "#") - tmpl = env.from_string( - """\ + env = Environment("{%", "%}", "${", "}", "/*", "*/", "##", "#") + tmpl = env.from_string( + """\ /* ignore me. I'm a multiline comment */ ## for item in seq: * ${item} # this is just extra stuff -## endfor""" - ) - assert tmpl.render(seq=[1, 2]).strip() == "* 1\n* 2" - env = Environment("{%", "%}", "${", "}", "/*", "*/", "#", "##") - tmpl = env.from_string( - """\ +## endfor""" + ) + assert tmpl.render(seq=[1, 2]).strip() == "* 1\n* 2" + env = Environment("{%", "%}", "${", "}", "/*", "*/", "#", "##") + tmpl = env.from_string( + """\ /* ignore me. I'm a multiline comment */ # for item in seq: * ${item} ## this is just extra stuff ## extra stuff i just want to ignore -# endfor""" - ) - assert tmpl.render(seq=[1, 2]).strip() == "* 1\n\n* 2" +# endfor""" + ) + assert tmpl.render(seq=[1, 2]).strip() == "* 1\n\n* 2" def test_error_messages(self, env): def assert_error(code, expected): - with pytest.raises(TemplateSyntaxError, match=expected): + with pytest.raises(TemplateSyntaxError, match=expected): Template(code) assert_error( - "{% for item in seq %}...{% endif %}", - "Encountered unknown tag 'endif'. Jinja was looking " - "for the following tags: 'endfor' or 'else'. The " - "innermost block that needs to be closed is 'for'.", - ) - assert_error( - "{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}", + "{% for item in seq %}...{% endif %}", + "Encountered unknown tag 'endif'. Jinja was looking " + "for the following tags: 'endfor' or 'else'. The " + "innermost block that needs to be closed is 'for'.", + ) + assert_error( + "{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}", "Encountered unknown tag 'endfor'. Jinja was looking for " "the following tags: 'elif' or 'else' or 'endif'. The " - "innermost block that needs to be closed is 'if'.", - ) + "innermost block that needs to be closed is 'if'.", + ) + assert_error( + "{% if foo %}", + "Unexpected end of template. Jinja was looking for the " + "following tags: 'elif' or 'else' or 'endif'. The " + "innermost block that needs to be closed is 'if'.", + ) + assert_error( + "{% for item in seq %}", + "Unexpected end of template. Jinja was looking for the " + "following tags: 'endfor' or 'else'. The innermost block " + "that needs to be closed is 'for'.", + ) assert_error( - "{% if foo %}", - "Unexpected end of template. Jinja was looking for the " - "following tags: 'elif' or 'else' or 'endif'. The " - "innermost block that needs to be closed is 'if'.", - ) - assert_error( - "{% for item in seq %}", - "Unexpected end of template. Jinja was looking for the " - "following tags: 'endfor' or 'else'. The innermost block " - "that needs to be closed is 'for'.", - ) - assert_error( - "{% block foo-bar-baz %}", + "{% block foo-bar-baz %}", "Block names in Jinja have to be valid Python identifiers " - "and may not contain hyphens, use an underscore instead.", - ) - assert_error("{% unknown_tag %}", "Encountered unknown tag 'unknown_tag'.") + "and may not contain hyphens, use an underscore instead.", + ) + assert_error("{% unknown_tag %}", "Encountered unknown tag 'unknown_tag'.") -class TestSyntax: +class TestSyntax: def test_call(self, env): env = Environment() - env.globals["foo"] = lambda a, b, c, e, g: a + b + c + e + g - tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}") - assert tmpl.render() == "abdfh" + env.globals["foo"] = lambda a, b, c, e, g: a + b + c + e + g + tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}") + assert tmpl.render() == "abdfh" def test_slicing(self, env): - tmpl = env.from_string("{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}") - assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]" + tmpl = env.from_string("{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}") + assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]" def test_attr(self, env): tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}") - assert tmpl.render(foo={"bar": 42}) == "42|42" + assert tmpl.render(foo={"bar": 42}) == "42|42" def test_subscript(self, env): tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}") - assert tmpl.render(foo=[0, 1, 2]) == "0|2" + assert tmpl.render(foo=[0, 1, 2]) == "0|2" def test_tuple(self, env): - tmpl = env.from_string("{{ () }}|{{ (1,) }}|{{ (1, 2) }}") - assert tmpl.render() == "()|(1,)|(1, 2)" + tmpl = env.from_string("{{ () }}|{{ (1,) }}|{{ (1, 2) }}") + assert tmpl.render() == "()|(1,)|(1, 2)" def test_math(self, env): - tmpl = env.from_string("{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}") - assert tmpl.render() == "1.5|8" + tmpl = env.from_string("{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}") + assert tmpl.render() == "1.5|8" def test_div(self, env): - tmpl = env.from_string("{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}") - assert tmpl.render() == "1|1.5|1" + tmpl = env.from_string("{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}") + assert tmpl.render() == "1|1.5|1" def test_unary(self, env): - tmpl = env.from_string("{{ +3 }}|{{ -3 }}") - assert tmpl.render() == "3|-3" + tmpl = env.from_string("{{ +3 }}|{{ -3 }}") + assert tmpl.render() == "3|-3" def test_concat(self, env): tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}") - assert tmpl.render() == "[1, 2]foo" - - @pytest.mark.parametrize( - ("a", "op", "b"), - [ - (1, ">", 0), - (1, ">=", 1), - (2, "<", 3), - (3, "<=", 4), - (4, "==", 4), - (4, "!=", 5), - ], - ) - def test_compare(self, env, a, op, b): - t = env.from_string(f"{{{{ {a} {op} {b} }}}}") - assert t.render() == "True" - - def test_compare_parens(self, env): - t = env.from_string("{{ i * (j < 5) }}") - assert t.render(i=2, j=3) == "2" - - @pytest.mark.parametrize( - ("src", "expect"), - [ - ("{{ 4 < 2 < 3 }}", "False"), - ("{{ a < b < c }}", "False"), - ("{{ 4 > 2 > 3 }}", "False"), - ("{{ a > b > c }}", "False"), - ("{{ 4 > 2 < 3 }}", "True"), - ("{{ a > b < c }}", "True"), - ], - ) - def test_compare_compound(self, env, src, expect): - t = env.from_string(src) - assert t.render(a=4, b=2, c=3) == expect - + assert tmpl.render() == "[1, 2]foo" + + @pytest.mark.parametrize( + ("a", "op", "b"), + [ + (1, ">", 0), + (1, ">=", 1), + (2, "<", 3), + (3, "<=", 4), + (4, "==", 4), + (4, "!=", 5), + ], + ) + def test_compare(self, env, a, op, b): + t = env.from_string(f"{{{{ {a} {op} {b} }}}}") + assert t.render() == "True" + + def test_compare_parens(self, env): + t = env.from_string("{{ i * (j < 5) }}") + assert t.render(i=2, j=3) == "2" + + @pytest.mark.parametrize( + ("src", "expect"), + [ + ("{{ 4 < 2 < 3 }}", "False"), + ("{{ a < b < c }}", "False"), + ("{{ 4 > 2 > 3 }}", "False"), + ("{{ a > b > c }}", "False"), + ("{{ 4 > 2 < 3 }}", "True"), + ("{{ a > b < c }}", "True"), + ], + ) + def test_compare_compound(self, env, src, expect): + t = env.from_string(src) + assert t.render(a=4, b=2, c=3) == expect + def test_inop(self, env): - tmpl = env.from_string("{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}") - assert tmpl.render() == "True|False" - - @pytest.mark.parametrize("value", ("[]", "{}", "()")) - def test_collection_literal(self, env, value): - t = env.from_string(f"{{{{ {value} }}}}") - assert t.render() == value - - @pytest.mark.parametrize( - ("value", "expect"), - ( - ("1", "1"), - ("123", "123"), - ("12_34_56", "123456"), - ("1.2", "1.2"), - ("34.56", "34.56"), - ("3_4.5_6", "34.56"), - ("1e0", "1.0"), - ("10e1", "100.0"), - ("2.5e100", "2.5e+100"), - ("2.5e+100", "2.5e+100"), - ("25.6e-10", "2.56e-09"), - ("1_2.3_4e5_6", "1.234e+57"), - ("0", "0"), - ("0_00", "0"), - ("0b1001_1111", "159"), - ("0o123", "83"), - ("0o1_23", "83"), - ("0x123abc", "1194684"), - ("0x12_3abc", "1194684"), - ), - ) - def test_numeric_literal(self, env, value, expect): - t = env.from_string(f"{{{{ {value} }}}}") - assert t.render() == expect - + tmpl = env.from_string("{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}") + assert tmpl.render() == "True|False" + + @pytest.mark.parametrize("value", ("[]", "{}", "()")) + def test_collection_literal(self, env, value): + t = env.from_string(f"{{{{ {value} }}}}") + assert t.render() == value + + @pytest.mark.parametrize( + ("value", "expect"), + ( + ("1", "1"), + ("123", "123"), + ("12_34_56", "123456"), + ("1.2", "1.2"), + ("34.56", "34.56"), + ("3_4.5_6", "34.56"), + ("1e0", "1.0"), + ("10e1", "100.0"), + ("2.5e100", "2.5e+100"), + ("2.5e+100", "2.5e+100"), + ("25.6e-10", "2.56e-09"), + ("1_2.3_4e5_6", "1.234e+57"), + ("0", "0"), + ("0_00", "0"), + ("0b1001_1111", "159"), + ("0o123", "83"), + ("0o1_23", "83"), + ("0x123abc", "1194684"), + ("0x12_3abc", "1194684"), + ), + ) + def test_numeric_literal(self, env, value, expect): + t = env.from_string(f"{{{{ {value} }}}}") + assert t.render() == expect + def test_bool(self, env): - tmpl = env.from_string( - "{{ true and false }}|{{ false or true }}|{{ not false }}" - ) - assert tmpl.render() == "False|True|True" + tmpl = env.from_string( + "{{ true and false }}|{{ false or true }}|{{ not false }}" + ) + assert tmpl.render() == "False|True|True" def test_grouping(self, env): tmpl = env.from_string( - "{{ (true and false) or (false and true) and not false }}" - ) - assert tmpl.render() == "False" + "{{ (true and false) or (false and true) and not false }}" + ) + assert tmpl.render() == "False" def test_django_attr(self, env): - tmpl = env.from_string("{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}") - assert tmpl.render() == "1|1" + tmpl = env.from_string("{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}") + assert tmpl.render() == "1|1" def test_conditional_expression(self, env): - tmpl = env.from_string("""{{ 0 if true else 1 }}""") - assert tmpl.render() == "0" + tmpl = env.from_string("""{{ 0 if true else 1 }}""") + assert tmpl.render() == "0" def test_short_conditional_expression(self, env): - tmpl = env.from_string("<{{ 1 if false }}>") - assert tmpl.render() == "<>" + tmpl = env.from_string("<{{ 1 if false }}>") + assert tmpl.render() == "<>" - tmpl = env.from_string("<{{ (1 if false).bar }}>") + tmpl = env.from_string("<{{ (1 if false).bar }}>") pytest.raises(UndefinedError, tmpl.render) def test_filter_priority(self, env): tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}') - assert tmpl.render() == "FOOBAR" + assert tmpl.render() == "FOOBAR" def test_function_calls(self, env): tests = [ - (True, "*foo, bar"), - (True, "*foo, *bar"), - (True, "**foo, *bar"), - (True, "**foo, bar"), - (True, "**foo, **bar"), - (True, "**foo, bar=42"), - (False, "foo, bar"), - (False, "foo, bar=42"), - (False, "foo, bar=23, *args"), - (False, "foo, *args, bar=23"), - (False, "a, b=c, *d, **e"), - (False, "*foo, bar=42"), - (False, "*foo, **bar"), - (False, "*foo, bar=42, **baz"), - (False, "foo, *args, bar=23, **baz"), + (True, "*foo, bar"), + (True, "*foo, *bar"), + (True, "**foo, *bar"), + (True, "**foo, bar"), + (True, "**foo, **bar"), + (True, "**foo, bar=42"), + (False, "foo, bar"), + (False, "foo, bar=42"), + (False, "foo, bar=23, *args"), + (False, "foo, *args, bar=23"), + (False, "a, b=c, *d, **e"), + (False, "*foo, bar=42"), + (False, "*foo, **bar"), + (False, "*foo, bar=42, **baz"), + (False, "foo, *args, bar=23, **baz"), ] for should_fail, sig in tests: if should_fail: - with pytest.raises(TemplateSyntaxError): - env.from_string(f"{{{{ foo({sig}) }}}}") + with pytest.raises(TemplateSyntaxError): + env.from_string(f"{{{{ foo({sig}) }}}}") else: - env.from_string(f"foo({sig})") + env.from_string(f"foo({sig})") def test_tuple_expr(self, env): for tmpl in [ - "{{ () }}", - "{{ (1, 2) }}", - "{{ (1, 2,) }}", - "{{ 1, }}", - "{{ 1, 2 }}", - "{% for foo, bar in seq %}...{% endfor %}", - "{% for x in foo, bar %}...{% endfor %}", - "{% for x in foo, %}...{% endfor %}", + "{{ () }}", + "{{ (1, 2) }}", + "{{ (1, 2,) }}", + "{{ 1, }}", + "{{ 1, 2 }}", + "{% for foo, bar in seq %}...{% endfor %}", + "{% for x in foo, bar %}...{% endfor %}", + "{% for x in foo, %}...{% endfor %}", ]: assert env.from_string(tmpl) def test_trailing_comma(self, env): - tmpl = env.from_string("{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}") - assert tmpl.render().lower() == "(1, 2)|[1, 2]|{1: 2}" + tmpl = env.from_string("{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}") + assert tmpl.render().lower() == "(1, 2)|[1, 2]|{1: 2}" def test_block_end_name(self, env): - env.from_string("{% block foo %}...{% endblock foo %}") - pytest.raises( - TemplateSyntaxError, env.from_string, "{% block x %}{% endblock y %}" - ) + env.from_string("{% block foo %}...{% endblock foo %}") + pytest.raises( + TemplateSyntaxError, env.from_string, "{% block x %}{% endblock y %}" + ) def test_constant_casing(self, env): for const in True, False, None: - const = str(const) - tmpl = env.from_string( - f"{{{{ {const} }}}}|{{{{ {const.lower()} }}}}|{{{{ {const.upper()} }}}}" - ) - assert tmpl.render() == f"{const}|{const}|" + const = str(const) + tmpl = env.from_string( + f"{{{{ {const} }}}}|{{{{ {const.lower()} }}}}|{{{{ {const.upper()} }}}}" + ) + assert tmpl.render() == f"{const}|{const}|" def test_test_chaining(self, env): - pytest.raises( - TemplateSyntaxError, env.from_string, "{{ foo is string is sequence }}" - ) - assert env.from_string("{{ 42 is string or 42 is number }}").render() == "True" + pytest.raises( + TemplateSyntaxError, env.from_string, "{{ foo is string is sequence }}" + ) + assert env.from_string("{{ 42 is string or 42 is number }}").render() == "True" def test_string_concatenation(self, env): tmpl = env.from_string('{{ "foo" "bar" "baz" }}') - assert tmpl.render() == "foobarbaz" + assert tmpl.render() == "foobarbaz" def test_notin(self, env): bar = range(100) - tmpl = env.from_string("""{{ not 42 in bar }}""") - assert tmpl.render(bar=bar) == "False" + tmpl = env.from_string("""{{ not 42 in bar }}""") + assert tmpl.render(bar=bar) == "False" def test_operator_precedence(self, env): - tmpl = env.from_string("""{{ 2 * 3 + 4 % 2 + 1 - 2 }}""") - assert tmpl.render() == "5" + tmpl = env.from_string("""{{ 2 * 3 + 4 % 2 + 1 - 2 }}""") + assert tmpl.render() == "5" def test_implicit_subscribed_tuple(self, env): - class Foo: + class Foo: def __getitem__(self, x): return x - t = env.from_string("{{ foo[1, 2] }}") - assert t.render(foo=Foo()) == "(1, 2)" - + t = env.from_string("{{ foo[1, 2] }}") + assert t.render(foo=Foo()) == "(1, 2)" + def test_raw2(self, env): - tmpl = env.from_string("{% raw %}{{ FOO }} and {% BAR %}{% endraw %}") - assert tmpl.render() == "{{ FOO }} and {% BAR %}" + tmpl = env.from_string("{% raw %}{{ FOO }} and {% BAR %}{% endraw %}") + assert tmpl.render() == "{{ FOO }} and {% BAR %}" def test_const(self, env): tmpl = env.from_string( - "{{ true }}|{{ false }}|{{ none }}|" - "{{ none is defined }}|{{ missing is defined }}" - ) - assert tmpl.render() == "True|False|None|True|False" + "{{ true }}|{{ false }}|{{ none }}|" + "{{ none is defined }}|{{ missing is defined }}" + ) + assert tmpl.render() == "True|False|None|True|False" def test_neg_filter_priority(self, env): - node = env.parse("{{ -1|foo }}") + node = env.parse("{{ -1|foo }}") assert isinstance(node.body[0].nodes[0], nodes.Filter) assert isinstance(node.body[0].nodes[0].node, nodes.Neg) def test_const_assign(self, env): - constass1 = """{% set true = 42 %}""" - constass2 = """{% for none in seq %}{% endfor %}""" + constass1 = """{% set true = 42 %}""" + constass2 = """{% for none in seq %}{% endfor %}""" for tmpl in constass1, constass2: pytest.raises(TemplateSyntaxError, env.from_string, tmpl) def test_localset(self, env): - tmpl = env.from_string( - """{% set foo = 0 %}\ + tmpl = env.from_string( + """{% set foo = 0 %}\ {% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\ -{{ foo }}""" - ) - assert tmpl.render() == "0" +{{ foo }}""" + ) + assert tmpl.render() == "0" def test_parse_unary(self, env): tmpl = env.from_string('{{ -foo["bar"] }}') - assert tmpl.render(foo={"bar": 42}) == "-42" + assert tmpl.render(foo={"bar": 42}) == "-42" tmpl = env.from_string('{{ -foo["bar"]|abs }}') - assert tmpl.render(foo={"bar": 42}) == "42" + assert tmpl.render(foo={"bar": 42}) == "42" -class TestLstripBlocks: +class TestLstripBlocks: def test_lstrip(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(""" {% if True %}\n {% endif %}""") + tmpl = env.from_string(""" {% if True %}\n {% endif %}""") assert tmpl.render() == "\n" def test_lstrip_trim(self, env): env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string(""" {% if True %}\n {% endif %}""") + tmpl = env.from_string(""" {% if True %}\n {% endif %}""") assert tmpl.render() == "" def test_no_lstrip(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""") + tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""") + assert tmpl.render() == " \n " + + def test_lstrip_blocks_false_with_no_lstrip(self, env): + # Test that + is a NOP (but does not cause an error) if lstrip_blocks=False + env = Environment(lstrip_blocks=False, trim_blocks=False) + tmpl = env.from_string(""" {% if True %}\n {% endif %}""") + assert tmpl.render() == " \n " + tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""") assert tmpl.render() == " \n " - def test_lstrip_blocks_false_with_no_lstrip(self, env): - # Test that + is a NOP (but does not cause an error) if lstrip_blocks=False - env = Environment(lstrip_blocks=False, trim_blocks=False) - tmpl = env.from_string(""" {% if True %}\n {% endif %}""") - assert tmpl.render() == " \n " - tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""") - assert tmpl.render() == " \n " - def test_lstrip_endline(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(""" hello{% if True %}\n goodbye{% endif %}""") + tmpl = env.from_string(""" hello{% if True %}\n goodbye{% endif %}""") assert tmpl.render() == " hello\n goodbye" def test_lstrip_inline(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(""" {% if True %}hello {% endif %}""") - assert tmpl.render() == "hello " + tmpl = env.from_string(""" {% if True %}hello {% endif %}""") + assert tmpl.render() == "hello " def test_lstrip_nested(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) tmpl = env.from_string( - """ {% if True %}a {% if True %}b {% endif %}c {% endif %}""" - ) - assert tmpl.render() == "a b c " + """ {% if True %}a {% if True %}b {% endif %}c {% endif %}""" + ) + assert tmpl.render() == "a b c " def test_lstrip_left_chars(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string( - """ abc {% if True %} - hello{% endif %}""" - ) - assert tmpl.render() == " abc \n hello" + tmpl = env.from_string( + """ abc {% if True %} + hello{% endif %}""" + ) + assert tmpl.render() == " abc \n hello" def test_lstrip_embeded_strings(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string(""" {% set x = " {% str %} " %}{{ x }}""") - assert tmpl.render() == " {% str %} " + tmpl = env.from_string(""" {% set x = " {% str %} " %}{{ x }}""") + assert tmpl.render() == " {% str %} " def test_lstrip_preserve_leading_newlines(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string("""\n\n\n{% set hello = 1 %}""") - assert tmpl.render() == "\n\n\n" + tmpl = env.from_string("""\n\n\n{% set hello = 1 %}""") + assert tmpl.render() == "\n\n\n" def test_lstrip_comment(self, env): env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string( - """ {# if True #} + tmpl = env.from_string( + """ {# if True #} hello - {#endif#}""" - ) - assert tmpl.render() == "\nhello\n" + {#endif#}""" + ) + assert tmpl.render() == "\nhello\n" def test_lstrip_angle_bracket_simple(self, env): - env = Environment( - "<%", - "%>", - "${", - "}", - "<%#", - "%>", - "%", - "##", - lstrip_blocks=True, - trim_blocks=True, - ) - tmpl = env.from_string(""" <% if True %>hello <% endif %>""") - assert tmpl.render() == "hello " + env = Environment( + "<%", + "%>", + "${", + "}", + "<%#", + "%>", + "%", + "##", + lstrip_blocks=True, + trim_blocks=True, + ) + tmpl = env.from_string(""" <% if True %>hello <% endif %>""") + assert tmpl.render() == "hello " def test_lstrip_angle_bracket_comment(self, env): - env = Environment( - "<%", - "%>", - "${", - "}", - "<%#", - "%>", - "%", - "##", - lstrip_blocks=True, - trim_blocks=True, - ) - tmpl = env.from_string(""" <%# if True %>hello <%# endif %>""") - assert tmpl.render() == "hello " + env = Environment( + "<%", + "%>", + "${", + "}", + "<%#", + "%>", + "%", + "##", + lstrip_blocks=True, + trim_blocks=True, + ) + tmpl = env.from_string(""" <%# if True %>hello <%# endif %>""") + assert tmpl.render() == "hello " def test_lstrip_angle_bracket(self, env): - env = Environment( - "<%", - "%>", - "${", - "}", - "<%#", - "%>", - "%", - "##", - lstrip_blocks=True, - trim_blocks=True, - ) - tmpl = env.from_string( - """\ + env = Environment( + "<%", + "%>", + "${", + "}", + "<%#", + "%>", + "%", + "##", + lstrip_blocks=True, + trim_blocks=True, + ) + tmpl = env.from_string( + """\ <%# regular comment %> <% for item in seq %> ${item} ## the rest of the stuff - <% endfor %>""" - ) - assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5)) + <% endfor %>""" + ) + assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5)) def test_lstrip_angle_bracket_compact(self, env): - env = Environment( - "<%", - "%>", - "${", - "}", - "<%#", - "%>", - "%", - "##", - lstrip_blocks=True, - trim_blocks=True, - ) - tmpl = env.from_string( - """\ + env = Environment( + "<%", + "%>", + "${", + "}", + "<%#", + "%>", + "%", + "##", + lstrip_blocks=True, + trim_blocks=True, + ) + tmpl = env.from_string( + """\ <%#regular comment%> <%for item in seq%> ${item} ## the rest of the stuff - <%endfor%>""" - ) - assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5)) - - def test_lstrip_blocks_outside_with_new_line(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string( - " {% if kvs %}(\n" - " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" - " ){% endif %}" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == "(\na=1 b=2 \n )" - - def test_lstrip_trim_blocks_outside_with_new_line(self): - env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string( - " {% if kvs %}(\n" - " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" - " ){% endif %}" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == "(\na=1 b=2 )" - - def test_lstrip_blocks_inside_with_new_line(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string( - " ({% if kvs %}\n" - " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" - " {% endif %})" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == " (\na=1 b=2 \n)" - - def test_lstrip_trim_blocks_inside_with_new_line(self): - env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string( - " ({% if kvs %}\n" - " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" - " {% endif %})" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == " (a=1 b=2 )" - - def test_lstrip_blocks_without_new_line(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string( - " {% if kvs %}" - " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}" - " {% endif %}" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == " a=1 b=2 " - - def test_lstrip_trim_blocks_without_new_line(self): - env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string( - " {% if kvs %}" - " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}" - " {% endif %}" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == " a=1 b=2 " - - def test_lstrip_blocks_consume_after_without_new_line(self): - env = Environment(lstrip_blocks=True, trim_blocks=False) - tmpl = env.from_string( - " {% if kvs -%}" - " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}" - " {% endif -%}" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == "a=1 b=2 " - - def test_lstrip_trim_blocks_consume_before_without_new_line(self): - env = Environment(lstrip_blocks=False, trim_blocks=False) - tmpl = env.from_string( - " {%- if kvs %}" - " {%- for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}" - " {%- endif %}" - ) - out = tmpl.render(kvs=[("a", 1), ("b", 2)]) - assert out == "a=1 b=2 " - - def test_lstrip_trim_blocks_comment(self): - env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string(" {# 1 space #}\n {# 2 spaces #} {# 4 spaces #}") - out = tmpl.render() - assert out == " " * 4 - - def test_lstrip_trim_blocks_raw(self): - env = Environment(lstrip_blocks=True, trim_blocks=True) - tmpl = env.from_string("{{x}}\n{%- raw %} {% endraw -%}\n{{ y }}") - out = tmpl.render(x=1, y=2) - assert out == "1 2" - + <%endfor%>""" + ) + assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5)) + + def test_lstrip_blocks_outside_with_new_line(self): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string( + " {% if kvs %}(\n" + " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" + " ){% endif %}" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == "(\na=1 b=2 \n )" + + def test_lstrip_trim_blocks_outside_with_new_line(self): + env = Environment(lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string( + " {% if kvs %}(\n" + " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" + " ){% endif %}" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == "(\na=1 b=2 )" + + def test_lstrip_blocks_inside_with_new_line(self): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string( + " ({% if kvs %}\n" + " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" + " {% endif %})" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == " (\na=1 b=2 \n)" + + def test_lstrip_trim_blocks_inside_with_new_line(self): + env = Environment(lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string( + " ({% if kvs %}\n" + " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}\n" + " {% endif %})" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == " (a=1 b=2 )" + + def test_lstrip_blocks_without_new_line(self): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string( + " {% if kvs %}" + " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}" + " {% endif %}" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == " a=1 b=2 " + + def test_lstrip_trim_blocks_without_new_line(self): + env = Environment(lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string( + " {% if kvs %}" + " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor %}" + " {% endif %}" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == " a=1 b=2 " + + def test_lstrip_blocks_consume_after_without_new_line(self): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string( + " {% if kvs -%}" + " {% for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}" + " {% endif -%}" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == "a=1 b=2 " + + def test_lstrip_trim_blocks_consume_before_without_new_line(self): + env = Environment(lstrip_blocks=False, trim_blocks=False) + tmpl = env.from_string( + " {%- if kvs %}" + " {%- for k, v in kvs %}{{ k }}={{ v }} {% endfor -%}" + " {%- endif %}" + ) + out = tmpl.render(kvs=[("a", 1), ("b", 2)]) + assert out == "a=1 b=2 " + + def test_lstrip_trim_blocks_comment(self): + env = Environment(lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string(" {# 1 space #}\n {# 2 spaces #} {# 4 spaces #}") + out = tmpl.render() + assert out == " " * 4 + + def test_lstrip_trim_blocks_raw(self): + env = Environment(lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string("{{x}}\n{%- raw %} {% endraw -%}\n{{ y }}") + out = tmpl.render(x=1, y=2) + assert out == "1 2" + def test_php_syntax_with_manual(self, env): - env = Environment( - "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True - ) - tmpl = env.from_string( - """\ + env = Environment( + "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True + ) + tmpl = env.from_string( + """\ <!-- I'm a comment, I'm not interesting --> <? for item in seq -?> <?= item ?> - <?- endfor ?>""" - ) - assert tmpl.render(seq=range(5)) == "01234" + <?- endfor ?>""" + ) + assert tmpl.render(seq=range(5)) == "01234" def test_php_syntax(self, env): - env = Environment( - "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True - ) - tmpl = env.from_string( - """\ + env = Environment( + "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True + ) + tmpl = env.from_string( + """\ <!-- I'm a comment, I'm not interesting --> <? for item in seq ?> <?= item ?> - <? endfor ?>""" - ) - assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5)) + <? endfor ?>""" + ) + assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5)) def test_php_syntax_compact(self, env): - env = Environment( - "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True - ) - tmpl = env.from_string( - """\ + env = Environment( + "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True + ) + tmpl = env.from_string( + """\ <!-- I'm a comment, I'm not interesting --> <?for item in seq?> <?=item?> - <?endfor?>""" - ) - assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5)) + <?endfor?>""" + ) + assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5)) def test_erb_syntax(self, env): - env = Environment( - "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True - ) - tmpl = env.from_string( - """\ + env = Environment( + "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True + ) + tmpl = env.from_string( + """\ <%# I'm a comment, I'm not interesting %> <% for item in seq %> <%= item %> <% endfor %> -""" - ) - assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5)) +""" + ) + assert tmpl.render(seq=range(5)) == "".join(f" {x}\n" for x in range(5)) def test_erb_syntax_with_manual(self, env): - env = Environment( - "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True - ) - tmpl = env.from_string( - """\ + env = Environment( + "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True + ) + tmpl = env.from_string( + """\ <%# I'm a comment, I'm not interesting %> <% for item in seq -%> <%= item %> - <%- endfor %>""" - ) - assert tmpl.render(seq=range(5)) == "01234" + <%- endfor %>""" + ) + assert tmpl.render(seq=range(5)) == "01234" def test_erb_syntax_no_lstrip(self, env): - env = Environment( - "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True - ) - tmpl = env.from_string( - """\ + env = Environment( + "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True + ) + tmpl = env.from_string( + """\ <%# I'm a comment, I'm not interesting %> <%+ for item in seq -%> <%= item %> - <%- endfor %>""" - ) - assert tmpl.render(seq=range(5)) == " 01234" + <%- endfor %>""" + ) + assert tmpl.render(seq=range(5)) == " 01234" def test_comment_syntax(self, env): - env = Environment( - "<!--", - "-->", - "${", - "}", - "<!--#", - "-->", - lstrip_blocks=True, - trim_blocks=True, - ) - tmpl = env.from_string( - """\ + env = Environment( + "<!--", + "-->", + "${", + "}", + "<!--#", + "-->", + lstrip_blocks=True, + trim_blocks=True, + ) + tmpl = env.from_string( + """\ <!--# I'm a comment, I'm not interesting -->\ <!-- for item in seq ---> ${item} -<!--- endfor -->""" - ) - assert tmpl.render(seq=range(5)) == "01234" - - -class TestTrimBlocks: - def test_trim(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=False) - tmpl = env.from_string(" {% if True %}\n {% endif %}") - assert tmpl.render() == " " - - def test_no_trim(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=False) - tmpl = env.from_string(" {% if True +%}\n {% endif %}") - assert tmpl.render() == " \n " - - def test_no_trim_outer(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=False) - tmpl = env.from_string("{% if True %}X{% endif +%}\nmore things") - assert tmpl.render() == "X\nmore things" - - def test_lstrip_no_trim(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string(" {% if True +%}\n {% endif %}") - assert tmpl.render() == "\n" - - def test_trim_blocks_false_with_no_trim(self, env): - # Test that + is a NOP (but does not cause an error) if trim_blocks=False - env = Environment(trim_blocks=False, lstrip_blocks=False) - tmpl = env.from_string(" {% if True %}\n {% endif %}") - assert tmpl.render() == " \n " - tmpl = env.from_string(" {% if True +%}\n {% endif %}") - assert tmpl.render() == " \n " - - tmpl = env.from_string(" {# comment #}\n ") - assert tmpl.render() == " \n " - tmpl = env.from_string(" {# comment +#}\n ") - assert tmpl.render() == " \n " - - tmpl = env.from_string(" {% raw %}{% endraw %}\n ") - assert tmpl.render() == " \n " - tmpl = env.from_string(" {% raw %}{% endraw +%}\n ") - assert tmpl.render() == " \n " - - def test_trim_nested(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string( - " {% if True %}\na {% if True %}\nb {% endif %}\nc {% endif %}" - ) - assert tmpl.render() == "a b c " - - def test_no_trim_nested(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string( - " {% if True +%}\na {% if True +%}\nb {% endif +%}\nc {% endif %}" - ) - assert tmpl.render() == "\na \nb \nc " - - def test_comment_trim(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string(""" {# comment #}\n\n """) - assert tmpl.render() == "\n " - - def test_comment_no_trim(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string(""" {# comment +#}\n\n """) - assert tmpl.render() == "\n\n " - - def test_multiple_comment_trim_lstrip(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string( - " {# comment #}\n\n{# comment2 #}\n \n{# comment3 #}\n\n " - ) - assert tmpl.render() == "\n \n\n " - - def test_multiple_comment_no_trim_lstrip(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string( - " {# comment +#}\n\n{# comment2 +#}\n \n{# comment3 +#}\n\n " - ) - assert tmpl.render() == "\n\n\n \n\n\n " - - def test_raw_trim_lstrip(self, env): - env = Environment(trim_blocks=True, lstrip_blocks=True) - tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw %}\n\n{{ y }}") - assert tmpl.render(x=1, y=2) == "1\n\n\n2" - - def test_raw_no_trim_lstrip(self, env): - env = Environment(trim_blocks=False, lstrip_blocks=True) - tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw +%}\n\n{{ y }}") - assert tmpl.render(x=1, y=2) == "1\n\n\n\n2" - - # raw blocks do not process inner text, so start tag cannot ignore trim - with pytest.raises(TemplateSyntaxError): - tmpl = env.from_string("{{x}}{% raw +%}\n\n {% endraw +%}\n\n{{ y }}") - - def test_no_trim_angle_bracket(self, env): - env = Environment( - "<%", "%>", "${", "}", "<%#", "%>", lstrip_blocks=True, trim_blocks=True - ) - tmpl = env.from_string(" <% if True +%>\n\n <% endif %>") - assert tmpl.render() == "\n\n" - - tmpl = env.from_string(" <%# comment +%>\n\n ") - assert tmpl.render() == "\n\n " - - def test_no_trim_php_syntax(self, env): - env = Environment( - "<?", - "?>", - "<?=", - "?>", - "<!--", - "-->", - lstrip_blocks=False, - trim_blocks=True, - ) - tmpl = env.from_string(" <? if True +?>\n\n <? endif ?>") - assert tmpl.render() == " \n\n " - tmpl = env.from_string(" <!-- comment +-->\n\n ") - assert tmpl.render() == " \n\n " +<!--- endfor -->""" + ) + assert tmpl.render(seq=range(5)) == "01234" + + +class TestTrimBlocks: + def test_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=False) + tmpl = env.from_string(" {% if True %}\n {% endif %}") + assert tmpl.render() == " " + + def test_no_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=False) + tmpl = env.from_string(" {% if True +%}\n {% endif %}") + assert tmpl.render() == " \n " + + def test_no_trim_outer(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=False) + tmpl = env.from_string("{% if True %}X{% endif +%}\nmore things") + assert tmpl.render() == "X\nmore things" + + def test_lstrip_no_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string(" {% if True +%}\n {% endif %}") + assert tmpl.render() == "\n" + + def test_trim_blocks_false_with_no_trim(self, env): + # Test that + is a NOP (but does not cause an error) if trim_blocks=False + env = Environment(trim_blocks=False, lstrip_blocks=False) + tmpl = env.from_string(" {% if True %}\n {% endif %}") + assert tmpl.render() == " \n " + tmpl = env.from_string(" {% if True +%}\n {% endif %}") + assert tmpl.render() == " \n " + + tmpl = env.from_string(" {# comment #}\n ") + assert tmpl.render() == " \n " + tmpl = env.from_string(" {# comment +#}\n ") + assert tmpl.render() == " \n " + + tmpl = env.from_string(" {% raw %}{% endraw %}\n ") + assert tmpl.render() == " \n " + tmpl = env.from_string(" {% raw %}{% endraw +%}\n ") + assert tmpl.render() == " \n " + + def test_trim_nested(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {% if True %}\na {% if True %}\nb {% endif %}\nc {% endif %}" + ) + assert tmpl.render() == "a b c " + + def test_no_trim_nested(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {% if True +%}\na {% if True +%}\nb {% endif +%}\nc {% endif %}" + ) + assert tmpl.render() == "\na \nb \nc " + + def test_comment_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string(""" {# comment #}\n\n """) + assert tmpl.render() == "\n " + + def test_comment_no_trim(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string(""" {# comment +#}\n\n """) + assert tmpl.render() == "\n\n " + + def test_multiple_comment_trim_lstrip(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {# comment #}\n\n{# comment2 #}\n \n{# comment3 #}\n\n " + ) + assert tmpl.render() == "\n \n\n " + + def test_multiple_comment_no_trim_lstrip(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string( + " {# comment +#}\n\n{# comment2 +#}\n \n{# comment3 +#}\n\n " + ) + assert tmpl.render() == "\n\n\n \n\n\n " + + def test_raw_trim_lstrip(self, env): + env = Environment(trim_blocks=True, lstrip_blocks=True) + tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw %}\n\n{{ y }}") + assert tmpl.render(x=1, y=2) == "1\n\n\n2" + + def test_raw_no_trim_lstrip(self, env): + env = Environment(trim_blocks=False, lstrip_blocks=True) + tmpl = env.from_string("{{x}}{% raw %}\n\n {% endraw +%}\n\n{{ y }}") + assert tmpl.render(x=1, y=2) == "1\n\n\n\n2" + + # raw blocks do not process inner text, so start tag cannot ignore trim + with pytest.raises(TemplateSyntaxError): + tmpl = env.from_string("{{x}}{% raw +%}\n\n {% endraw +%}\n\n{{ y }}") + + def test_no_trim_angle_bracket(self, env): + env = Environment( + "<%", "%>", "${", "}", "<%#", "%>", lstrip_blocks=True, trim_blocks=True + ) + tmpl = env.from_string(" <% if True +%>\n\n <% endif %>") + assert tmpl.render() == "\n\n" + + tmpl = env.from_string(" <%# comment +%>\n\n ") + assert tmpl.render() == "\n\n " + + def test_no_trim_php_syntax(self, env): + env = Environment( + "<?", + "?>", + "<?=", + "?>", + "<!--", + "-->", + lstrip_blocks=False, + trim_blocks=True, + ) + tmpl = env.from_string(" <? if True +?>\n\n <? endif ?>") + assert tmpl.render() == " \n\n " + tmpl = env.from_string(" <!-- comment +-->\n\n ") + assert tmpl.render() == " \n\n " diff --git a/contrib/python/Jinja2/py3/tests/test_loader.py b/contrib/python/Jinja2/py3/tests/test_loader.py index ff07570bc3..5042350607 100644 --- a/contrib/python/Jinja2/py3/tests/test_loader.py +++ b/contrib/python/Jinja2/py3/tests/test_loader.py @@ -1,197 +1,197 @@ -import importlib.abc -import importlib.machinery -import importlib.util +import importlib.abc +import importlib.machinery +import importlib.util import os -import platform -import shutil +import platform +import shutil import sys import tempfile -import time +import time import weakref -from pathlib import Path - -import pytest - -from jinja2 import Environment -from jinja2 import loaders -from jinja2 import PackageLoader -from jinja2.exceptions import TemplateNotFound +from pathlib import Path + +import pytest + +from jinja2 import Environment +from jinja2 import loaders +from jinja2 import PackageLoader +from jinja2.exceptions import TemplateNotFound from jinja2.loaders import split_template_path -import yatest.common as yc +import yatest.common as yc + - -class TestLoaders: +class TestLoaders: def test_dict_loader(self, dict_loader): env = Environment(loader=dict_loader) - tmpl = env.get_template("justdict.html") - assert tmpl.render().strip() == "FOO" - pytest.raises(TemplateNotFound, env.get_template, "missing.html") + tmpl = env.get_template("justdict.html") + assert tmpl.render().strip() == "FOO" + pytest.raises(TemplateNotFound, env.get_template, "missing.html") def test_package_loader(self, package_loader): env = Environment(loader=package_loader) - tmpl = env.get_template("test.html") - assert tmpl.render().strip() == "BAR" - pytest.raises(TemplateNotFound, env.get_template, "missing.html") - - def test_filesystem_loader_overlapping_names(self, filesystem_loader): - t2_dir = Path(filesystem_loader.searchpath[0]) / ".." / "templates2" - # Make "foo" show up before "foo/test.html". - filesystem_loader.searchpath.insert(0, t2_dir) - e = Environment(loader=filesystem_loader) - e.get_template("foo") - # This would raise NotADirectoryError if "t2/foo" wasn't skipped. - e.get_template("foo/test.html") + tmpl = env.get_template("test.html") + assert tmpl.render().strip() == "BAR" + pytest.raises(TemplateNotFound, env.get_template, "missing.html") + + def test_filesystem_loader_overlapping_names(self, filesystem_loader): + t2_dir = Path(filesystem_loader.searchpath[0]) / ".." / "templates2" + # Make "foo" show up before "foo/test.html". + filesystem_loader.searchpath.insert(0, t2_dir) + e = Environment(loader=filesystem_loader) + e.get_template("foo") + # This would raise NotADirectoryError if "t2/foo" wasn't skipped. + e.get_template("foo/test.html") def test_choice_loader(self, choice_loader): env = Environment(loader=choice_loader) - tmpl = env.get_template("justdict.html") - assert tmpl.render().strip() == "FOO" - tmpl = env.get_template("test.html") - assert tmpl.render().strip() == "BAR" - pytest.raises(TemplateNotFound, env.get_template, "missing.html") + tmpl = env.get_template("justdict.html") + assert tmpl.render().strip() == "FOO" + tmpl = env.get_template("test.html") + assert tmpl.render().strip() == "BAR" + pytest.raises(TemplateNotFound, env.get_template, "missing.html") def test_function_loader(self, function_loader): env = Environment(loader=function_loader) - tmpl = env.get_template("justfunction.html") - assert tmpl.render().strip() == "FOO" - pytest.raises(TemplateNotFound, env.get_template, "missing.html") + tmpl = env.get_template("justfunction.html") + assert tmpl.render().strip() == "FOO" + pytest.raises(TemplateNotFound, env.get_template, "missing.html") def test_prefix_loader(self, prefix_loader): env = Environment(loader=prefix_loader) - tmpl = env.get_template("a/test.html") - assert tmpl.render().strip() == "BAR" - tmpl = env.get_template("b/justdict.html") - assert tmpl.render().strip() == "FOO" - pytest.raises(TemplateNotFound, env.get_template, "missing") + tmpl = env.get_template("a/test.html") + assert tmpl.render().strip() == "BAR" + tmpl = env.get_template("b/justdict.html") + assert tmpl.render().strip() == "FOO" + pytest.raises(TemplateNotFound, env.get_template, "missing") def test_caching(self): changed = False class TestLoader(loaders.BaseLoader): def get_source(self, environment, template): - return "foo", None, lambda: not changed - + return "foo", None, lambda: not changed + env = Environment(loader=TestLoader(), cache_size=-1) - tmpl = env.get_template("template") - assert tmpl is env.get_template("template") + tmpl = env.get_template("template") + assert tmpl is env.get_template("template") changed = True - assert tmpl is not env.get_template("template") + assert tmpl is not env.get_template("template") changed = False def test_no_cache(self): - mapping = {"foo": "one"} + mapping = {"foo": "one"} env = Environment(loader=loaders.DictLoader(mapping), cache_size=0) - assert env.get_template("foo") is not env.get_template("foo") + assert env.get_template("foo") is not env.get_template("foo") def test_limited_size_cache(self): - mapping = {"one": "foo", "two": "bar", "three": "baz"} + mapping = {"one": "foo", "two": "bar", "three": "baz"} loader = loaders.DictLoader(mapping) env = Environment(loader=loader, cache_size=2) - t1 = env.get_template("one") - t2 = env.get_template("two") - assert t2 is env.get_template("two") - assert t1 is env.get_template("one") - env.get_template("three") + t1 = env.get_template("one") + t2 = env.get_template("two") + assert t2 is env.get_template("two") + assert t1 is env.get_template("one") + env.get_template("three") loader_ref = weakref.ref(loader) - assert (loader_ref, "one") in env.cache - assert (loader_ref, "two") not in env.cache - assert (loader_ref, "three") in env.cache + assert (loader_ref, "one") in env.cache + assert (loader_ref, "two") not in env.cache + assert (loader_ref, "three") in env.cache def test_cache_loader_change(self): - loader1 = loaders.DictLoader({"foo": "one"}) - loader2 = loaders.DictLoader({"foo": "two"}) + loader1 = loaders.DictLoader({"foo": "one"}) + loader2 = loaders.DictLoader({"foo": "two"}) env = Environment(loader=loader1, cache_size=2) - assert env.get_template("foo").render() == "one" + assert env.get_template("foo").render() == "one" env.loader = loader2 - assert env.get_template("foo").render() == "two" + assert env.get_template("foo").render() == "two" def test_dict_loader_cache_invalidates(self): - mapping = {"foo": "one"} + mapping = {"foo": "one"} env = Environment(loader=loaders.DictLoader(mapping)) - assert env.get_template("foo").render() == "one" - mapping["foo"] = "two" - assert env.get_template("foo").render() == "two" + assert env.get_template("foo").render() == "one" + mapping["foo"] = "two" + assert env.get_template("foo").render() == "two" def test_split_template_path(self): - assert split_template_path("foo/bar") == ["foo", "bar"] - assert split_template_path("./foo/bar") == ["foo", "bar"] - pytest.raises(TemplateNotFound, split_template_path, "../foo") - - -class TestFileSystemLoader: - searchpath = (Path(yc.test_source_path()) / "res" / "templates").resolve() - - @staticmethod - def _test_common(env): - tmpl = env.get_template("test.html") - assert tmpl.render().strip() == "BAR" - tmpl = env.get_template("foo/test.html") - assert tmpl.render().strip() == "FOO" - pytest.raises(TemplateNotFound, env.get_template, "missing.html") - - def test_searchpath_as_str(self): - filesystem_loader = loaders.FileSystemLoader(str(self.searchpath)) - - env = Environment(loader=filesystem_loader) - self._test_common(env) - - def test_searchpath_as_pathlib(self): - filesystem_loader = loaders.FileSystemLoader(self.searchpath) - env = Environment(loader=filesystem_loader) - self._test_common(env) - - def test_searchpath_as_list_including_pathlib(self): - filesystem_loader = loaders.FileSystemLoader( - ["/tmp/templates", self.searchpath] - ) - env = Environment(loader=filesystem_loader) - self._test_common(env) - - @pytest.mark.skip("Arcadia read only") - def test_caches_template_based_on_mtime(self): - filesystem_loader = loaders.FileSystemLoader(self.searchpath) - - env = Environment(loader=filesystem_loader) - tmpl1 = env.get_template("test.html") - tmpl2 = env.get_template("test.html") - assert tmpl1 is tmpl2 - - os.utime(self.searchpath / "test.html", (time.time(), time.time())) - tmpl3 = env.get_template("test.html") - assert tmpl1 is not tmpl3 - - @pytest.mark.parametrize( - ("encoding", "expect"), - [ - ("utf-8", "文字化け"), - ("iso-8859-1", "æ\x96\x87\xe5\xad\x97\xe5\x8c\x96\xe3\x81\x91"), - ], - ) - def test_uses_specified_encoding(self, encoding, expect): - loader = loaders.FileSystemLoader(self.searchpath, encoding=encoding) - e = Environment(loader=loader) - t = e.get_template("mojibake.txt") - assert t.render() == expect - - -class TestModuleLoader: + assert split_template_path("foo/bar") == ["foo", "bar"] + assert split_template_path("./foo/bar") == ["foo", "bar"] + pytest.raises(TemplateNotFound, split_template_path, "../foo") + + +class TestFileSystemLoader: + searchpath = (Path(yc.test_source_path()) / "res" / "templates").resolve() + + @staticmethod + def _test_common(env): + tmpl = env.get_template("test.html") + assert tmpl.render().strip() == "BAR" + tmpl = env.get_template("foo/test.html") + assert tmpl.render().strip() == "FOO" + pytest.raises(TemplateNotFound, env.get_template, "missing.html") + + def test_searchpath_as_str(self): + filesystem_loader = loaders.FileSystemLoader(str(self.searchpath)) + + env = Environment(loader=filesystem_loader) + self._test_common(env) + + def test_searchpath_as_pathlib(self): + filesystem_loader = loaders.FileSystemLoader(self.searchpath) + env = Environment(loader=filesystem_loader) + self._test_common(env) + + def test_searchpath_as_list_including_pathlib(self): + filesystem_loader = loaders.FileSystemLoader( + ["/tmp/templates", self.searchpath] + ) + env = Environment(loader=filesystem_loader) + self._test_common(env) + + @pytest.mark.skip("Arcadia read only") + def test_caches_template_based_on_mtime(self): + filesystem_loader = loaders.FileSystemLoader(self.searchpath) + + env = Environment(loader=filesystem_loader) + tmpl1 = env.get_template("test.html") + tmpl2 = env.get_template("test.html") + assert tmpl1 is tmpl2 + + os.utime(self.searchpath / "test.html", (time.time(), time.time())) + tmpl3 = env.get_template("test.html") + assert tmpl1 is not tmpl3 + + @pytest.mark.parametrize( + ("encoding", "expect"), + [ + ("utf-8", "文字化け"), + ("iso-8859-1", "æ\x96\x87\xe5\xad\x97\xe5\x8c\x96\xe3\x81\x91"), + ], + ) + def test_uses_specified_encoding(self, encoding, expect): + loader = loaders.FileSystemLoader(self.searchpath, encoding=encoding) + e = Environment(loader=loader) + t = e.get_template("mojibake.txt") + assert t.render() == expect + + +class TestModuleLoader: archive = None - def compile_down(self, prefix_loader, zip="deflated"): + def compile_down(self, prefix_loader, zip="deflated"): log = [] self.reg_env = Environment(loader=prefix_loader) if zip is not None: - fd, self.archive = tempfile.mkstemp(suffix=".zip") + fd, self.archive = tempfile.mkstemp(suffix=".zip") os.close(fd) else: self.archive = tempfile.mkdtemp() - self.reg_env.compile_templates(self.archive, zip=zip, log_function=log.append) + self.reg_env.compile_templates(self.archive, zip=zip, log_function=log.append) self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive)) - return "".join(log) + return "".join(log) def teardown(self): - if hasattr(self, "mod_env"): + if hasattr(self, "mod_env"): if os.path.isfile(self.archive): os.remove(self.archive) else: @@ -200,31 +200,31 @@ class TestModuleLoader: def test_log(self, prefix_loader): log = self.compile_down(prefix_loader) - assert ( - 'Compiled "a/foo/test.html" as ' - "tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a" in log - ) - assert "Finished compiling templates" in log - assert ( - 'Could not compile "a/syntaxerror.html": ' - "Encountered unknown tag 'endif'" in log - ) + assert ( + 'Compiled "a/foo/test.html" as ' + "tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a" in log + ) + assert "Finished compiling templates" in log + assert ( + 'Could not compile "a/syntaxerror.html": ' + "Encountered unknown tag 'endif'" in log + ) def _test_common(self): - tmpl1 = self.reg_env.get_template("a/test.html") - tmpl2 = self.mod_env.get_template("a/test.html") + tmpl1 = self.reg_env.get_template("a/test.html") + tmpl2 = self.mod_env.get_template("a/test.html") assert tmpl1.render() == tmpl2.render() - tmpl1 = self.reg_env.get_template("b/justdict.html") - tmpl2 = self.mod_env.get_template("b/justdict.html") + tmpl1 = self.reg_env.get_template("b/justdict.html") + tmpl2 = self.mod_env.get_template("b/justdict.html") assert tmpl1.render() == tmpl2.render() def test_deflated_zip_compile(self, prefix_loader): - self.compile_down(prefix_loader, zip="deflated") + self.compile_down(prefix_loader, zip="deflated") self._test_common() def test_stored_zip_compile(self, prefix_loader): - self.compile_down(prefix_loader, zip="stored") + self.compile_down(prefix_loader, zip="stored") self._test_common() def test_filesystem_compile(self, prefix_loader): @@ -233,90 +233,90 @@ class TestModuleLoader: def test_weak_references(self, prefix_loader): self.compile_down(prefix_loader) - self.mod_env.get_template("a/test.html") - key = loaders.ModuleLoader.get_template_key("a/test.html") + self.mod_env.get_template("a/test.html") + key = loaders.ModuleLoader.get_template_key("a/test.html") name = self.mod_env.loader.module.__name__ assert hasattr(self.mod_env.loader.module, key) assert name in sys.modules # unset all, ensure the module is gone from sys.modules - self.mod_env = None + self.mod_env = None try: import gc - + gc.collect() - except BaseException: + except BaseException: pass assert name not in sys.modules def test_choice_loader(self, prefix_loader): - self.compile_down(prefix_loader) - self.mod_env.loader = loaders.ChoiceLoader( - [self.mod_env.loader, loaders.DictLoader({"DICT_SOURCE": "DICT_TEMPLATE"})] - ) - tmpl1 = self.mod_env.get_template("a/test.html") - assert tmpl1.render() == "BAR" - tmpl2 = self.mod_env.get_template("DICT_SOURCE") - assert tmpl2.render() == "DICT_TEMPLATE" - - def test_prefix_loader(self, prefix_loader): - self.compile_down(prefix_loader) - self.mod_env.loader = loaders.PrefixLoader( - { - "MOD": self.mod_env.loader, - "DICT": loaders.DictLoader({"test.html": "DICT_TEMPLATE"}), - } - ) - tmpl1 = self.mod_env.get_template("MOD/a/test.html") - assert tmpl1.render() == "BAR" - tmpl2 = self.mod_env.get_template("DICT/test.html") - assert tmpl2.render() == "DICT_TEMPLATE" - - def test_path_as_pathlib(self, prefix_loader): - self.compile_down(prefix_loader) - - mod_path = self.mod_env.loader.module.__path__[0] - mod_loader = loaders.ModuleLoader(Path(mod_path)) - self.mod_env = Environment(loader=mod_loader) - - self._test_common() - - def test_supports_pathlib_in_list_of_paths(self, prefix_loader): - self.compile_down(prefix_loader) - - mod_path = self.mod_env.loader.module.__path__[0] - mod_loader = loaders.ModuleLoader([Path(mod_path), "/tmp/templates"]) - self.mod_env = Environment(loader=mod_loader) - - self._test_common() - - -@pytest.fixture() -def package_dir_loader(monkeypatch): - monkeypatch.syspath_prepend(Path(__file__).parent) - return PackageLoader("res") - - -@pytest.mark.parametrize( - ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")] -) -def test_package_dir_source(package_dir_loader, template, expect): - source, name, up_to_date = package_dir_loader.get_source(None, template) - assert source.rstrip() == expect - assert name.endswith(os.path.join(*split_template_path(template))) - assert up_to_date() - - -def test_package_dir_list(package_dir_loader): - templates = package_dir_loader.list_templates() - assert "foo/test.html" in templates - assert "test.html" in templates - - -@pytest.fixture() + self.compile_down(prefix_loader) + self.mod_env.loader = loaders.ChoiceLoader( + [self.mod_env.loader, loaders.DictLoader({"DICT_SOURCE": "DICT_TEMPLATE"})] + ) + tmpl1 = self.mod_env.get_template("a/test.html") + assert tmpl1.render() == "BAR" + tmpl2 = self.mod_env.get_template("DICT_SOURCE") + assert tmpl2.render() == "DICT_TEMPLATE" + + def test_prefix_loader(self, prefix_loader): + self.compile_down(prefix_loader) + self.mod_env.loader = loaders.PrefixLoader( + { + "MOD": self.mod_env.loader, + "DICT": loaders.DictLoader({"test.html": "DICT_TEMPLATE"}), + } + ) + tmpl1 = self.mod_env.get_template("MOD/a/test.html") + assert tmpl1.render() == "BAR" + tmpl2 = self.mod_env.get_template("DICT/test.html") + assert tmpl2.render() == "DICT_TEMPLATE" + + def test_path_as_pathlib(self, prefix_loader): + self.compile_down(prefix_loader) + + mod_path = self.mod_env.loader.module.__path__[0] + mod_loader = loaders.ModuleLoader(Path(mod_path)) + self.mod_env = Environment(loader=mod_loader) + + self._test_common() + + def test_supports_pathlib_in_list_of_paths(self, prefix_loader): + self.compile_down(prefix_loader) + + mod_path = self.mod_env.loader.module.__path__[0] + mod_loader = loaders.ModuleLoader([Path(mod_path), "/tmp/templates"]) + self.mod_env = Environment(loader=mod_loader) + + self._test_common() + + +@pytest.fixture() +def package_dir_loader(monkeypatch): + monkeypatch.syspath_prepend(Path(__file__).parent) + return PackageLoader("res") + + +@pytest.mark.parametrize( + ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")] +) +def test_package_dir_source(package_dir_loader, template, expect): + source, name, up_to_date = package_dir_loader.get_source(None, template) + assert source.rstrip() == expect + assert name.endswith(os.path.join(*split_template_path(template))) + assert up_to_date() + + +def test_package_dir_list(package_dir_loader): + templates = package_dir_loader.list_templates() + assert "foo/test.html" in templates + assert "test.html" in templates + + +@pytest.fixture() def package_file_loader(monkeypatch): monkeypatch.syspath_prepend(Path(__file__).parent / "res") return PackageLoader("res") @@ -339,68 +339,68 @@ def test_package_file_list(package_file_loader): @pytest.fixture() -def package_zip_loader(monkeypatch): - package_zip = (Path(yc.test_source_path()) / "res" / "package.zip").resolve() - monkeypatch.syspath_prepend(package_zip) - return PackageLoader("t_pack") - - -@pytest.mark.parametrize( - ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")] -) -def test_package_zip_source(package_zip_loader, template, expect): - source, name, up_to_date = package_zip_loader.get_source(None, template) - assert source.rstrip() == expect - assert name.endswith(os.path.join(*split_template_path(template))) - assert up_to_date is None - - -@pytest.mark.xfail( - platform.python_implementation() == "PyPy", - reason="PyPy's zipimporter doesn't have a '_files' attribute.", - raises=TypeError, -) -def test_package_zip_list(package_zip_loader): - assert package_zip_loader.list_templates() == ["foo/test.html", "test.html"] - - -@pytest.mark.parametrize("package_path", ["", ".", "./"]) -def test_package_zip_omit_curdir(package_zip_loader, package_path): - """PackageLoader should not add or include "." or "./" in the root - path, it is invalid in zip paths. - """ - loader = PackageLoader("t_pack", package_path) - assert loader.package_path == "" - source, _, _ = loader.get_source(None, "templates/foo/test.html") - assert source.rstrip() == "FOO" - - -def test_pep_451_import_hook(): - class ImportHook(importlib.abc.MetaPathFinder, importlib.abc.Loader): - def find_spec(self, name, path=None, target=None): - if name != "res": - return None - - spec = importlib.machinery.PathFinder.find_spec(name) - return importlib.util.spec_from_file_location( - name, - spec.origin, - loader=self, - submodule_search_locations=spec.submodule_search_locations, - ) - - def create_module(self, spec): - return None # default behaviour is fine - - def exec_module(self, module): - return None # we need this to satisfy the interface, it's wrong - - # ensure we restore `sys.meta_path` after putting in our loader - before = sys.meta_path[:] - - try: - sys.meta_path.insert(0, ImportHook()) - package_loader = PackageLoader("res") - assert "test.html" in package_loader.list_templates() - finally: - sys.meta_path[:] = before +def package_zip_loader(monkeypatch): + package_zip = (Path(yc.test_source_path()) / "res" / "package.zip").resolve() + monkeypatch.syspath_prepend(package_zip) + return PackageLoader("t_pack") + + +@pytest.mark.parametrize( + ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")] +) +def test_package_zip_source(package_zip_loader, template, expect): + source, name, up_to_date = package_zip_loader.get_source(None, template) + assert source.rstrip() == expect + assert name.endswith(os.path.join(*split_template_path(template))) + assert up_to_date is None + + +@pytest.mark.xfail( + platform.python_implementation() == "PyPy", + reason="PyPy's zipimporter doesn't have a '_files' attribute.", + raises=TypeError, +) +def test_package_zip_list(package_zip_loader): + assert package_zip_loader.list_templates() == ["foo/test.html", "test.html"] + + +@pytest.mark.parametrize("package_path", ["", ".", "./"]) +def test_package_zip_omit_curdir(package_zip_loader, package_path): + """PackageLoader should not add or include "." or "./" in the root + path, it is invalid in zip paths. + """ + loader = PackageLoader("t_pack", package_path) + assert loader.package_path == "" + source, _, _ = loader.get_source(None, "templates/foo/test.html") + assert source.rstrip() == "FOO" + + +def test_pep_451_import_hook(): + class ImportHook(importlib.abc.MetaPathFinder, importlib.abc.Loader): + def find_spec(self, name, path=None, target=None): + if name != "res": + return None + + spec = importlib.machinery.PathFinder.find_spec(name) + return importlib.util.spec_from_file_location( + name, + spec.origin, + loader=self, + submodule_search_locations=spec.submodule_search_locations, + ) + + def create_module(self, spec): + return None # default behaviour is fine + + def exec_module(self, module): + return None # we need this to satisfy the interface, it's wrong + + # ensure we restore `sys.meta_path` after putting in our loader + before = sys.meta_path[:] + + try: + sys.meta_path.insert(0, ImportHook()) + package_loader = PackageLoader("res") + assert "test.html" in package_loader.list_templates() + finally: + sys.meta_path[:] = before diff --git a/contrib/python/Jinja2/py3/tests/test_nativetypes.py b/contrib/python/Jinja2/py3/tests/test_nativetypes.py index 922af2af72..9bae938cdb 100644 --- a/contrib/python/Jinja2/py3/tests/test_nativetypes.py +++ b/contrib/python/Jinja2/py3/tests/test_nativetypes.py @@ -1,10 +1,10 @@ -import math - +import math + import pytest from jinja2.exceptions import UndefinedError from jinja2.nativetypes import NativeEnvironment -from jinja2.nativetypes import NativeTemplate +from jinja2.nativetypes import NativeTemplate from jinja2.runtime import Undefined @@ -13,140 +13,140 @@ def env(): return NativeEnvironment() -def test_is_defined_native_return(env): - t = env.from_string("{{ missing is defined }}") - assert not t.render() - - -def test_undefined_native_return(env): - t = env.from_string("{{ missing }}") - assert isinstance(t.render(), Undefined) - - -def test_adding_undefined_native_return(env): - t = env.from_string("{{ 3 + missing }}") - - with pytest.raises(UndefinedError): - t.render() - - -def test_cast_int(env): - t = env.from_string("{{ value|int }}") - result = t.render(value="3") - assert isinstance(result, int) - assert result == 3 - - -def test_list_add(env): - t = env.from_string("{{ a + b }}") - result = t.render(a=["a", "b"], b=["c", "d"]) - assert isinstance(result, list) - assert result == ["a", "b", "c", "d"] - - -def test_multi_expression_add(env): - t = env.from_string("{{ a }} + {{ b }}") - result = t.render(a=["a", "b"], b=["c", "d"]) - assert not isinstance(result, list) - assert result == "['a', 'b'] + ['c', 'd']" - - -def test_loops(env): - t = env.from_string("{% for x in value %}{{ x }}{% endfor %}") - result = t.render(value=["a", "b", "c", "d"]) - assert isinstance(result, str) - assert result == "abcd" - - -def test_loops_with_ints(env): - t = env.from_string("{% for x in value %}{{ x }}{% endfor %}") - result = t.render(value=[1, 2, 3, 4]) - assert isinstance(result, int) - assert result == 1234 - - -def test_loop_look_alike(env): - t = env.from_string("{% for x in value %}{{ x }}{% endfor %}") - result = t.render(value=[1]) - assert isinstance(result, int) - assert result == 1 - - -@pytest.mark.parametrize( - ("source", "expect"), - ( - ("{{ value }}", True), - ("{{ value }}", False), - ("{{ 1 == 1 }}", True), - ("{{ 2 + 2 == 5 }}", False), - ("{{ None is none }}", True), - ("{{ '' == None }}", False), - ), -) -def test_booleans(env, source, expect): - t = env.from_string(source) - result = t.render(value=expect) - assert isinstance(result, bool) - assert result is expect - - -def test_variable_dunder(env): - t = env.from_string("{{ x.__class__ }}") - result = t.render(x=True) - assert isinstance(result, type) - - -def test_constant_dunder(env): - t = env.from_string("{{ true.__class__ }}") - result = t.render() - assert isinstance(result, type) - - -def test_constant_dunder_to_string(env): - t = env.from_string("{{ true.__class__|string }}") - result = t.render() - assert not isinstance(result, type) - assert result in {"<type 'bool'>", "<class 'bool'>"} - - -def test_string_literal_var(env): - t = env.from_string("[{{ 'all' }}]") - result = t.render() - assert isinstance(result, str) - assert result == "[all]" - - -def test_string_top_level(env): - t = env.from_string("'Jinja'") - result = t.render() - assert result == "Jinja" - - -def test_tuple_of_variable_strings(env): - t = env.from_string("'{{ a }}', 'data', '{{ b }}', b'{{ c }}'") - result = t.render(a=1, b=2, c="bytes") - assert isinstance(result, tuple) - assert result == ("1", "data", "2", b"bytes") - - -def test_concat_strings_with_quotes(env): - t = env.from_string("--host='{{ host }}' --user \"{{ user }}\"") - result = t.render(host="localhost", user="Jinja") - assert result == "--host='localhost' --user \"Jinja\"" - - -def test_no_intermediate_eval(env): - t = env.from_string("0.000{{ a }}") - result = t.render(a=7) - assert isinstance(result, float) - # If intermediate eval happened, 0.000 would render 0.0, then 7 - # would be appended, resulting in 0.07. - assert math.isclose(result, 0.0007) - - -def test_spontaneous_env(): - t = NativeTemplate("{{ true }}") - assert isinstance(t.environment, NativeEnvironment) +def test_is_defined_native_return(env): + t = env.from_string("{{ missing is defined }}") + assert not t.render() + + +def test_undefined_native_return(env): + t = env.from_string("{{ missing }}") + assert isinstance(t.render(), Undefined) + + +def test_adding_undefined_native_return(env): + t = env.from_string("{{ 3 + missing }}") + + with pytest.raises(UndefinedError): + t.render() + + +def test_cast_int(env): + t = env.from_string("{{ value|int }}") + result = t.render(value="3") + assert isinstance(result, int) + assert result == 3 + + +def test_list_add(env): + t = env.from_string("{{ a + b }}") + result = t.render(a=["a", "b"], b=["c", "d"]) + assert isinstance(result, list) + assert result == ["a", "b", "c", "d"] + + +def test_multi_expression_add(env): + t = env.from_string("{{ a }} + {{ b }}") + result = t.render(a=["a", "b"], b=["c", "d"]) + assert not isinstance(result, list) + assert result == "['a', 'b'] + ['c', 'd']" + + +def test_loops(env): + t = env.from_string("{% for x in value %}{{ x }}{% endfor %}") + result = t.render(value=["a", "b", "c", "d"]) + assert isinstance(result, str) + assert result == "abcd" + + +def test_loops_with_ints(env): + t = env.from_string("{% for x in value %}{{ x }}{% endfor %}") + result = t.render(value=[1, 2, 3, 4]) + assert isinstance(result, int) + assert result == 1234 + + +def test_loop_look_alike(env): + t = env.from_string("{% for x in value %}{{ x }}{% endfor %}") + result = t.render(value=[1]) + assert isinstance(result, int) + assert result == 1 + + +@pytest.mark.parametrize( + ("source", "expect"), + ( + ("{{ value }}", True), + ("{{ value }}", False), + ("{{ 1 == 1 }}", True), + ("{{ 2 + 2 == 5 }}", False), + ("{{ None is none }}", True), + ("{{ '' == None }}", False), + ), +) +def test_booleans(env, source, expect): + t = env.from_string(source) + result = t.render(value=expect) + assert isinstance(result, bool) + assert result is expect + + +def test_variable_dunder(env): + t = env.from_string("{{ x.__class__ }}") + result = t.render(x=True) + assert isinstance(result, type) + + +def test_constant_dunder(env): + t = env.from_string("{{ true.__class__ }}") + result = t.render() + assert isinstance(result, type) + + +def test_constant_dunder_to_string(env): + t = env.from_string("{{ true.__class__|string }}") + result = t.render() + assert not isinstance(result, type) + assert result in {"<type 'bool'>", "<class 'bool'>"} + + +def test_string_literal_var(env): + t = env.from_string("[{{ 'all' }}]") + result = t.render() + assert isinstance(result, str) + assert result == "[all]" + + +def test_string_top_level(env): + t = env.from_string("'Jinja'") + result = t.render() + assert result == "Jinja" + + +def test_tuple_of_variable_strings(env): + t = env.from_string("'{{ a }}', 'data', '{{ b }}', b'{{ c }}'") + result = t.render(a=1, b=2, c="bytes") + assert isinstance(result, tuple) + assert result == ("1", "data", "2", b"bytes") + + +def test_concat_strings_with_quotes(env): + t = env.from_string("--host='{{ host }}' --user \"{{ user }}\"") + result = t.render(host="localhost", user="Jinja") + assert result == "--host='localhost' --user \"Jinja\"" + + +def test_no_intermediate_eval(env): + t = env.from_string("0.000{{ a }}") + result = t.render(a=7) + assert isinstance(result, float) + # If intermediate eval happened, 0.000 would render 0.0, then 7 + # would be appended, resulting in 0.07. + assert math.isclose(result, 0.0007) + + +def test_spontaneous_env(): + t = NativeTemplate("{{ true }}") + assert isinstance(t.environment, NativeEnvironment) def test_leading_spaces(env): diff --git a/contrib/python/Jinja2/py3/tests/test_regression.py b/contrib/python/Jinja2/py3/tests/test_regression.py index 3eeff98cc0..7e2336978e 100644 --- a/contrib/python/Jinja2/py3/tests/test_regression.py +++ b/contrib/python/Jinja2/py3/tests/test_regression.py @@ -1,127 +1,127 @@ import pytest -from jinja2 import DictLoader -from jinja2 import Environment -from jinja2 import PrefixLoader -from jinja2 import Template -from jinja2 import TemplateAssertionError -from jinja2 import TemplateNotFound -from jinja2 import TemplateSyntaxError -from jinja2.utils import pass_context +from jinja2 import DictLoader +from jinja2 import Environment +from jinja2 import PrefixLoader +from jinja2 import Template +from jinja2 import TemplateAssertionError +from jinja2 import TemplateNotFound +from jinja2 import TemplateSyntaxError +from jinja2.utils import pass_context -class TestCorner: +class TestCorner: def test_assigned_scoping(self, env): - t = env.from_string( - """ + t = env.from_string( + """ {%- for item in (1, 2, 3, 4) -%} [{{ item }}] {%- endfor %} {{- item -}} - """ - ) - assert t.render(item=42) == "[1][2][3][4]42" + """ + ) + assert t.render(item=42) == "[1][2][3][4]42" - t = env.from_string( - """ + t = env.from_string( + """ {%- for item in (1, 2, 3, 4) -%} [{{ item }}] {%- endfor %} {%- set item = 42 %} {{- item -}} - """ - ) - assert t.render() == "[1][2][3][4]42" + """ + ) + assert t.render() == "[1][2][3][4]42" - t = env.from_string( - """ + t = env.from_string( + """ {%- set item = 42 %} {%- for item in (1, 2, 3, 4) -%} [{{ item }}] {%- endfor %} {{- item -}} - """ - ) - assert t.render() == "[1][2][3][4]42" + """ + ) + assert t.render() == "[1][2][3][4]42" def test_closure_scoping(self, env): - t = env.from_string( - """ + t = env.from_string( + """ {%- set wrapper = "<FOO>" %} {%- for item in (1, 2, 3, 4) %} {%- macro wrapper() %}[{{ item }}]{% endmacro %} {{- wrapper() }} {%- endfor %} {{- wrapper -}} - """ - ) - assert t.render() == "[1][2][3][4]<FOO>" + """ + ) + assert t.render() == "[1][2][3][4]<FOO>" - t = env.from_string( - """ + t = env.from_string( + """ {%- for item in (1, 2, 3, 4) %} {%- macro wrapper() %}[{{ item }}]{% endmacro %} {{- wrapper() }} {%- endfor %} {%- set wrapper = "<FOO>" %} {{- wrapper -}} - """ - ) - assert t.render() == "[1][2][3][4]<FOO>" + """ + ) + assert t.render() == "[1][2][3][4]<FOO>" - t = env.from_string( - """ + t = env.from_string( + """ {%- for item in (1, 2, 3, 4) %} {%- macro wrapper() %}[{{ item }}]{% endmacro %} {{- wrapper() }} {%- endfor %} {{- wrapper -}} - """ - ) - assert t.render(wrapper=23) == "[1][2][3][4]23" + """ + ) + assert t.render(wrapper=23) == "[1][2][3][4]23" -class TestBug: +class TestBug: def test_keyword_folding(self, env): env = Environment() - env.filters["testing"] = lambda value, some: value + some - assert ( - env.from_string("{{ 'test'|testing(some='stuff') }}").render() - == "teststuff" - ) + env.filters["testing"] = lambda value, some: value + some + assert ( + env.from_string("{{ 'test'|testing(some='stuff') }}").render() + == "teststuff" + ) def test_extends_output_bugs(self, env): - env = Environment( - loader=DictLoader({"parent.html": "(({% block title %}{% endblock %}))"}) - ) + env = Environment( + loader=DictLoader({"parent.html": "(({% block title %}{% endblock %}))"}) + ) t = env.from_string( '{% if expr %}{% extends "parent.html" %}{% endif %}' - "[[{% block title %}title{% endblock %}]]" - "{% for item in [1, 2, 3] %}({{ item }}){% endfor %}" + "[[{% block title %}title{% endblock %}]]" + "{% for item in [1, 2, 3] %}({{ item }}){% endfor %}" ) - assert t.render(expr=False) == "[[title]](1)(2)(3)" - assert t.render(expr=True) == "((title))" + assert t.render(expr=False) == "[[title]](1)(2)(3)" + assert t.render(expr=True) == "((title))" def test_urlize_filter_escaping(self, env): tmpl = env.from_string('{{ "http://www.example.org/<foo"|urlize }}') - assert ( - tmpl.render() == '<a href="http://www.example.org/<foo" rel="noopener">' - "http://www.example.org/<foo</a>" - ) - - def test_urlize_filter_closing_punctuation(self, env): - tmpl = env.from_string( - '{{ "(see http://www.example.org/?page=subj_<desc.h>)"|urlize }}' - ) - assert tmpl.render() == ( - '(see <a href="http://www.example.org/?page=subj_<desc.h>" ' - 'rel="noopener">http://www.example.org/?page=subj_<desc.h></a>)' - ) - + assert ( + tmpl.render() == '<a href="http://www.example.org/<foo" rel="noopener">' + "http://www.example.org/<foo</a>" + ) + + def test_urlize_filter_closing_punctuation(self, env): + tmpl = env.from_string( + '{{ "(see http://www.example.org/?page=subj_<desc.h>)"|urlize }}' + ) + assert tmpl.render() == ( + '(see <a href="http://www.example.org/?page=subj_<desc.h>" ' + 'rel="noopener">http://www.example.org/?page=subj_<desc.h></a>)' + ) + def test_loop_call_loop(self, env): - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {% macro test() %} {{ caller() }} @@ -135,35 +135,35 @@ class TestBug: {% endcall %} {% endfor %} - """ - ) + """ + ) - assert tmpl.render().split() == [str(x) for x in range(1, 11)] * 5 + assert tmpl.render().split() == [str(x) for x in range(1, 11)] * 5 def test_weird_inline_comment(self, env): - env = Environment(line_statement_prefix="%") - pytest.raises( - TemplateSyntaxError, - env.from_string, - "% for item in seq {# missing #}\n...% endfor", - ) + env = Environment(line_statement_prefix="%") + pytest.raises( + TemplateSyntaxError, + env.from_string, + "% for item in seq {# missing #}\n...% endfor", + ) def test_old_macro_loop_scoping_bug(self, env): - tmpl = env.from_string( - "{% for i in (1, 2) %}{{ i }}{% endfor %}" - "{% macro i() %}3{% endmacro %}{{ i() }}" - ) - assert tmpl.render() == "123" + tmpl = env.from_string( + "{% for i in (1, 2) %}{{ i }}{% endfor %}" + "{% macro i() %}3{% endmacro %}{{ i() }}" + ) + assert tmpl.render() == "123" def test_partial_conditional_assignments(self, env): - tmpl = env.from_string("{% if b %}{% set a = 42 %}{% endif %}{{ a }}") - assert tmpl.render(a=23) == "23" - assert tmpl.render(b=True) == "42" + tmpl = env.from_string("{% if b %}{% set a = 42 %}{% endif %}{{ a }}") + assert tmpl.render(a=23) == "23" + assert tmpl.render(b=True) == "42" def test_stacked_locals_scoping_bug(self, env): - env = Environment(line_statement_prefix="#") - t = env.from_string( - """\ + env = Environment(line_statement_prefix="#") + t = env.from_string( + """\ # for j in [1, 2]: # set x = 1 # for i in [1, 2]: @@ -182,13 +182,13 @@ class TestBug: # else # print 'D' # endif - """ - ) - assert t.render(a=0, b=False, c=42, d=42.0) == "1111C" + """ + ) + assert t.render(a=0, b=False, c=42, d=42.0) == "1111C" def test_stacked_locals_scoping_bug_twoframe(self, env): - t = Template( - """ + t = Template( + """ {% set x = 1 %} {% for item in foo %} {% if item == 1 %} @@ -196,14 +196,14 @@ class TestBug: {% endif %} {% endfor %} {{ x }} - """ - ) + """ + ) rv = t.render(foo=[1]).strip() - assert rv == "1" + assert rv == "1" def test_call_with_args(self, env): - t = Template( - """{% macro dump_users(users) -%} + t = Template( + """{% macro dump_users(users) -%} <ul> {%- for user in users -%} <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li> @@ -218,159 +218,159 @@ class TestBug: <dl>Description</dl> <dd>{{ user.description }}</dd> </dl> - {% endcall %}""" - ) - - assert [ - x.strip() - for x in t.render( - list_of_user=[ - { - "username": "apo", - "realname": "something else", - "description": "test", - } - ] - ).splitlines() - ] == [ - "<ul><li><p>apo</p><dl>", - "<dl>Realname</dl>", - "<dd>something else</dd>", - "<dl>Description</dl>", - "<dd>test</dd>", - "</dl>", - "</li></ul>", + {% endcall %}""" + ) + + assert [ + x.strip() + for x in t.render( + list_of_user=[ + { + "username": "apo", + "realname": "something else", + "description": "test", + } + ] + ).splitlines() + ] == [ + "<ul><li><p>apo</p><dl>", + "<dl>Realname</dl>", + "<dd>something else</dd>", + "<dl>Description</dl>", + "<dd>test</dd>", + "</dl>", + "</li></ul>", ] def test_empty_if_condition_fails(self, env): - pytest.raises(TemplateSyntaxError, Template, "{% if %}....{% endif %}") - pytest.raises( - TemplateSyntaxError, Template, "{% if foo %}...{% elif %}...{% endif %}" - ) - pytest.raises(TemplateSyntaxError, Template, "{% for x in %}..{% endfor %}") - - def test_recursive_loop_compile(self, env): - Template( - """ - {% for p in foo recursive%} + pytest.raises(TemplateSyntaxError, Template, "{% if %}....{% endif %}") + pytest.raises( + TemplateSyntaxError, Template, "{% if foo %}...{% elif %}...{% endif %}" + ) + pytest.raises(TemplateSyntaxError, Template, "{% for x in %}..{% endfor %}") + + def test_recursive_loop_compile(self, env): + Template( + """ + {% for p in foo recursive%} {{p.bar}} - {% for f in p.fields recursive%} - {{f.baz}} - {{p.bar}} - {% if f.rec %} - {{ loop(f.sub) }} - {% endif %} - {% endfor %} + {% for f in p.fields recursive%} + {{f.baz}} + {{p.bar}} + {% if f.rec %} + {{ loop(f.sub) }} + {% endif %} + {% endfor %} {% endfor %} - """ - ) - Template( - """ - {% for p in foo%} + """ + ) + Template( + """ + {% for p in foo%} {{p.bar}} - {% for f in p.fields recursive%} - {{f.baz}} - {{p.bar}} - {% if f.rec %} - {{ loop(f.sub) }} - {% endif %} - {% endfor %} + {% for f in p.fields recursive%} + {{f.baz}} + {{p.bar}} + {% if f.rec %} + {{ loop(f.sub) }} + {% endif %} + {% endfor %} {% endfor %} - """ - ) + """ + ) def test_else_loop_bug(self, env): - t = Template( - """ + t = Template( + """ {% for x in y %} {{ loop.index0 }} {% else %} {% for i in range(3) %}{{ i }}{% endfor %} {% endfor %} - """ - ) - assert t.render(y=[]).strip() == "012" + """ + ) + assert t.render(y=[]).strip() == "012" def test_correct_prefix_loader_name(self, env): - env = Environment(loader=PrefixLoader({"foo": DictLoader({})})) - with pytest.raises(TemplateNotFound) as e: - env.get_template("foo/bar.html") - - assert e.value.name == "foo/bar.html" - - def test_pass_context_callable_class(self, env): - class CallableClass: - @pass_context + env = Environment(loader=PrefixLoader({"foo": DictLoader({})})) + with pytest.raises(TemplateNotFound) as e: + env.get_template("foo/bar.html") + + assert e.value.name == "foo/bar.html" + + def test_pass_context_callable_class(self, env): + class CallableClass: + @pass_context def __call__(self, ctx): - return ctx.resolve("hello") + return ctx.resolve("hello") tpl = Template("""{{ callableclass() }}""") - output = tpl.render(callableclass=CallableClass(), hello="TEST") - expected = "TEST" + output = tpl.render(callableclass=CallableClass(), hello="TEST") + expected = "TEST" assert output == expected def test_block_set_with_extends(self): - env = Environment( - loader=DictLoader({"main": "{% block body %}[{{ x }}]{% endblock %}"}) - ) + env = Environment( + loader=DictLoader({"main": "{% block body %}[{{ x }}]{% endblock %}"}) + ) t = env.from_string('{% extends "main" %}{% set x %}42{% endset %}') - assert t.render() == "[42]" + assert t.render() == "[42]" def test_nested_for_else(self, env): - tmpl = env.from_string( - "{% for x in y %}{{ loop.index0 }}{% else %}" - "{% for i in range(3) %}{{ i }}{% endfor %}" - "{% endfor %}" - ) - assert tmpl.render() == "012" + tmpl = env.from_string( + "{% for x in y %}{{ loop.index0 }}{% else %}" + "{% for i in range(3) %}{{ i }}{% endfor %}" + "{% endfor %}" + ) + assert tmpl.render() == "012" def test_macro_var_bug(self, env): - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {% set i = 1 %} {% macro test() %} {% for i in range(0, 10) %}{{ i }}{% endfor %} {% endmacro %}{{ test() }} - """ - ) - assert tmpl.render().strip() == "0123456789" + """ + ) + assert tmpl.render().strip() == "0123456789" def test_macro_var_bug_advanced(self, env): - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {% macro outer() %} {% set i = 1 %} {% macro test() %} {% for i in range(0, 10) %}{{ i }}{% endfor %} {% endmacro %}{{ test() }} {% endmacro %}{{ outer() }} - """ - ) - assert tmpl.render().strip() == "0123456789" + """ + ) + assert tmpl.render().strip() == "0123456789" def test_callable_defaults(self): env = Environment() - env.globals["get_int"] = lambda: 42 - t = env.from_string( - """ + env.globals["get_int"] = lambda: 42 + t = env.from_string( + """ {% macro test(a, b, c=get_int()) -%} {{ a + b + c }} {%- endmacro %} {{ test(1, 2) }}|{{ test(1, 2, 3) }} - """ - ) - assert t.render().strip() == "45|6" + """ + ) + assert t.render().strip() == "45|6" def test_macro_escaping(self): - env = Environment(autoescape=lambda x: False) + env = Environment(autoescape=lambda x: False) template = "{% macro m() %}<html>{% endmacro %}" template += "{% autoescape true %}{{ m() }}{% endautoescape %}" assert env.from_string(template).render() def test_macro_scoping(self, env): - tmpl = env.from_string( - """ + tmpl = env.from_string( + """ {% set n=[1,2,3,4,5] %} {% for n in [[1,2,3], [3,4,5], [5,6,7]] %} @@ -382,56 +382,56 @@ class TestBug: {{ x(n) }} {% endfor %} - """ - ) - assert list(map(int, tmpl.render().split())) == [3, 2, 1, 5, 4, 3, 7, 6, 5] + """ + ) + assert list(map(int, tmpl.render().split())) == [3, 2, 1, 5, 4, 3, 7, 6, 5] def test_scopes_and_blocks(self): - env = Environment( - loader=DictLoader( - { - "a.html": """ + env = Environment( + loader=DictLoader( + { + "a.html": """ {%- set foo = 'bar' -%} {% include 'x.html' -%} - """, - "b.html": """ + """, + "b.html": """ {%- set foo = 'bar' -%} {% block test %}{% include 'x.html' %}{% endblock -%} - """, - "c.html": """ + """, + "c.html": """ {%- set foo = 'bar' -%} {% block test %}{% set foo = foo %}{% include 'x.html' %}{% endblock -%} - """, - "x.html": """{{ foo }}|{{ test }}""", - } - ) - ) + """, + "x.html": """{{ foo }}|{{ test }}""", + } + ) + ) - a = env.get_template("a.html") - b = env.get_template("b.html") - c = env.get_template("c.html") + a = env.get_template("a.html") + b = env.get_template("b.html") + c = env.get_template("c.html") - assert a.render(test="x").strip() == "bar|x" - assert b.render(test="x").strip() == "bar|x" - assert c.render(test="x").strip() == "bar|x" + assert a.render(test="x").strip() == "bar|x" + assert b.render(test="x").strip() == "bar|x" + assert c.render(test="x").strip() == "bar|x" def test_scopes_and_include(self): - env = Environment( - loader=DictLoader( - { - "include.html": "{{ var }}", - "base.html": '{% include "include.html" %}', - "child.html": '{% extends "base.html" %}{% set var = 42 %}', - } - ) - ) - t = env.get_template("child.html") - assert t.render() == "42" + env = Environment( + loader=DictLoader( + { + "include.html": "{{ var }}", + "base.html": '{% include "include.html" %}', + "child.html": '{% extends "base.html" %}{% set var = 42 %}', + } + ) + ) + t = env.get_template("child.html") + assert t.render() == "42" def test_caller_scoping(self, env): - t = env.from_string( - """ + t = env.from_string( + """ {% macro detail(icon, value) -%} {% if value -%} <p><span class="fa fa-fw fa-{{ icon }}"></span> @@ -449,67 +449,67 @@ class TestBug: <a href="{{ href }}">{{ value }}</a> {%- endcall %} {%- endmacro %} - """ - ) + """ + ) - assert t.module.link_detail("circle", "Index", "/") == ( - '<p><span class="fa fa-fw fa-circle"></span><a href="/">Index</a></p>' - ) + assert t.module.link_detail("circle", "Index", "/") == ( + '<p><span class="fa fa-fw fa-circle"></span><a href="/">Index</a></p>' + ) def test_variable_reuse(self, env): - t = env.from_string("{% for x in x.y %}{{ x }}{% endfor %}") - assert t.render(x={"y": [0, 1, 2]}) == "012" + t = env.from_string("{% for x in x.y %}{{ x }}{% endfor %}") + assert t.render(x={"y": [0, 1, 2]}) == "012" - t = env.from_string("{% for x in x.y %}{{ loop.index0 }}|{{ x }}{% endfor %}") - assert t.render(x={"y": [0, 1, 2]}) == "0|01|12|2" + t = env.from_string("{% for x in x.y %}{{ loop.index0 }}|{{ x }}{% endfor %}") + assert t.render(x={"y": [0, 1, 2]}) == "0|01|12|2" - t = env.from_string("{% for x in x.y recursive %}{{ x }}{% endfor %}") - assert t.render(x={"y": [0, 1, 2]}) == "012" + t = env.from_string("{% for x in x.y recursive %}{{ x }}{% endfor %}") + assert t.render(x={"y": [0, 1, 2]}) == "012" def test_double_caller(self, env): - t = env.from_string( - "{% macro x(caller=none) %}[{% if caller %}" - "{{ caller() }}{% endif %}]{% endmacro %}" - "{{ x() }}{% call x() %}aha!{% endcall %}" - ) - assert t.render() == "[][aha!]" + t = env.from_string( + "{% macro x(caller=none) %}[{% if caller %}" + "{{ caller() }}{% endif %}]{% endmacro %}" + "{{ x() }}{% call x() %}aha!{% endcall %}" + ) + assert t.render() == "[][aha!]" def test_double_caller_no_default(self, env): with pytest.raises(TemplateAssertionError) as exc_info: - env.from_string( - "{% macro x(caller) %}[{% if caller %}" - "{{ caller() }}{% endif %}]{% endmacro %}" - ) - assert exc_info.match( - r'"caller" argument must be omitted or ' r"be given a default" - ) - - t = env.from_string( - "{% macro x(caller=none) %}[{% if caller %}" - "{{ caller() }}{% endif %}]{% endmacro %}" - ) + env.from_string( + "{% macro x(caller) %}[{% if caller %}" + "{{ caller() }}{% endif %}]{% endmacro %}" + ) + assert exc_info.match( + r'"caller" argument must be omitted or ' r"be given a default" + ) + + t = env.from_string( + "{% macro x(caller=none) %}[{% if caller %}" + "{{ caller() }}{% endif %}]{% endmacro %}" + ) with pytest.raises(TypeError) as exc_info: t.module.x(None, caller=lambda: 42) - assert exc_info.match( - r"\'x\' was invoked with two values for the " r"special caller argument" - ) + assert exc_info.match( + r"\'x\' was invoked with two values for the " r"special caller argument" + ) def test_macro_blocks(self, env): - t = env.from_string( - "{% macro x() %}{% block foo %}x{% endblock %}{% endmacro %}{{ x() }}" - ) - assert t.render() == "x" + t = env.from_string( + "{% macro x() %}{% block foo %}x{% endblock %}{% endmacro %}{{ x() }}" + ) + assert t.render() == "x" def test_scoped_block(self, env): - t = env.from_string( - "{% set x = 1 %}{% with x = 2 %}{% block y scoped %}" - "{{ x }}{% endblock %}{% endwith %}" - ) - assert t.render() == "2" + t = env.from_string( + "{% set x = 1 %}{% with x = 2 %}{% block y scoped %}" + "{{ x }}{% endblock %}{% endwith %}" + ) + assert t.render() == "2" def test_recursive_loop_filter(self, env): - t = env.from_string( - """ + t = env.from_string( + """ <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> {%- for page in [site.root] if page.url != this recursive %} @@ -517,63 +517,63 @@ class TestBug: {{- loop(page.children) }} {%- endfor %} </urlset> - """ - ) - sm = t.render( - this="/foo", - site={"root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"}]}}, - ) + """ + ) + sm = t.render( + this="/foo", + site={"root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"}]}}, + ) lines = [x.strip() for x in sm.splitlines() if x.strip()] assert lines == [ '<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">', - "<url><loc>/</loc></url>", - "<url><loc>/bar</loc></url>", - "</urlset>", + "<url><loc>/</loc></url>", + "<url><loc>/bar</loc></url>", + "</urlset>", ] def test_empty_if(self, env): - t = env.from_string("{% if foo %}{% else %}42{% endif %}") - assert t.render(foo=False) == "42" - - def test_subproperty_if(self, env): - t = env.from_string( - "{% if object1.subproperty1 is eq object2.subproperty2 %}42{% endif %}" - ) - assert ( - t.render( - object1={"subproperty1": "value"}, object2={"subproperty2": "value"} - ) - == "42" - ) - + t = env.from_string("{% if foo %}{% else %}42{% endif %}") + assert t.render(foo=False) == "42" + + def test_subproperty_if(self, env): + t = env.from_string( + "{% if object1.subproperty1 is eq object2.subproperty2 %}42{% endif %}" + ) + assert ( + t.render( + object1={"subproperty1": "value"}, object2={"subproperty2": "value"} + ) + == "42" + ) + def test_set_and_include(self): - env = Environment( - loader=DictLoader( - { - "inc": "bar", - "main": '{% set foo = "foo" %}{{ foo }}{% include "inc" %}', - } - ) - ) - assert env.get_template("main").render() == "foobar" + env = Environment( + loader=DictLoader( + { + "inc": "bar", + "main": '{% set foo = "foo" %}{{ foo }}{% include "inc" %}', + } + ) + ) + assert env.get_template("main").render() == "foobar" def test_loop_include(self): - env = Environment( - loader=DictLoader( - { - "inc": "{{ i }}", - "main": '{% for i in [1, 2, 3] %}{% include "inc" %}{% endfor %}', - } - ) - ) - assert env.get_template("main").render() == "123" + env = Environment( + loader=DictLoader( + { + "inc": "{{ i }}", + "main": '{% for i in [1, 2, 3] %}{% include "inc" %}{% endfor %}', + } + ) + ) + assert env.get_template("main").render() == "123" def test_grouper_repr(self): from jinja2.filters import _GroupTuple - - t = _GroupTuple("foo", [1, 2]) - assert t.grouper == "foo" + + t = _GroupTuple("foo", [1, 2]) + assert t.grouper == "foo" assert t.list == [1, 2] assert repr(t) == "('foo', [1, 2])" assert str(t) == "('foo', [1, 2])" @@ -587,175 +587,175 @@ class TestBug: class MyEnvironment(Environment): context_class = MyContext - loader = DictLoader({"base": "{{ foobar }}", "test": '{% extends "base" %}'}) + loader = DictLoader({"base": "{{ foobar }}", "test": '{% extends "base" %}'}) env = MyEnvironment(loader=loader) - assert env.get_template("test").render(foobar="test") == "test" + assert env.get_template("test").render(foobar="test") == "test" def test_legacy_custom_context(self, env): - from jinja2.runtime import Context, missing + from jinja2.runtime import Context, missing + + with pytest.deprecated_call(): - with pytest.deprecated_call(): + class MyContext(Context): + def resolve(self, name): + if name == "foo": + return 42 + return super().resolve(name) - class MyContext(Context): - def resolve(self, name): - if name == "foo": - return 42 - return super().resolve(name) - - x = MyContext(env, parent={"bar": 23}, name="foo", blocks={}) + x = MyContext(env, parent={"bar": 23}, name="foo", blocks={}) assert x._legacy_resolve_mode - assert x.resolve_or_missing("foo") == 42 - assert x.resolve_or_missing("bar") == 23 - assert x.resolve_or_missing("baz") is missing + assert x.resolve_or_missing("foo") == 42 + assert x.resolve_or_missing("bar") == 23 + assert x.resolve_or_missing("baz") is missing def test_recursive_loop_bug(self, env): - tmpl = env.from_string( - "{%- for value in values recursive %}1{% else %}0{% endfor -%}" - ) - assert tmpl.render(values=[]) == "0" - - def test_markup_and_chainable_undefined(self): - from markupsafe import Markup - from jinja2.runtime import ChainableUndefined - - assert str(Markup(ChainableUndefined())) == "" - - def test_scoped_block_loop_vars(self, env): - tmpl = env.from_string( - """\ -Start -{% for i in ["foo", "bar"] -%} -{% block body scoped -%} -{{ loop.index }}) {{ i }}{% if loop.last %} last{% endif -%} -{%- endblock %} -{% endfor -%} -End""" - ) - assert tmpl.render() == "Start\n1) foo\n2) bar last\nEnd" - - def test_pass_context_loop_vars(self, env): - @pass_context - def test(ctx): - return f"{ctx['i']}{ctx['j']}" - - tmpl = env.from_string( - """\ -{% set i = 42 %} -{%- for idx in range(2) -%} -{{ i }}{{ j }} -{% set i = idx -%} -{%- set j = loop.index -%} -{{ test() }} -{{ i }}{{ j }} -{% endfor -%} -{{ i }}{{ j }}""" - ) - tmpl.globals["test"] = test - assert tmpl.render() == "42\n01\n01\n42\n12\n12\n42" - - def test_pass_context_scoped_loop_vars(self, env): - @pass_context - def test(ctx): - return f"{ctx['i']}" - - tmpl = env.from_string( - """\ -{% set i = 42 %} -{%- for idx in range(2) -%} -{{ i }} -{%- set i = loop.index0 -%} -{% block body scoped %} -{{ test() }} -{% endblock -%} -{% endfor -%} -{{ i }}""" - ) - tmpl.globals["test"] = test - assert tmpl.render() == "42\n0\n42\n1\n42" - - def test_pass_context_in_blocks(self, env): - @pass_context - def test(ctx): - return f"{ctx['i']}" - - tmpl = env.from_string( - """\ -{%- set i = 42 -%} -{{ i }} -{% block body -%} -{% set i = 24 -%} -{{ test() }} -{% endblock -%} -{{ i }}""" - ) - tmpl.globals["test"] = test - assert tmpl.render() == "42\n24\n42" - - def test_pass_context_block_and_loop(self, env): - @pass_context - def test(ctx): - return f"{ctx['i']}" - - tmpl = env.from_string( - """\ -{%- set i = 42 -%} -{% for idx in range(2) -%} -{{ test() }} -{%- set i = idx -%} -{% block body scoped %} -{{ test() }} -{% set i = 24 -%} -{{ test() }} -{% endblock -%} -{{ test() }} -{% endfor -%} -{{ test() }}""" - ) - tmpl.globals["test"] = test - - # values set within a block or loop should not - # show up outside of it - assert tmpl.render() == "42\n0\n24\n0\n42\n1\n24\n1\n42" - - @pytest.mark.parametrize("op", ["extends", "include"]) - def test_cached_extends(self, op): - env = Environment( - loader=DictLoader( - {"base": "{{ x }} {{ y }}", "main": f"{{% {op} 'base' %}}"} - ) - ) - env.globals["x"] = "x" - env.globals["y"] = "y" - - # template globals overlay env globals - tmpl = env.get_template("main", globals={"x": "bar"}) - assert tmpl.render() == "bar y" - - # base was loaded indirectly, it just has env globals - tmpl = env.get_template("base") - assert tmpl.render() == "x y" - - # set template globals for base, no longer uses env globals - tmpl = env.get_template("base", globals={"x": 42}) - assert tmpl.render() == "42 y" - - # templates are cached, they keep template globals set earlier - tmpl = env.get_template("main") - assert tmpl.render() == "bar y" - - tmpl = env.get_template("base") - assert tmpl.render() == "42 y" - - def test_nested_loop_scoping(self, env): - tmpl = env.from_string( - "{% set output %}{% for x in [1,2,3] %}hello{% endfor %}" - "{% endset %}{{ output }}" - ) - assert tmpl.render() == "hellohellohello" - - -@pytest.mark.parametrize("unicode_char", ["\N{FORM FEED}", "\x85"]) -def test_unicode_whitespace(env, unicode_char): - content = "Lorem ipsum\n" + unicode_char + "\nMore text" - tmpl = env.from_string(content) - assert tmpl.render() == content + tmpl = env.from_string( + "{%- for value in values recursive %}1{% else %}0{% endfor -%}" + ) + assert tmpl.render(values=[]) == "0" + + def test_markup_and_chainable_undefined(self): + from markupsafe import Markup + from jinja2.runtime import ChainableUndefined + + assert str(Markup(ChainableUndefined())) == "" + + def test_scoped_block_loop_vars(self, env): + tmpl = env.from_string( + """\ +Start +{% for i in ["foo", "bar"] -%} +{% block body scoped -%} +{{ loop.index }}) {{ i }}{% if loop.last %} last{% endif -%} +{%- endblock %} +{% endfor -%} +End""" + ) + assert tmpl.render() == "Start\n1) foo\n2) bar last\nEnd" + + def test_pass_context_loop_vars(self, env): + @pass_context + def test(ctx): + return f"{ctx['i']}{ctx['j']}" + + tmpl = env.from_string( + """\ +{% set i = 42 %} +{%- for idx in range(2) -%} +{{ i }}{{ j }} +{% set i = idx -%} +{%- set j = loop.index -%} +{{ test() }} +{{ i }}{{ j }} +{% endfor -%} +{{ i }}{{ j }}""" + ) + tmpl.globals["test"] = test + assert tmpl.render() == "42\n01\n01\n42\n12\n12\n42" + + def test_pass_context_scoped_loop_vars(self, env): + @pass_context + def test(ctx): + return f"{ctx['i']}" + + tmpl = env.from_string( + """\ +{% set i = 42 %} +{%- for idx in range(2) -%} +{{ i }} +{%- set i = loop.index0 -%} +{% block body scoped %} +{{ test() }} +{% endblock -%} +{% endfor -%} +{{ i }}""" + ) + tmpl.globals["test"] = test + assert tmpl.render() == "42\n0\n42\n1\n42" + + def test_pass_context_in_blocks(self, env): + @pass_context + def test(ctx): + return f"{ctx['i']}" + + tmpl = env.from_string( + """\ +{%- set i = 42 -%} +{{ i }} +{% block body -%} +{% set i = 24 -%} +{{ test() }} +{% endblock -%} +{{ i }}""" + ) + tmpl.globals["test"] = test + assert tmpl.render() == "42\n24\n42" + + def test_pass_context_block_and_loop(self, env): + @pass_context + def test(ctx): + return f"{ctx['i']}" + + tmpl = env.from_string( + """\ +{%- set i = 42 -%} +{% for idx in range(2) -%} +{{ test() }} +{%- set i = idx -%} +{% block body scoped %} +{{ test() }} +{% set i = 24 -%} +{{ test() }} +{% endblock -%} +{{ test() }} +{% endfor -%} +{{ test() }}""" + ) + tmpl.globals["test"] = test + + # values set within a block or loop should not + # show up outside of it + assert tmpl.render() == "42\n0\n24\n0\n42\n1\n24\n1\n42" + + @pytest.mark.parametrize("op", ["extends", "include"]) + def test_cached_extends(self, op): + env = Environment( + loader=DictLoader( + {"base": "{{ x }} {{ y }}", "main": f"{{% {op} 'base' %}}"} + ) + ) + env.globals["x"] = "x" + env.globals["y"] = "y" + + # template globals overlay env globals + tmpl = env.get_template("main", globals={"x": "bar"}) + assert tmpl.render() == "bar y" + + # base was loaded indirectly, it just has env globals + tmpl = env.get_template("base") + assert tmpl.render() == "x y" + + # set template globals for base, no longer uses env globals + tmpl = env.get_template("base", globals={"x": 42}) + assert tmpl.render() == "42 y" + + # templates are cached, they keep template globals set earlier + tmpl = env.get_template("main") + assert tmpl.render() == "bar y" + + tmpl = env.get_template("base") + assert tmpl.render() == "42 y" + + def test_nested_loop_scoping(self, env): + tmpl = env.from_string( + "{% set output %}{% for x in [1,2,3] %}hello{% endfor %}" + "{% endset %}{{ output }}" + ) + assert tmpl.render() == "hellohellohello" + + +@pytest.mark.parametrize("unicode_char", ["\N{FORM FEED}", "\x85"]) +def test_unicode_whitespace(env, unicode_char): + content = "Lorem ipsum\n" + unicode_char + "\nMore text" + tmpl = env.from_string(content) + assert tmpl.render() == content diff --git a/contrib/python/Jinja2/py3/tests/test_runtime.py b/contrib/python/Jinja2/py3/tests/test_runtime.py index 42a94bc1d2..1978c64104 100644 --- a/contrib/python/Jinja2/py3/tests/test_runtime.py +++ b/contrib/python/Jinja2/py3/tests/test_runtime.py @@ -1,75 +1,75 @@ -import itertools - -from jinja2 import Template -from jinja2.runtime import LoopContext - -TEST_IDX_TEMPLATE_STR_1 = ( - "[{% for i in lst|reverse %}(len={{ loop.length }}," - " revindex={{ loop.revindex }}, index={{ loop.index }}, val={{ i }}){% endfor %}]" -) -TEST_IDX0_TEMPLATE_STR_1 = ( - "[{% for i in lst|reverse %}(len={{ loop.length }}," - " revindex0={{ loop.revindex0 }}, index0={{ loop.index0 }}, val={{ i }})" - "{% endfor %}]" -) - - -def test_loop_idx(): - t = Template(TEST_IDX_TEMPLATE_STR_1) - lst = [10] - excepted_render = "[(len=1, revindex=1, index=1, val=10)]" - assert excepted_render == t.render(lst=lst) - - -def test_loop_idx0(): - t = Template(TEST_IDX0_TEMPLATE_STR_1) - lst = [10] - excepted_render = "[(len=1, revindex0=0, index0=0, val=10)]" - assert excepted_render == t.render(lst=lst) - - -def test_loopcontext0(): - in_lst = [] - lc = LoopContext(reversed(in_lst), None) - assert lc.length == len(in_lst) - - -def test_loopcontext1(): - in_lst = [10] - lc = LoopContext(reversed(in_lst), None) - assert lc.length == len(in_lst) - - -def test_loopcontext2(): - in_lst = [10, 11] - lc = LoopContext(reversed(in_lst), None) - assert lc.length == len(in_lst) - - -def test_iterator_not_advanced_early(): - t = Template("{% for _, g in gs %}{{ loop.index }} {{ g|list }}\n{% endfor %}") - out = t.render( - gs=itertools.groupby([(1, "a"), (1, "b"), (2, "c"), (3, "d")], lambda x: x[0]) - ) - # groupby groups depend on the current position of the iterator. If - # it was advanced early, the lists would appear empty. - assert out == "1 [(1, 'a'), (1, 'b')]\n2 [(2, 'c')]\n3 [(3, 'd')]\n" - - -def test_mock_not_pass_arg_marker(): - """If a callable class has a ``__getattr__`` that returns True-like - values for arbitrary attrs, it should not be incorrectly identified - as a ``pass_context`` function. - """ - - class Calc: - def __getattr__(self, item): - return object() - - def __call__(self, *args, **kwargs): - return len(args) + len(kwargs) - - t = Template("{{ calc() }}") - out = t.render(calc=Calc()) - # Would be "1" if context argument was passed. - assert out == "0" +import itertools + +from jinja2 import Template +from jinja2.runtime import LoopContext + +TEST_IDX_TEMPLATE_STR_1 = ( + "[{% for i in lst|reverse %}(len={{ loop.length }}," + " revindex={{ loop.revindex }}, index={{ loop.index }}, val={{ i }}){% endfor %}]" +) +TEST_IDX0_TEMPLATE_STR_1 = ( + "[{% for i in lst|reverse %}(len={{ loop.length }}," + " revindex0={{ loop.revindex0 }}, index0={{ loop.index0 }}, val={{ i }})" + "{% endfor %}]" +) + + +def test_loop_idx(): + t = Template(TEST_IDX_TEMPLATE_STR_1) + lst = [10] + excepted_render = "[(len=1, revindex=1, index=1, val=10)]" + assert excepted_render == t.render(lst=lst) + + +def test_loop_idx0(): + t = Template(TEST_IDX0_TEMPLATE_STR_1) + lst = [10] + excepted_render = "[(len=1, revindex0=0, index0=0, val=10)]" + assert excepted_render == t.render(lst=lst) + + +def test_loopcontext0(): + in_lst = [] + lc = LoopContext(reversed(in_lst), None) + assert lc.length == len(in_lst) + + +def test_loopcontext1(): + in_lst = [10] + lc = LoopContext(reversed(in_lst), None) + assert lc.length == len(in_lst) + + +def test_loopcontext2(): + in_lst = [10, 11] + lc = LoopContext(reversed(in_lst), None) + assert lc.length == len(in_lst) + + +def test_iterator_not_advanced_early(): + t = Template("{% for _, g in gs %}{{ loop.index }} {{ g|list }}\n{% endfor %}") + out = t.render( + gs=itertools.groupby([(1, "a"), (1, "b"), (2, "c"), (3, "d")], lambda x: x[0]) + ) + # groupby groups depend on the current position of the iterator. If + # it was advanced early, the lists would appear empty. + assert out == "1 [(1, 'a'), (1, 'b')]\n2 [(2, 'c')]\n3 [(3, 'd')]\n" + + +def test_mock_not_pass_arg_marker(): + """If a callable class has a ``__getattr__`` that returns True-like + values for arbitrary attrs, it should not be incorrectly identified + as a ``pass_context`` function. + """ + + class Calc: + def __getattr__(self, item): + return object() + + def __call__(self, *args, **kwargs): + return len(args) + len(kwargs) + + t = Template("{{ calc() }}") + out = t.render(calc=Calc()) + # Would be "1" if context argument was passed. + assert out == "0" diff --git a/contrib/python/Jinja2/py3/tests/test_security.py b/contrib/python/Jinja2/py3/tests/test_security.py index 65afb1cf6d..0e8dc5c038 100644 --- a/contrib/python/Jinja2/py3/tests/test_security.py +++ b/contrib/python/Jinja2/py3/tests/test_security.py @@ -1,17 +1,17 @@ import pytest -from markupsafe import escape +from markupsafe import escape from jinja2 import Environment -from jinja2.exceptions import SecurityError -from jinja2.exceptions import TemplateRuntimeError -from jinja2.exceptions import TemplateSyntaxError +from jinja2.exceptions import SecurityError +from jinja2.exceptions import TemplateRuntimeError +from jinja2.exceptions import TemplateSyntaxError from jinja2.nodes import EvalContext -from jinja2.sandbox import ImmutableSandboxedEnvironment -from jinja2.sandbox import SandboxedEnvironment -from jinja2.sandbox import unsafe +from jinja2.sandbox import ImmutableSandboxedEnvironment +from jinja2.sandbox import SandboxedEnvironment +from jinja2.sandbox import unsafe -class PrivateStuff: +class PrivateStuff: def bar(self): return 23 @@ -20,76 +20,76 @@ class PrivateStuff: return 42 def __repr__(self): - return "PrivateStuff" + return "PrivateStuff" -class PublicStuff: - def bar(self): - return 23 +class PublicStuff: + def bar(self): + return 23 + + def _foo(self): + return 42 - def _foo(self): - return 42 - def __repr__(self): - return "PublicStuff" + return "PublicStuff" -class TestSandbox: +class TestSandbox: def test_unsafe(self, env): env = SandboxedEnvironment() - pytest.raises( - SecurityError, env.from_string("{{ foo.foo() }}").render, foo=PrivateStuff() - ) - assert env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()) == "23" - - pytest.raises( - SecurityError, env.from_string("{{ foo._foo() }}").render, foo=PublicStuff() - ) - assert env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()) == "23" - assert env.from_string("{{ foo.__class__ }}").render(foo=42) == "" - assert env.from_string("{{ foo.func_code }}").render(foo=lambda: None) == "" + pytest.raises( + SecurityError, env.from_string("{{ foo.foo() }}").render, foo=PrivateStuff() + ) + assert env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()) == "23" + + pytest.raises( + SecurityError, env.from_string("{{ foo._foo() }}").render, foo=PublicStuff() + ) + assert env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()) == "23" + assert env.from_string("{{ foo.__class__ }}").render(foo=42) == "" + assert env.from_string("{{ foo.func_code }}").render(foo=lambda: None) == "" # security error comes from __class__ already. - pytest.raises( - SecurityError, - env.from_string("{{ foo.__class__.__subclasses__() }}").render, - foo=42, - ) + pytest.raises( + SecurityError, + env.from_string("{{ foo.__class__.__subclasses__() }}").render, + foo=42, + ) def test_immutable_environment(self, env): env = ImmutableSandboxedEnvironment() - pytest.raises(SecurityError, env.from_string("{{ [].append(23) }}").render) - pytest.raises(SecurityError, env.from_string("{{ {1:2}.clear() }}").render) + pytest.raises(SecurityError, env.from_string("{{ [].append(23) }}").render) + pytest.raises(SecurityError, env.from_string("{{ {1:2}.clear() }}").render) def test_restricted(self, env): env = SandboxedEnvironment() - pytest.raises( - TemplateSyntaxError, - env.from_string, - "{% for item.attribute in seq %}...{% endfor %}", - ) - pytest.raises( - TemplateSyntaxError, - env.from_string, - "{% for foo, bar.baz in seq %}...{% endfor %}", - ) + pytest.raises( + TemplateSyntaxError, + env.from_string, + "{% for item.attribute in seq %}...{% endfor %}", + ) + pytest.raises( + TemplateSyntaxError, + env.from_string, + "{% for foo, bar.baz in seq %}...{% endfor %}", + ) def test_template_data(self, env): env = Environment(autoescape=True) - t = env.from_string( - "{% macro say_hello(name) %}" - "<p>Hello {{ name }}!</p>{% endmacro %}" - '{{ say_hello("<blink>foo</blink>") }}' - ) - escaped_out = "<p>Hello <blink>foo</blink>!</p>" + t = env.from_string( + "{% macro say_hello(name) %}" + "<p>Hello {{ name }}!</p>{% endmacro %}" + '{{ say_hello("<blink>foo</blink>") }}' + ) + escaped_out = "<p>Hello <blink>foo</blink>!</p>" assert t.render() == escaped_out - assert str(t.module) == escaped_out + assert str(t.module) == escaped_out assert escape(t.module) == escaped_out - assert t.module.say_hello("<blink>foo</blink>") == escaped_out - assert ( - escape(t.module.say_hello(EvalContext(env), "<blink>foo</blink>")) - == escaped_out - ) - assert escape(t.module.say_hello("<blink>foo</blink>")) == escaped_out + assert t.module.say_hello("<blink>foo</blink>") == escaped_out + assert ( + escape(t.module.say_hello(EvalContext(env), "<blink>foo</blink>")) + == escaped_out + ) + assert escape(t.module.say_hello("<blink>foo</blink>")) == escaped_out def test_attr_filter(self, env): env = SandboxedEnvironment() @@ -98,76 +98,76 @@ class TestSandbox: def test_binary_operator_intercepting(self, env): def disable_op(left, right): - raise TemplateRuntimeError("that operator so does not work") - - for expr, ctx, rv in ("1 + 2", {}, "3"), ("a + 2", {"a": 2}, "4"): + raise TemplateRuntimeError("that operator so does not work") + + for expr, ctx, rv in ("1 + 2", {}, "3"), ("a + 2", {"a": 2}, "4"): env = SandboxedEnvironment() - env.binop_table["+"] = disable_op - t = env.from_string(f"{{{{ {expr} }}}}") + env.binop_table["+"] = disable_op + t = env.from_string(f"{{{{ {expr} }}}}") assert t.render(ctx) == rv - env.intercepted_binops = frozenset(["+"]) - t = env.from_string(f"{{{{ {expr} }}}}") - with pytest.raises(TemplateRuntimeError): + env.intercepted_binops = frozenset(["+"]) + t = env.from_string(f"{{{{ {expr} }}}}") + with pytest.raises(TemplateRuntimeError): t.render(ctx) def test_unary_operator_intercepting(self, env): def disable_op(arg): - raise TemplateRuntimeError("that operator so does not work") - - for expr, ctx, rv in ("-1", {}, "-1"), ("-a", {"a": 2}, "-2"): + raise TemplateRuntimeError("that operator so does not work") + + for expr, ctx, rv in ("-1", {}, "-1"), ("-a", {"a": 2}, "-2"): env = SandboxedEnvironment() - env.unop_table["-"] = disable_op - t = env.from_string(f"{{{{ {expr} }}}}") + env.unop_table["-"] = disable_op + t = env.from_string(f"{{{{ {expr} }}}}") assert t.render(ctx) == rv - env.intercepted_unops = frozenset(["-"]) - t = env.from_string(f"{{{{ {expr} }}}}") - with pytest.raises(TemplateRuntimeError): + env.intercepted_unops = frozenset(["-"]) + t = env.from_string(f"{{{{ {expr} }}}}") + with pytest.raises(TemplateRuntimeError): t.render(ctx) -class TestStringFormat: +class TestStringFormat: def test_basic_format_safety(self): env = SandboxedEnvironment() t = env.from_string('{{ "a{0.__class__}b".format(42) }}') - assert t.render() == "ab" + assert t.render() == "ab" def test_basic_format_all_okay(self): env = SandboxedEnvironment() t = env.from_string('{{ "a{0.foo}b".format({"foo": 42}) }}') - assert t.render() == "a42b" + assert t.render() == "a42b" def test_safe_format_safety(self): env = SandboxedEnvironment() t = env.from_string('{{ ("a{0.__class__}b{1}"|safe).format(42, "<foo>") }}') - assert t.render() == "ab<foo>" + assert t.render() == "ab<foo>" def test_safe_format_all_okay(self): env = SandboxedEnvironment() t = env.from_string('{{ ("a{0.foo}b{1}"|safe).format({"foo": 42}, "<foo>") }}') - assert t.render() == "a42b<foo>" - - def test_empty_braces_format(self): - env = SandboxedEnvironment() - t1 = env.from_string('{{ ("a{}b{}").format("foo", "42")}}') - t2 = env.from_string('{{ ("a{}b{}"|safe).format(42, "<foo>") }}') - assert t1.render() == "afoob42" - assert t2.render() == "a42b<foo>" - - -class TestStringFormatMap: - def test_basic_format_safety(self): - env = SandboxedEnvironment() - t = env.from_string('{{ "a{x.__class__}b".format_map({"x":42}) }}') - assert t.render() == "ab" - - def test_basic_format_all_okay(self): - env = SandboxedEnvironment() - t = env.from_string('{{ "a{x.foo}b".format_map({"x":{"foo": 42}}) }}') - assert t.render() == "a42b" - - def test_safe_format_all_okay(self): - env = SandboxedEnvironment() - t = env.from_string( - '{{ ("a{x.foo}b{y}"|safe).format_map({"x":{"foo": 42}, "y":"<foo>"}) }}' - ) - assert t.render() == "a42b<foo>" + assert t.render() == "a42b<foo>" + + def test_empty_braces_format(self): + env = SandboxedEnvironment() + t1 = env.from_string('{{ ("a{}b{}").format("foo", "42")}}') + t2 = env.from_string('{{ ("a{}b{}"|safe).format(42, "<foo>") }}') + assert t1.render() == "afoob42" + assert t2.render() == "a42b<foo>" + + +class TestStringFormatMap: + def test_basic_format_safety(self): + env = SandboxedEnvironment() + t = env.from_string('{{ "a{x.__class__}b".format_map({"x":42}) }}') + assert t.render() == "ab" + + def test_basic_format_all_okay(self): + env = SandboxedEnvironment() + t = env.from_string('{{ "a{x.foo}b".format_map({"x":{"foo": 42}}) }}') + assert t.render() == "a42b" + + def test_safe_format_all_okay(self): + env = SandboxedEnvironment() + t = env.from_string( + '{{ ("a{x.foo}b{y}"|safe).format_map({"x":{"foo": 42}, "y":"<foo>"}) }}' + ) + assert t.render() == "a42b<foo>" diff --git a/contrib/python/Jinja2/py3/tests/test_tests.py b/contrib/python/Jinja2/py3/tests/test_tests.py index 5cb4f57e17..75178d6adf 100644 --- a/contrib/python/Jinja2/py3/tests/test_tests.py +++ b/contrib/python/Jinja2/py3/tests/test_tests.py @@ -1,233 +1,233 @@ -import pytest -from markupsafe import Markup +import pytest +from markupsafe import Markup -from jinja2 import Environment -from jinja2 import TemplateAssertionError -from jinja2 import TemplateRuntimeError +from jinja2 import Environment +from jinja2 import TemplateAssertionError +from jinja2 import TemplateRuntimeError -class MyDict(dict): - pass +class MyDict(dict): + pass -class TestTestsCase: +class TestTestsCase: def test_defined(self, env): - tmpl = env.from_string("{{ missing is defined }}|{{ true is defined }}") - assert tmpl.render() == "False|True" + tmpl = env.from_string("{{ missing is defined }}|{{ true is defined }}") + assert tmpl.render() == "False|True" def test_even(self, env): - tmpl = env.from_string("""{{ 1 is even }}|{{ 2 is even }}""") - assert tmpl.render() == "False|True" + tmpl = env.from_string("""{{ 1 is even }}|{{ 2 is even }}""") + assert tmpl.render() == "False|True" def test_odd(self, env): - tmpl = env.from_string("""{{ 1 is odd }}|{{ 2 is odd }}""") - assert tmpl.render() == "True|False" + tmpl = env.from_string("""{{ 1 is odd }}|{{ 2 is odd }}""") + assert tmpl.render() == "True|False" def test_lower(self, env): - tmpl = env.from_string("""{{ "foo" is lower }}|{{ "FOO" is lower }}""") - assert tmpl.render() == "True|False" - - # Test type checks - @pytest.mark.parametrize( - "op,expect", - ( - ("none is none", True), - ("false is none", False), - ("true is none", False), - ("42 is none", False), - ("none is true", False), - ("false is true", False), - ("true is true", True), - ("0 is true", False), - ("1 is true", False), - ("42 is true", False), - ("none is false", False), - ("false is false", True), - ("true is false", False), - ("0 is false", False), - ("1 is false", False), - ("42 is false", False), - ("none is boolean", False), - ("false is boolean", True), - ("true is boolean", True), - ("0 is boolean", False), - ("1 is boolean", False), - ("42 is boolean", False), - ("0.0 is boolean", False), - ("1.0 is boolean", False), - ("3.14159 is boolean", False), - ("none is integer", False), - ("false is integer", False), - ("true is integer", False), - ("42 is integer", True), - ("3.14159 is integer", False), - ("(10 ** 100) is integer", True), - ("none is float", False), - ("false is float", False), - ("true is float", False), - ("42 is float", False), - ("4.2 is float", True), - ("(10 ** 100) is float", False), - ("none is number", False), - ("false is number", True), - ("true is number", True), - ("42 is number", True), - ("3.14159 is number", True), - ("complex is number", True), - ("(10 ** 100) is number", True), - ("none is string", False), - ("false is string", False), - ("true is string", False), - ("42 is string", False), - ('"foo" is string', True), - ("none is sequence", False), - ("false is sequence", False), - ("42 is sequence", False), - ('"foo" is sequence', True), - ("[] is sequence", True), - ("[1, 2, 3] is sequence", True), - ("{} is sequence", True), - ("none is mapping", False), - ("false is mapping", False), - ("42 is mapping", False), - ('"foo" is mapping', False), - ("[] is mapping", False), - ("{} is mapping", True), - ("mydict is mapping", True), - ("none is iterable", False), - ("false is iterable", False), - ("42 is iterable", False), - ('"foo" is iterable', True), - ("[] is iterable", True), - ("{} is iterable", True), - ("range(5) is iterable", True), - ("none is callable", False), - ("false is callable", False), - ("42 is callable", False), - ('"foo" is callable', False), - ("[] is callable", False), - ("{} is callable", False), - ("range is callable", True), - ), - ) - def test_types(self, env, op, expect): - t = env.from_string(f"{{{{ {op} }}}}") - assert t.render(mydict=MyDict(), complex=complex(1, 2)) == str(expect) + tmpl = env.from_string("""{{ "foo" is lower }}|{{ "FOO" is lower }}""") + assert tmpl.render() == "True|False" + + # Test type checks + @pytest.mark.parametrize( + "op,expect", + ( + ("none is none", True), + ("false is none", False), + ("true is none", False), + ("42 is none", False), + ("none is true", False), + ("false is true", False), + ("true is true", True), + ("0 is true", False), + ("1 is true", False), + ("42 is true", False), + ("none is false", False), + ("false is false", True), + ("true is false", False), + ("0 is false", False), + ("1 is false", False), + ("42 is false", False), + ("none is boolean", False), + ("false is boolean", True), + ("true is boolean", True), + ("0 is boolean", False), + ("1 is boolean", False), + ("42 is boolean", False), + ("0.0 is boolean", False), + ("1.0 is boolean", False), + ("3.14159 is boolean", False), + ("none is integer", False), + ("false is integer", False), + ("true is integer", False), + ("42 is integer", True), + ("3.14159 is integer", False), + ("(10 ** 100) is integer", True), + ("none is float", False), + ("false is float", False), + ("true is float", False), + ("42 is float", False), + ("4.2 is float", True), + ("(10 ** 100) is float", False), + ("none is number", False), + ("false is number", True), + ("true is number", True), + ("42 is number", True), + ("3.14159 is number", True), + ("complex is number", True), + ("(10 ** 100) is number", True), + ("none is string", False), + ("false is string", False), + ("true is string", False), + ("42 is string", False), + ('"foo" is string', True), + ("none is sequence", False), + ("false is sequence", False), + ("42 is sequence", False), + ('"foo" is sequence', True), + ("[] is sequence", True), + ("[1, 2, 3] is sequence", True), + ("{} is sequence", True), + ("none is mapping", False), + ("false is mapping", False), + ("42 is mapping", False), + ('"foo" is mapping', False), + ("[] is mapping", False), + ("{} is mapping", True), + ("mydict is mapping", True), + ("none is iterable", False), + ("false is iterable", False), + ("42 is iterable", False), + ('"foo" is iterable', True), + ("[] is iterable", True), + ("{} is iterable", True), + ("range(5) is iterable", True), + ("none is callable", False), + ("false is callable", False), + ("42 is callable", False), + ('"foo" is callable', False), + ("[] is callable", False), + ("{} is callable", False), + ("range is callable", True), + ), + ) + def test_types(self, env, op, expect): + t = env.from_string(f"{{{{ {op} }}}}") + assert t.render(mydict=MyDict(), complex=complex(1, 2)) == str(expect) def test_upper(self, env): tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}') - assert tmpl.render() == "True|False" + assert tmpl.render() == "True|False" def test_equalto(self, env): tmpl = env.from_string( - "{{ foo is eq 12 }}|" - "{{ foo is eq 0 }}|" - "{{ foo is eq (3 * 4) }}|" + "{{ foo is eq 12 }}|" + "{{ foo is eq 0 }}|" + "{{ foo is eq (3 * 4) }}|" '{{ bar is eq "baz" }}|' '{{ bar is eq "zab" }}|' '{{ bar is eq ("ba" + "z") }}|' - "{{ bar is eq bar }}|" - "{{ bar is eq foo }}" + "{{ bar is eq bar }}|" + "{{ bar is eq foo }}" ) - assert ( - tmpl.render(foo=12, bar="baz") - == "True|False|True|True|False|True|True|False" - ) - - @pytest.mark.parametrize( - "op,expect", - ( - ("eq 2", True), - ("eq 3", False), - ("ne 3", True), - ("ne 2", False), - ("lt 3", True), - ("lt 2", False), - ("le 2", True), - ("le 1", False), - ("gt 1", True), - ("gt 2", False), - ("ge 2", True), - ("ge 3", False), - ), - ) + assert ( + tmpl.render(foo=12, bar="baz") + == "True|False|True|True|False|True|True|False" + ) + + @pytest.mark.parametrize( + "op,expect", + ( + ("eq 2", True), + ("eq 3", False), + ("ne 3", True), + ("ne 2", False), + ("lt 3", True), + ("lt 2", False), + ("le 2", True), + ("le 1", False), + ("gt 1", True), + ("gt 2", False), + ("ge 2", True), + ("ge 3", False), + ), + ) def test_compare_aliases(self, env, op, expect): - t = env.from_string(f"{{{{ 2 is {op} }}}}") + t = env.from_string(f"{{{{ 2 is {op} }}}}") assert t.render() == str(expect) def test_sameas(self, env): - tmpl = env.from_string("{{ foo is sameas false }}|{{ 0 is sameas false }}") - assert tmpl.render(foo=False) == "True|False" + tmpl = env.from_string("{{ foo is sameas false }}|{{ 0 is sameas false }}") + assert tmpl.render(foo=False) == "True|False" def test_no_paren_for_arg1(self, env): - tmpl = env.from_string("{{ foo is sameas none }}") - assert tmpl.render(foo=None) == "True" + tmpl = env.from_string("{{ foo is sameas none }}") + assert tmpl.render(foo=None) == "True" def test_escaped(self, env): env = Environment(autoescape=True) - tmpl = env.from_string("{{ x is escaped }}|{{ y is escaped }}") - assert tmpl.render(x="foo", y=Markup("foo")) == "False|True" + tmpl = env.from_string("{{ x is escaped }}|{{ y is escaped }}") + assert tmpl.render(x="foo", y=Markup("foo")) == "False|True" def test_greaterthan(self, env): - tmpl = env.from_string("{{ 1 is greaterthan 0 }}|{{ 0 is greaterthan 1 }}") - assert tmpl.render() == "True|False" + tmpl = env.from_string("{{ 1 is greaterthan 0 }}|{{ 0 is greaterthan 1 }}") + assert tmpl.render() == "True|False" def test_lessthan(self, env): - tmpl = env.from_string("{{ 0 is lessthan 1 }}|{{ 1 is lessthan 0 }}") - assert tmpl.render() == "True|False" + tmpl = env.from_string("{{ 0 is lessthan 1 }}|{{ 1 is lessthan 0 }}") + assert tmpl.render() == "True|False" def test_multiple_tests(self): items = [] - + def matching(x, y): items.append((x, y)) return False - + env = Environment() - env.tests["matching"] = matching - tmpl = env.from_string( - "{{ 'us-west-1' is matching '(us-east-1|ap-northeast-1)'" - " or 'stage' is matching '(dev|stage)' }}" - ) - assert tmpl.render() == "False" - assert items == [ - ("us-west-1", "(us-east-1|ap-northeast-1)"), - ("stage", "(dev|stage)"), - ] + env.tests["matching"] = matching + tmpl = env.from_string( + "{{ 'us-west-1' is matching '(us-east-1|ap-northeast-1)'" + " or 'stage' is matching '(dev|stage)' }}" + ) + assert tmpl.render() == "False" + assert items == [ + ("us-west-1", "(us-east-1|ap-northeast-1)"), + ("stage", "(dev|stage)"), + ] def test_in(self, env): - tmpl = env.from_string( - '{{ "o" is in "foo" }}|' - '{{ "foo" is in "foo" }}|' - '{{ "b" is in "foo" }}|' - "{{ 1 is in ((1, 2)) }}|" - "{{ 3 is in ((1, 2)) }}|" - "{{ 1 is in [1, 2] }}|" - "{{ 3 is in [1, 2] }}|" - '{{ "foo" is in {"foo": 1}}}|' - '{{ "baz" is in {"bar": 1}}}' - ) - assert tmpl.render() == "True|True|False|True|False|True|False|True|False" - - -def test_name_undefined(env): - with pytest.raises(TemplateAssertionError, match="No test named 'f'"): - env.from_string("{{ x is f }}") - - -def test_name_undefined_in_if(env): - t = env.from_string("{% if x is defined %}{{ x is f }}{% endif %}") - assert t.render() == "" - - with pytest.raises(TemplateRuntimeError, match="No test named 'f'"): - t.render(x=1) - - -def test_is_filter(env): - assert env.call_test("filter", "title") - assert not env.call_test("filter", "bad-name") - - -def test_is_test(env): - assert env.call_test("test", "number") - assert not env.call_test("test", "bad-name") + tmpl = env.from_string( + '{{ "o" is in "foo" }}|' + '{{ "foo" is in "foo" }}|' + '{{ "b" is in "foo" }}|' + "{{ 1 is in ((1, 2)) }}|" + "{{ 3 is in ((1, 2)) }}|" + "{{ 1 is in [1, 2] }}|" + "{{ 3 is in [1, 2] }}|" + '{{ "foo" is in {"foo": 1}}}|' + '{{ "baz" is in {"bar": 1}}}' + ) + assert tmpl.render() == "True|True|False|True|False|True|False|True|False" + + +def test_name_undefined(env): + with pytest.raises(TemplateAssertionError, match="No test named 'f'"): + env.from_string("{{ x is f }}") + + +def test_name_undefined_in_if(env): + t = env.from_string("{% if x is defined %}{{ x is f }}{% endif %}") + assert t.render() == "" + + with pytest.raises(TemplateRuntimeError, match="No test named 'f'"): + t.render(x=1) + + +def test_is_filter(env): + assert env.call_test("filter", "title") + assert not env.call_test("filter", "bad-name") + + +def test_is_test(env): + assert env.call_test("test", "number") + assert not env.call_test("test", "bad-name") diff --git a/contrib/python/Jinja2/py3/tests/test_utils.py b/contrib/python/Jinja2/py3/tests/test_utils.py index cfc9bd0f5e..feaf8dc1d4 100644 --- a/contrib/python/Jinja2/py3/tests/test_utils.py +++ b/contrib/python/Jinja2/py3/tests/test_utils.py @@ -1,21 +1,21 @@ -import pickle -import random -from collections import deque -from copy import copy as shallow_copy +import pickle +import random +from collections import deque +from copy import copy as shallow_copy import pytest -from markupsafe import Markup +from markupsafe import Markup -from jinja2.utils import consume -from jinja2.utils import generate_lorem_ipsum -from jinja2.utils import LRUCache -from jinja2.utils import missing -from jinja2.utils import object_type_repr -from jinja2.utils import select_autoescape -from jinja2.utils import urlize +from jinja2.utils import consume +from jinja2.utils import generate_lorem_ipsum +from jinja2.utils import LRUCache +from jinja2.utils import missing +from jinja2.utils import object_type_repr +from jinja2.utils import select_autoescape +from jinja2.utils import urlize -class TestLRUCache: +class TestLRUCache: def test_simple(self): d = LRUCache(3) d["a"] = 1 @@ -23,18 +23,18 @@ class TestLRUCache: d["c"] = 3 d["a"] d["d"] = 4 - assert d.keys() == ["d", "a", "c"] - - def test_values(self): - cache = LRUCache(3) - cache["b"] = 1 - cache["a"] = 2 - assert cache.values() == [2, 1] - - def test_values_empty(self): - cache = LRUCache(2) - assert cache.values() == [] - + assert d.keys() == ["d", "a", "c"] + + def test_values(self): + cache = LRUCache(3) + cache["b"] = 1 + cache["a"] = 2 + assert cache.values() == [2, 1] + + def test_values_empty(self): + cache = LRUCache(2) + assert cache.values() == [] + def test_pickleable(self): cache = LRUCache(2) cache["foo"] = 42 @@ -47,139 +47,139 @@ class TestLRUCache: assert copy._mapping == cache._mapping assert copy._queue == cache._queue - @pytest.mark.parametrize("copy_func", [LRUCache.copy, shallow_copy]) - def test_copy(self, copy_func): - cache = LRUCache(2) - cache["a"] = 1 - cache["b"] = 2 - copy = copy_func(cache) - assert copy._queue == cache._queue - copy["c"] = 3 - assert copy._queue != cache._queue - assert copy.keys() == ["c", "b"] - - def test_clear(self): - d = LRUCache(3) - d["a"] = 1 - d["b"] = 2 - d["c"] = 3 - d.clear() - assert d.__getstate__() == {"capacity": 3, "_mapping": {}, "_queue": deque([])} - - def test_repr(self): - d = LRUCache(3) - d["a"] = 1 - d["b"] = 2 - d["c"] = 3 - # Sort the strings - mapping is unordered - assert sorted(repr(d)) == sorted("<LRUCache {'a': 1, 'b': 2, 'c': 3}>") - - def test_items(self): - """Test various items, keys, values and iterators of LRUCache.""" - d = LRUCache(3) - d["a"] = 1 - d["b"] = 2 - d["c"] = 3 - assert d.items() == [("c", 3), ("b", 2), ("a", 1)] - assert d.keys() == ["c", "b", "a"] - assert d.values() == [3, 2, 1] - assert list(reversed(d)) == ["a", "b", "c"] - - # Change the cache a little - d["b"] - d["a"] = 4 - assert d.items() == [("a", 4), ("b", 2), ("c", 3)] - assert d.keys() == ["a", "b", "c"] - assert d.values() == [4, 2, 3] - assert list(reversed(d)) == ["c", "b", "a"] - - def test_setdefault(self): - d = LRUCache(3) - assert len(d) == 0 - assert d.setdefault("a") is None - assert d.setdefault("a", 1) is None - assert len(d) == 1 - assert d.setdefault("b", 2) == 2 - assert len(d) == 2 - - -class TestHelpers: + @pytest.mark.parametrize("copy_func", [LRUCache.copy, shallow_copy]) + def test_copy(self, copy_func): + cache = LRUCache(2) + cache["a"] = 1 + cache["b"] = 2 + copy = copy_func(cache) + assert copy._queue == cache._queue + copy["c"] = 3 + assert copy._queue != cache._queue + assert copy.keys() == ["c", "b"] + + def test_clear(self): + d = LRUCache(3) + d["a"] = 1 + d["b"] = 2 + d["c"] = 3 + d.clear() + assert d.__getstate__() == {"capacity": 3, "_mapping": {}, "_queue": deque([])} + + def test_repr(self): + d = LRUCache(3) + d["a"] = 1 + d["b"] = 2 + d["c"] = 3 + # Sort the strings - mapping is unordered + assert sorted(repr(d)) == sorted("<LRUCache {'a': 1, 'b': 2, 'c': 3}>") + + def test_items(self): + """Test various items, keys, values and iterators of LRUCache.""" + d = LRUCache(3) + d["a"] = 1 + d["b"] = 2 + d["c"] = 3 + assert d.items() == [("c", 3), ("b", 2), ("a", 1)] + assert d.keys() == ["c", "b", "a"] + assert d.values() == [3, 2, 1] + assert list(reversed(d)) == ["a", "b", "c"] + + # Change the cache a little + d["b"] + d["a"] = 4 + assert d.items() == [("a", 4), ("b", 2), ("c", 3)] + assert d.keys() == ["a", "b", "c"] + assert d.values() == [4, 2, 3] + assert list(reversed(d)) == ["c", "b", "a"] + + def test_setdefault(self): + d = LRUCache(3) + assert len(d) == 0 + assert d.setdefault("a") is None + assert d.setdefault("a", 1) is None + assert len(d) == 1 + assert d.setdefault("b", 2) == 2 + assert len(d) == 2 + + +class TestHelpers: def test_object_type_repr(self): - class X: + class X: pass - assert object_type_repr(42) == "int object" - assert object_type_repr([]) == "list object" - assert object_type_repr(X()) == "__tests__.test_utils.X object" - assert object_type_repr(None) == "None" - assert object_type_repr(Ellipsis) == "Ellipsis" - + assert object_type_repr(42) == "int object" + assert object_type_repr([]) == "list object" + assert object_type_repr(X()) == "__tests__.test_utils.X object" + assert object_type_repr(None) == "None" + assert object_type_repr(Ellipsis) == "Ellipsis" + def test_autoescape_select(self): func = select_autoescape( - enabled_extensions=("html", ".htm"), - disabled_extensions=("txt",), - default_for_string="STRING", - default="NONE", + enabled_extensions=("html", ".htm"), + disabled_extensions=("txt",), + default_for_string="STRING", + default="NONE", ) - assert func(None) == "STRING" - assert func("unknown.foo") == "NONE" - assert func("foo.html") - assert func("foo.htm") - assert not func("foo.txt") - assert func("FOO.HTML") - assert not func("FOO.TXT") + assert func(None) == "STRING" + assert func("unknown.foo") == "NONE" + assert func("foo.html") + assert func("foo.htm") + assert not func("foo.txt") + assert func("FOO.HTML") + assert not func("FOO.TXT") -class TestEscapeUrlizeTarget: +class TestEscapeUrlizeTarget: def test_escape_urlize_target(self): url = "http://example.org" target = "<script>" - assert urlize(url, target=target) == ( - '<a href="http://example.org"' - ' target="<script>">' - "http://example.org</a>" - ) - - -class TestLoremIpsum: - def test_lorem_ipsum_markup(self): - """Test that output of lorem_ipsum is Markup by default.""" - assert isinstance(generate_lorem_ipsum(), Markup) - - def test_lorem_ipsum_html(self): - """Test that output of lorem_ipsum is a string_type when not html.""" - assert isinstance(generate_lorem_ipsum(html=False), str) - - def test_lorem_ipsum_n(self): - """Test that the n (number of lines) works as expected.""" - assert generate_lorem_ipsum(n=0, html=False) == "" - for n in range(1, 50): - assert generate_lorem_ipsum(n=n, html=False).count("\n") == (n - 1) * 2 - - def test_lorem_ipsum_min(self): - """Test that at least min words are in the output of each line""" - for _ in range(5): - m = random.randrange(20, 99) - for _ in range(10): - assert generate_lorem_ipsum(n=1, min=m, html=False).count(" ") >= m - 1 - - def test_lorem_ipsum_max(self): - """Test that at least max words are in the output of each line""" - for _ in range(5): - m = random.randrange(21, 100) - for _ in range(10): - assert generate_lorem_ipsum(n=1, max=m, html=False).count(" ") < m - 1 - - -def test_missing(): - """Test the repr of missing.""" - assert repr(missing) == "missing" - - -def test_consume(): - """Test that consume consumes an iterator.""" - x = iter([1, 2, 3, 4, 5]) - consume(x) - with pytest.raises(StopIteration): - next(x) + assert urlize(url, target=target) == ( + '<a href="http://example.org"' + ' target="<script>">' + "http://example.org</a>" + ) + + +class TestLoremIpsum: + def test_lorem_ipsum_markup(self): + """Test that output of lorem_ipsum is Markup by default.""" + assert isinstance(generate_lorem_ipsum(), Markup) + + def test_lorem_ipsum_html(self): + """Test that output of lorem_ipsum is a string_type when not html.""" + assert isinstance(generate_lorem_ipsum(html=False), str) + + def test_lorem_ipsum_n(self): + """Test that the n (number of lines) works as expected.""" + assert generate_lorem_ipsum(n=0, html=False) == "" + for n in range(1, 50): + assert generate_lorem_ipsum(n=n, html=False).count("\n") == (n - 1) * 2 + + def test_lorem_ipsum_min(self): + """Test that at least min words are in the output of each line""" + for _ in range(5): + m = random.randrange(20, 99) + for _ in range(10): + assert generate_lorem_ipsum(n=1, min=m, html=False).count(" ") >= m - 1 + + def test_lorem_ipsum_max(self): + """Test that at least max words are in the output of each line""" + for _ in range(5): + m = random.randrange(21, 100) + for _ in range(10): + assert generate_lorem_ipsum(n=1, max=m, html=False).count(" ") < m - 1 + + +def test_missing(): + """Test the repr of missing.""" + assert repr(missing) == "missing" + + +def test_consume(): + """Test that consume consumes an iterator.""" + x = iter([1, 2, 3, 4, 5]) + consume(x) + with pytest.raises(StopIteration): + next(x) diff --git a/contrib/python/Jinja2/py3/tests/ya.make b/contrib/python/Jinja2/py3/tests/ya.make index 680b0c2cde..019fe0025e 100644 --- a/contrib/python/Jinja2/py3/tests/ya.make +++ b/contrib/python/Jinja2/py3/tests/ya.make @@ -1,54 +1,54 @@ -PY3TEST() - -OWNER(g:python-contrib) - -PEERDIR( - contrib/python/Jinja2 -) - -PY_SRCS( - TOP_LEVEL - res/__init__.py -) - -DATA( - arcadia/contrib/python/Jinja2/py3/tests/res -) - -RESOURCE_FILES( - PREFIX contrib/python/Jinja2/py3/tests/ - res/templates/broken.html - res/templates/foo/test.html - res/templates/mojibake.txt - res/templates/syntaxerror.html - res/templates/test.html - res/templates2/foo -) - -TEST_SRCS( - conftest.py - test_api.py - test_async_filters.py - test_async.py - test_bytecode_cache.py - test_core_tags.py - test_debug.py - test_ext.py - test_features.py - test_filters.py - test_idtracking.py - test_imports.py - test_inheritance.py - test_lexnparse.py - test_loader.py - test_nativetypes.py - test_regression.py - test_runtime.py - test_security.py - test_tests.py - test_utils.py -) - -NO_LINT() - -END() +PY3TEST() + +OWNER(g:python-contrib) + +PEERDIR( + contrib/python/Jinja2 +) + +PY_SRCS( + TOP_LEVEL + res/__init__.py +) + +DATA( + arcadia/contrib/python/Jinja2/py3/tests/res +) + +RESOURCE_FILES( + PREFIX contrib/python/Jinja2/py3/tests/ + res/templates/broken.html + res/templates/foo/test.html + res/templates/mojibake.txt + res/templates/syntaxerror.html + res/templates/test.html + res/templates2/foo +) + +TEST_SRCS( + conftest.py + test_api.py + test_async_filters.py + test_async.py + test_bytecode_cache.py + test_core_tags.py + test_debug.py + test_ext.py + test_features.py + test_filters.py + test_idtracking.py + test_imports.py + test_inheritance.py + test_lexnparse.py + test_loader.py + test_nativetypes.py + test_regression.py + test_runtime.py + test_security.py + test_tests.py + test_utils.py +) + +NO_LINT() + +END() |