mirror of https://github.com/lukechilds/node.git
Browse Source
PR-URL: https://github.com/nodejs/node/pull/7796 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: jasnell - James M Snell <jasnell@gmail.com>v6.x
committed by
cjihrig
20 changed files with 0 additions and 3721 deletions
@ -1,74 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.conftest |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Configuration and Fixtures for the tests |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
import os |
|
||||
import re |
|
||||
import sys |
|
||||
|
|
||||
from traceback import format_exception |
|
||||
from jinja2 import loaders |
|
||||
from jinja2._compat import PY2 |
|
||||
from jinja2 import Environment |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def env(): |
|
||||
'''returns a new environment. |
|
||||
''' |
|
||||
return Environment() |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def dict_loader(): |
|
||||
'''returns DictLoader |
|
||||
''' |
|
||||
return loaders.DictLoader({ |
|
||||
'justdict.html': 'FOO' |
|
||||
}) |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def package_loader(): |
|
||||
'''returns PackageLoader initialized from templates |
|
||||
''' |
|
||||
return loaders.PackageLoader('res', 'templates') |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def filesystem_loader(): |
|
||||
'''returns FileSystemLoader initialized to res/templates directory |
|
||||
''' |
|
||||
here = os.path.dirname(os.path.abspath(__file__)) |
|
||||
return loaders.FileSystemLoader(here + '/res/templates') |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def function_loader(): |
|
||||
'''returns a FunctionLoader |
|
||||
''' |
|
||||
return loaders.FunctionLoader({'justfunction.html': 'FOO'}.get) |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def choice_loader(dict_loader, package_loader): |
|
||||
'''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 |
|
||||
}) |
|
@ -1,3 +0,0 @@ |
|||||
Before |
|
||||
{{ fail() }} |
|
||||
After |
|
@ -1 +0,0 @@ |
|||||
FOO |
|
@ -1,4 +0,0 @@ |
|||||
Foo |
|
||||
{% for item in broken %} |
|
||||
... |
|
||||
{% endif %} |
|
@ -1 +0,0 @@ |
|||||
BAR |
|
@ -1,327 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.api |
|
||||
~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests the public API and related stuff. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import os |
|
||||
import tempfile |
|
||||
import shutil |
|
||||
|
|
||||
import pytest |
|
||||
from jinja2 import Environment, Undefined, DebugUndefined, \ |
|
||||
StrictUndefined, UndefinedError, meta, \ |
|
||||
is_undefined, Template, DictLoader, make_logging_undefined |
|
||||
from jinja2.compiler import CodeGenerator |
|
||||
from jinja2.runtime import Context |
|
||||
from jinja2.utils import Cycler |
|
||||
|
|
||||
|
|
||||
@pytest.mark.api |
|
||||
@pytest.mark.extended |
|
||||
class TestExtendedAPI(): |
|
||||
|
|
||||
def test_item_and_attribute(self, env): |
|
||||
from jinja2.sandbox import SandboxedEnvironment |
|
||||
|
|
||||
for env in Environment(), SandboxedEnvironment(): |
|
||||
# the |list is necessary for python3 |
|
||||
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)]" |
|
||||
tmpl = env.from_string('{{ foo["items"] }}') |
|
||||
assert tmpl.render(foo={'items': 42}) == '42' |
|
||||
|
|
||||
def test_finalizer(self, env): |
|
||||
def finalize_none_empty(value): |
|
||||
if value is None: |
|
||||
value = u'' |
|
||||
return value |
|
||||
env = Environment(finalize=finalize_none_empty) |
|
||||
tmpl = env.from_string('{% for item in seq %}|{{ item }}{% endfor %}') |
|
||||
assert tmpl.render(seq=(None, 1, "foo")) == '||1|foo' |
|
||||
tmpl = env.from_string('<{{ none }}>') |
|
||||
assert tmpl.render() == '<>' |
|
||||
|
|
||||
def test_cycler(self, env): |
|
||||
items = 1, 2, 3 |
|
||||
c = Cycler(*items) |
|
||||
for item in items + items: |
|
||||
assert c.current == item |
|
||||
assert next(c) == item |
|
||||
next(c) |
|
||||
assert c.current == 2 |
|
||||
c.reset() |
|
||||
assert c.current == 1 |
|
||||
|
|
||||
def test_expressions(self, env): |
|
||||
expr = env.compile_expression("foo") |
|
||||
assert expr() is None |
|
||||
assert expr(foo=42) == 42 |
|
||||
expr2 = env.compile_expression("foo", undefined_to_none=False) |
|
||||
assert is_undefined(expr2()) |
|
||||
|
|
||||
expr = env.compile_expression("42 + foo") |
|
||||
assert expr(foo=42) == 84 |
|
||||
|
|
||||
def test_template_passthrough(self, env): |
|
||||
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_autoescape_autoselect(self, env): |
|
||||
def select_autoescape(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>' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.api |
|
||||
@pytest.mark.meta |
|
||||
class TestMeta(): |
|
||||
|
|
||||
def test_find_undeclared_variables(self, env): |
|
||||
ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') |
|
||||
x = meta.find_undeclared_variables(ast) |
|
||||
assert x == set(['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 == set(['bar', 'seq', 'muh']) |
|
||||
|
|
||||
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) 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" %}') |
|
||||
i = meta.find_referenced_templates(ast) |
|
||||
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'] |
|
||||
|
|
||||
ast = env.parse('{% include ("foo.html", "bar.html") %}') |
|
||||
i = meta.find_referenced_templates(ast) |
|
||||
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] |
|
||||
|
|
||||
ast = env.parse('{% include ("foo.html", "bar.html", foo) %}') |
|
||||
i = meta.find_referenced_templates(ast) |
|
||||
assert list(i) == ['foo.html', 'bar.html', None] |
|
||||
|
|
||||
|
|
||||
@pytest.mark.api |
|
||||
@pytest.mark.streaming |
|
||||
class TestStreaming(): |
|
||||
|
|
||||
def test_basic_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(4))) |
|
||||
assert next(stream) == '<ul>' |
|
||||
assert next(stream) == '<li>1 - 0</li>' |
|
||||
assert next(stream) == '<li>2 - 1</li>' |
|
||||
assert next(stream) == '<li>3 - 2</li>' |
|
||||
assert next(stream) == '<li>4 - 3</li>' |
|
||||
assert next(stream) == '</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(4))) |
|
||||
stream.enable_buffering(size=3) |
|
||||
assert next(stream) == u'<ul><li>1 - 0</li><li>2 - 1</li>' |
|
||||
assert next(stream) == u'<li>3 - 2</li><li>4 - 3</li></ul>' |
|
||||
|
|
||||
def test_streaming_behavior(self, env): |
|
||||
tmpl = env.from_string("") |
|
||||
stream = tmpl.stream() |
|
||||
assert not stream.buffered |
|
||||
stream.enable_buffering(20) |
|
||||
assert stream.buffered |
|
||||
stream.disable_buffering() |
|
||||
assert not stream.buffered |
|
||||
|
|
||||
def test_dump_stream(self, env): |
|
||||
tmp = tempfile.mkdtemp() |
|
||||
try: |
|
||||
tmpl = env.from_string(u"\u2713") |
|
||||
stream = tmpl.stream() |
|
||||
stream.dump(os.path.join(tmp, 'dump.txt'), 'utf-8') |
|
||||
with open(os.path.join(tmp, 'dump.txt'), 'rb') as f: |
|
||||
assert f.read() == b'\xe2\x9c\x93' |
|
||||
finally: |
|
||||
shutil.rmtree(tmp) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.api |
|
||||
@pytest.mark.undefined |
|
||||
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') |
|
||||
pytest.raises(UndefinedError, t.render, test=test) |
|
||||
|
|
||||
def test_undefined_and_special_attributes(self): |
|
||||
try: |
|
||||
Undefined('Foo').__dict__ |
|
||||
except AttributeError: |
|
||||
pass |
|
||||
else: |
|
||||
assert False, "Expected actual attribute error" |
|
||||
|
|
||||
def test_logging_undefined(self): |
|
||||
_messages = [] |
|
||||
|
|
||||
class DebugLogger(object): |
|
||||
def warning(self, msg, *args): |
|
||||
_messages.append('W:' + msg % args) |
|
||||
|
|
||||
def error(self, msg, *args): |
|
||||
_messages.append('E:' + msg % args) |
|
||||
|
|
||||
logging_undefined = make_logging_undefined(DebugLogger()) |
|
||||
env = Environment(undefined=logging_undefined) |
|
||||
assert env.from_string('{{ missing }}').render() == u'' |
|
||||
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', |
|
||||
"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', |
|
||||
] |
|
||||
|
|
||||
def test_default_undefined(self): |
|
||||
env = Environment(undefined=Undefined) |
|
||||
assert env.from_string('{{ missing }}').render() == u'' |
|
||||
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' |
|
||||
|
|
||||
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) \ |
|
||||
== u"{{ no such element: int object['missing'] }}" |
|
||||
assert env.from_string('{{ not missing }}').render() == 'True' |
|
||||
|
|
||||
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) |
|
||||
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' |
|
||||
|
|
||||
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): |
|
||||
try: |
|
||||
Environment().getattr(None, 'split')() |
|
||||
except UndefinedError as e: |
|
||||
assert e.message == "'None' has no attribute 'split'" |
|
||||
else: |
|
||||
assert False, 'expected exception' |
|
||||
|
|
||||
def test_object_repr(self): |
|
||||
try: |
|
||||
Undefined(obj=42, name='upper')() |
|
||||
except UndefinedError as e: |
|
||||
assert e.message == "'int object' has no attribute 'upper'" |
|
||||
else: |
|
||||
assert False, 'expected exception' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.api |
|
||||
@pytest.mark.lowlevel |
|
||||
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')) |
|
||||
else: |
|
||||
super(CustomCodeGenerator, self).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' |
|
||||
|
|
||||
def test_custom_context(self): |
|
||||
class CustomContext(Context): |
|
||||
def resolve(self, key): |
|
||||
return 'resolve-' + key |
|
||||
|
|
||||
class CustomEnvironment(Environment): |
|
||||
context_class = CustomContext |
|
||||
|
|
||||
env = CustomEnvironment() |
|
||||
tmpl = env.from_string('{{ foo }}') |
|
||||
assert tmpl.render() == 'resolve-foo' |
|
@ -1,32 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.bytecode_cache |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Test bytecode caching |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
from jinja2 import Environment |
|
||||
from jinja2.bccache import FileSystemBytecodeCache |
|
||||
from jinja2.exceptions import TemplateNotFound |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def env(package_loader): |
|
||||
bytecode_cache = FileSystemBytecodeCache() |
|
||||
return Environment( |
|
||||
loader=package_loader, |
|
||||
bytecode_cache=bytecode_cache, |
|
||||
) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.byte_code_cache |
|
||||
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') |
|
@ -1,337 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.core_tags |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Test the core tags like for and if. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
from jinja2 import Environment, TemplateSyntaxError, UndefinedError, \ |
|
||||
DictLoader |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def env_trim(): |
|
||||
return Environment(trim_blocks=True) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.core_tags |
|
||||
@pytest.mark.for_loop |
|
||||
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' |
|
||||
|
|
||||
def test_else(self, env): |
|
||||
tmpl = env.from_string( |
|
||||
'{% for item in seq %}XXX{% else %}...{% endfor %}') |
|
||||
assert tmpl.render() == '...' |
|
||||
|
|
||||
def test_empty_blocks(self, env): |
|
||||
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 -%} |
|
||||
{{ 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('|') |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_cycling(self, env): |
|
||||
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 |
|
||||
|
|
||||
def test_scope(self, env): |
|
||||
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): |
|
||||
def inner(): |
|
||||
for item in range(5): |
|
||||
yield item |
|
||||
tmpl = env.from_string('{% for item in iter %}{{ item }}{% endfor %}') |
|
||||
output = tmpl.render(iter=inner()) |
|
||||
assert output == '01234' |
|
||||
|
|
||||
def test_noniter(self, env): |
|
||||
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 -%} |
|
||||
[{{ 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]>]' |
|
||||
|
|
||||
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]>]' |
|
||||
|
|
||||
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]>]' |
|
||||
|
|
||||
def test_looploop(self, env): |
|
||||
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]' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_loop_errors(self, env): |
|
||||
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() == '' |
|
||||
|
|
||||
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(''' |
|
||||
{%- 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]' |
|
||||
|
|
||||
def test_loop_unassignable(self, env): |
|
||||
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]' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_recursive_empty_loop_iter(self, env): |
|
||||
t = env.from_string(''' |
|
||||
{%- for item in foo recursive -%}{%- endfor -%} |
|
||||
''') |
|
||||
assert t.render(dict(foo=[])) == '' |
|
||||
|
|
||||
def test_call_in_loop(self, env): |
|
||||
t = env.from_string(''' |
|
||||
{%- macro do_something() -%} |
|
||||
[{{ caller() }}] |
|
||||
{%- endmacro %} |
|
||||
|
|
||||
{%- for i in [1, 2, 3] %} |
|
||||
{%- call do_something() -%} |
|
||||
{{ i }} |
|
||||
{%- endcall %} |
|
||||
{%- endfor -%} |
|
||||
''') |
|
||||
assert t.render() == '[1][2][3]' |
|
||||
|
|
||||
def test_scoping_bug(self, env): |
|
||||
t = env.from_string(''' |
|
||||
{%- for item in foo %}...{{ item }}...{% endfor %} |
|
||||
{%- macro item(a) %}...{{ a }}...{% endmacro %} |
|
||||
{{- item(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' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.core_tags |
|
||||
@pytest.mark.if_condition |
|
||||
class TestIfCondition(): |
|
||||
|
|
||||
def test_simple(self, env): |
|
||||
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() == '...' |
|
||||
|
|
||||
def test_else(self, env): |
|
||||
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() == '[]' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.core_tags |
|
||||
@pytest.mark.macros |
|
||||
class TestMacros(): |
|
||||
def test_simple(self, env_trim): |
|
||||
tmpl = env_trim.from_string('''\ |
|
||||
{% macro say_hello(name) %}Hello {{ name }}!{% endmacro %} |
|
||||
{{ say_hello('Peter') }}''') |
|
||||
assert tmpl.render() == 'Hello Peter!' |
|
||||
|
|
||||
def test_scoping(self, env_trim): |
|
||||
tmpl = env_trim.from_string('''\ |
|
||||
{% macro level1(data1) %} |
|
||||
{% macro level2(data2) %}{{ data1 }}|{{ data2 }}{% endmacro %} |
|
||||
{{ level2('bar') }}{% endmacro %} |
|
||||
{{ level1('foo') }}''') |
|
||||
assert tmpl.render() == 'foo|bar' |
|
||||
|
|
||||
def test_arguments(self, env_trim): |
|
||||
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' |
|
||||
|
|
||||
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 %}''') |
|
||||
|
|
||||
def test_caller_defaults_nonsense(self, env_trim): |
|
||||
pytest.raises(TemplateSyntaxError, env_trim.from_string, '''\ |
|
||||
{% macro a() %}{{ caller() }}{% endmacro %} |
|
||||
{% call(x, y=1, z) a() %}{% endcall %}''') |
|
||||
|
|
||||
def test_varargs(self, env_trim): |
|
||||
tmpl = env_trim.from_string('''\ |
|
||||
{% macro test() %}{{ varargs|join('|') }}{% endmacro %}\ |
|
||||
{{ test(1, 2, 3) }}''') |
|
||||
assert tmpl.render() == '1|2|3' |
|
||||
|
|
||||
def test_simple_call(self, env_trim): |
|
||||
tmpl = env_trim.from_string('''\ |
|
||||
{% macro test() %}[[{{ caller() }}]]{% endmacro %}\ |
|
||||
{% call test() %}data{% endcall %}''') |
|
||||
assert tmpl.render() == '[[data]]' |
|
||||
|
|
||||
def test_complex_call(self, env_trim): |
|
||||
tmpl = env_trim.from_string('''\ |
|
||||
{% macro test() %}[[{{ caller('data') }}]]{% endmacro %}\ |
|
||||
{% call(data) test() %}{{ data }}{% endcall %}''') |
|
||||
assert tmpl.render() == '[[data]]' |
|
||||
|
|
||||
def test_caller_undefined(self, env_trim): |
|
||||
tmpl = env_trim.from_string('''\ |
|
||||
{% set caller = 42 %}\ |
|
||||
{% macro test() %}{{ caller is not defined }}{% endmacro %}\ |
|
||||
{{ test() }}''') |
|
||||
assert tmpl.render() == 'True' |
|
||||
|
|
||||
def test_include(self, env_trim): |
|
||||
env_trim = Environment( |
|
||||
loader=DictLoader({ |
|
||||
'include': '{% macro test(foo) %}[{{ foo }}]{% endmacro %}' |
|
||||
}) |
|
||||
) |
|
||||
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.defaults == () |
|
||||
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 |
|
||||
assert tmpl.module.bar.arguments == () |
|
||||
assert tmpl.module.bar.defaults == () |
|
||||
assert not tmpl.module.bar.caller |
|
||||
assert tmpl.module.bar.catch_kwargs |
|
||||
assert tmpl.module.bar.catch_varargs |
|
||||
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' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.core_tags |
|
||||
@pytest.mark.set |
|
||||
class TestSet(): |
|
||||
|
|
||||
def test_normal(self, env_trim): |
|
||||
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 == u'42' |
|
@ -1,73 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.debug |
|
||||
~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests the debug system. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
|
|
||||
import re |
|
||||
|
|
||||
import sys |
|
||||
from traceback import format_exception |
|
||||
|
|
||||
from jinja2 import Environment, TemplateSyntaxError |
|
||||
from traceback import format_exception |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def fs_env(filesystem_loader): |
|
||||
'''returns a new environment. |
|
||||
''' |
|
||||
return Environment(loader=filesystem_loader) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.debug |
|
||||
class TestDebug(): |
|
||||
|
|
||||
def assert_traceback_matches(self, callback, expected_tb): |
|
||||
try: |
|
||||
callback() |
|
||||
except Exception as e: |
|
||||
tb = format_exception(*sys.exc_info()) |
|
||||
if re.search(expected_tb.strip(), ''.join(tb)) is None: |
|
||||
assert False, ('Traceback did not match:\n\n%s\nexpected:\n%s' % |
|
||||
(''.join(tb), expected_tb)) |
|
||||
else: |
|
||||
assert False, 'Expected exception' |
|
||||
|
|
||||
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''' |
|
||||
File ".*?broken.html", line 2, in (top-level template code|<module>) |
|
||||
\{\{ fail\(\) \}\} |
|
||||
File ".*debug?.pyc?", line \d+, in <lambda> |
|
||||
tmpl\.render\(fail=lambda: 1 / 0\) |
|
||||
ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero |
|
||||
''') |
|
||||
|
|
||||
def test_syntax_error(self, fs_env): |
|
||||
# XXX: the .*? is necessary for python3 which does not hide |
|
||||
# some of the stack frames we don't want to show. Not sure |
|
||||
# what's up with that, but that is not that critical. Should |
|
||||
# be fixed though. |
|
||||
self.assert_traceback_matches(lambda: fs_env.get_template('syntaxerror.html'), r'''(?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'. |
|
||||
''') |
|
||||
|
|
||||
def test_regular_syntax_error(self, fs_env): |
|
||||
def test(): |
|
||||
raise TemplateSyntaxError('wtf', 42) |
|
||||
self.assert_traceback_matches(test, r''' |
|
||||
File ".*debug.pyc?", line \d+, in test |
|
||||
raise TemplateSyntaxError\('wtf', 42\) |
|
||||
(jinja2\.exceptions\.)?TemplateSyntaxError: wtf |
|
||||
line 42''') |
|
@ -1,467 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.ext |
|
||||
~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests for the extensions. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import re |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Environment, DictLoader, contextfunction, nodes |
|
||||
from jinja2.exceptions import TemplateAssertionError |
|
||||
from jinja2.ext import Extension |
|
||||
from jinja2.lexer import Token, count_newlines |
|
||||
from jinja2._compat import BytesIO, itervalues, text_type |
|
||||
|
|
||||
importable_object = 23 |
|
||||
|
|
||||
_gettext_re = re.compile(r'_\((.*?)\)(?s)') |
|
||||
|
|
||||
|
|
||||
i18n_templates = { |
|
||||
'master.html': '<title>{{ page_title|default(_("missing")) }}</title>' |
|
||||
'{% block body %}{% endblock %}', |
|
||||
'child.html': '{% extends "master.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 = { |
|
||||
'master.html': '<title>{{ page_title|default(_("missing")) }}</title>' |
|
||||
'{% block body %}{% endblock %}', |
|
||||
'child.html': '{% extends "master.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 %}', |
|
||||
'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': u'fehlend', |
|
||||
'watch out': u'pass auf', |
|
||||
'One user online': u'Ein Benutzer online', |
|
||||
'%(user_count)s users online': u'%(user_count)s Benutzer online', |
|
||||
'User: %(num)s': u'Benutzer: %(num)s', |
|
||||
'User: %(count)s': u'Benutzer: %(count)s', |
|
||||
'%(num)s apple': u'%(num)s Apfel', |
|
||||
'%(num)s apples': u'%(num)s Äpfel' |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
@contextfunction |
|
||||
def gettext(context, string): |
|
||||
language = context.get('LANGUAGE', 'en') |
|
||||
return languages.get(language, {}).get(string, string) |
|
||||
|
|
||||
|
|
||||
@contextfunction |
|
||||
def ngettext(context, s, p, n): |
|
||||
language = context.get('LANGUAGE', 'en') |
|
||||
if n != 1: |
|
||||
return languages.get(language, {}).get(p, p) |
|
||||
return languages.get(language, {}).get(s, s) |
|
||||
|
|
||||
|
|
||||
i18n_env = Environment( |
|
||||
loader=DictLoader(i18n_templates), |
|
||||
extensions=['jinja2.ext.i18n'] |
|
||||
) |
|
||||
i18n_env.globals.update({ |
|
||||
'_': gettext, |
|
||||
'gettext': gettext, |
|
||||
'ngettext': ngettext |
|
||||
}) |
|
||||
|
|
||||
newstyle_i18n_env = Environment( |
|
||||
loader=DictLoader(newstyle_i18n_templates), |
|
||||
extensions=['jinja2.ext.i18n'] |
|
||||
) |
|
||||
newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True) |
|
||||
|
|
||||
|
|
||||
class TestExtension(Extension): |
|
||||
tags = set(['test']) |
|
||||
ext_attr = 42 |
|
||||
|
|
||||
def parse(self, parser): |
|
||||
return nodes.Output([self.call_method('_dump', [ |
|
||||
nodes.EnvironmentAttribute('sandboxed'), |
|
||||
self.attr('ext_attr'), |
|
||||
nodes.ImportedName(__name__ + '.importable_object'), |
|
||||
nodes.ContextReference() |
|
||||
])]).set_lineno(next(parser.stream).lineno) |
|
||||
|
|
||||
def _dump(self, sandboxed, ext_attr, imported_object, context): |
|
||||
return '%s|%s|%s|%s' % ( |
|
||||
sandboxed, |
|
||||
ext_attr, |
|
||||
imported_object, |
|
||||
context.blocks |
|
||||
) |
|
||||
|
|
||||
|
|
||||
class PreprocessorExtension(Extension): |
|
||||
|
|
||||
def preprocess(self, source, name, filename=None): |
|
||||
return source.replace('[[TEST]]', '({{ foo }})') |
|
||||
|
|
||||
|
|
||||
class StreamFilterExtension(Extension): |
|
||||
|
|
||||
def filter_stream(self, stream): |
|
||||
for token in stream: |
|
||||
if token.type == 'data': |
|
||||
for t in self.interpolate(token): |
|
||||
yield t |
|
||||
else: |
|
||||
yield token |
|
||||
|
|
||||
def interpolate(self, token): |
|
||||
pos = 0 |
|
||||
end = len(token.value) |
|
||||
lineno = token.lineno |
|
||||
while 1: |
|
||||
match = _gettext_re.search(token.value, pos) |
|
||||
if match is None: |
|
||||
break |
|
||||
value = token.value[pos:match.start()] |
|
||||
if 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) |
|
||||
pos = match.end() |
|
||||
if pos < end: |
|
||||
yield Token(lineno, 'data', token.value[pos:]) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.ext |
|
||||
class TestExtensions(): |
|
||||
|
|
||||
def test_extend_late(self): |
|
||||
env = Environment() |
|
||||
env.add_extension('jinja2.ext.autoescape') |
|
||||
t = env.from_string( |
|
||||
'{% autoescape true %}{{ "<test>" }}{% endautoescape %}') |
|
||||
assert t.render() == '<test>' |
|
||||
|
|
||||
def test_loop_controls(self): |
|
||||
env = Environment(extensions=['jinja2.ext.loopcontrols']) |
|
||||
|
|
||||
tmpl = env.from_string(''' |
|
||||
{%- for item in [1, 2, 3, 4] %} |
|
||||
{%- if item % 2 == 0 %}{% continue %}{% endif -%} |
|
||||
{{ item }} |
|
||||
{%- endfor %}''') |
|
||||
assert tmpl.render() == '13' |
|
||||
|
|
||||
tmpl = env.from_string(''' |
|
||||
{%- for item in [1, 2, 3, 4] %} |
|
||||
{%- if item > 2 %}{% break %}{% endif -%} |
|
||||
{{ item }} |
|
||||
{%- endfor %}''') |
|
||||
assert tmpl.render() == '12' |
|
||||
|
|
||||
def test_do(self): |
|
||||
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' |
|
||||
|
|
||||
def test_with(self): |
|
||||
env = Environment(extensions=['jinja2.ext.with_']) |
|
||||
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'] |
|
||||
|
|
||||
def test_extension_nodes(self): |
|
||||
env = Environment(extensions=[TestExtension]) |
|
||||
tmpl = env.from_string('{% test %}') |
|
||||
assert tmpl.render() == 'False|42|23|{}' |
|
||||
|
|
||||
def test_identifier(self): |
|
||||
assert TestExtension.identifier == __name__ + '.TestExtension' |
|
||||
|
|
||||
def test_rebinding(self): |
|
||||
original = Environment(extensions=[TestExtension]) |
|
||||
overlay = original.overlay() |
|
||||
for env in original, overlay: |
|
||||
for ext in itervalues(env.extensions): |
|
||||
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)}' |
|
||||
|
|
||||
def test_streamfilter_extension(self): |
|
||||
env = Environment(extensions=[StreamFilterExtension]) |
|
||||
env.globals['gettext'] = lambda x: x.upper() |
|
||||
tmpl = env.from_string('Foo _(bar) Baz') |
|
||||
out = tmpl.render() |
|
||||
assert out == 'Foo BAR Baz' |
|
||||
|
|
||||
def test_extension_ordering(self): |
|
||||
class T1(Extension): |
|
||||
priority = 1 |
|
||||
|
|
||||
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 |
|
||||
|
|
||||
|
|
||||
@pytest.mark.ext |
|
||||
class TestInternationalization(): |
|
||||
|
|
||||
def test_trans(self): |
|
||||
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' |
|
||||
|
|
||||
def test_trans_plural_with_functions(self): |
|
||||
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 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 %}') |
|
||||
|
|
||||
def test_trans_stringformatting(self): |
|
||||
tmpl = i18n_env.get_template('stringformat.html') |
|
||||
assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5' |
|
||||
|
|
||||
def test_extract(self): |
|
||||
from jinja2.ext import babel_extract |
|
||||
source = BytesIO(''' |
|
||||
{{ gettext('Hello World') }} |
|
||||
{% trans %}Hello World{% endtrans %} |
|
||||
{% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} |
|
||||
'''.encode('ascii')) # make python 3 happy |
|
||||
assert list(babel_extract(source, |
|
||||
('gettext', 'ngettext', '_'), [], {})) == [ |
|
||||
(2, 'gettext', u'Hello World', []), |
|
||||
(3, 'gettext', u'Hello World', []), |
|
||||
(4, 'ngettext', (u'%(users)s user', u'%(users)s users', None), []) |
|
||||
] |
|
||||
|
|
||||
def test_comment_extract(self): |
|
||||
from jinja2.ext import babel_extract |
|
||||
source = BytesIO(''' |
|
||||
{# trans first #} |
|
||||
{{ gettext('Hello World') }} |
|
||||
{% trans %}Hello World{% endtrans %}{# trans second #} |
|
||||
{#: third #} |
|
||||
{% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} |
|
||||
'''.encode('utf-8')) # make python 3 happy |
|
||||
assert list(babel_extract(source, |
|
||||
('gettext', 'ngettext', '_'), |
|
||||
['trans', ':'], {})) == [ |
|
||||
(3, 'gettext', u'Hello World', ['first']), |
|
||||
(4, 'gettext', u'Hello World', ['second']), |
|
||||
(6, 'ngettext', (u'%(users)s user', u'%(users)s users', None), |
|
||||
['third']) |
|
||||
] |
|
||||
|
|
||||
|
|
||||
@pytest.mark.ext |
|
||||
class TestNewstyleInternationalization(): |
|
||||
|
|
||||
def test_trans(self): |
|
||||
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' |
|
||||
|
|
||||
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 %}') |
|
||||
|
|
||||
def test_trans_stringformatting(self): |
|
||||
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) == u'5 Äpfel' |
|
||||
|
|
||||
def test_autoescape_support(self): |
|
||||
env = Environment(extensions=['jinja2.ext.autoescape', |
|
||||
'jinja2.ext.i18n']) |
|
||||
env.install_gettext_callables( |
|
||||
lambda x: u'<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_num_used_twice(self): |
|
||||
tmpl = newstyle_i18n_env.get_template('ngettext_long.html') |
|
||||
assert tmpl.render(apples=5, LANGUAGE='de') == u'5 Äpfel' |
|
||||
|
|
||||
def test_num_called_num(self): |
|
||||
source = newstyle_i18n_env.compile(''' |
|
||||
{% trans num=3 %}{{ num }} apple{% pluralize |
|
||||
%}{{ num }} apples{% endtrans %} |
|
||||
''', 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"l_ngettext, u?'\%\(num\)s apple', u?'\%\(num\)s " |
|
||||
r"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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.ext |
|
||||
class TestAutoEscape(): |
|
||||
|
|
||||
def test_scoped_setting(self): |
|
||||
env = Environment(extensions=['jinja2.ext.autoescape'], |
|
||||
autoescape=True) |
|
||||
tmpl = env.from_string(''' |
|
||||
{{ "<HelloWorld>" }} |
|
||||
{% autoescape false %} |
|
||||
{{ "<HelloWorld>" }} |
|
||||
{% endautoescape %} |
|
||||
{{ "<HelloWorld>" }} |
|
||||
''') |
|
||||
assert tmpl.render().split() == \ |
|
||||
[u'<HelloWorld>', u'<HelloWorld>', u'<HelloWorld>'] |
|
||||
|
|
||||
env = Environment(extensions=['jinja2.ext.autoescape'], |
|
||||
autoescape=False) |
|
||||
tmpl = env.from_string(''' |
|
||||
{{ "<HelloWorld>" }} |
|
||||
{% autoescape true %} |
|
||||
{{ "<HelloWorld>" }} |
|
||||
{% endautoescape %} |
|
||||
{{ "<HelloWorld>" }} |
|
||||
''') |
|
||||
assert tmpl.render().split() == \ |
|
||||
[u'<HelloWorld>', u'<HelloWorld>', u'<HelloWorld>'] |
|
||||
|
|
||||
def test_nonvolatile(self): |
|
||||
env = Environment(extensions=['jinja2.ext.autoescape'], |
|
||||
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;"' |
|
||||
|
|
||||
def test_volatile(self): |
|
||||
env = Environment(extensions=['jinja2.ext.autoescape'], |
|
||||
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(extensions=['jinja2.ext.autoescape']) |
|
||||
tmpl = env.from_string( |
|
||||
'{% autoescape true %}{% set x = "<x>" %}{{ x }}' |
|
||||
'{% endautoescape %}{{ x }}{{ "<y>" }}') |
|
||||
assert tmpl.render(x=1) == '<x>1<y>' |
|
||||
|
|
||||
def test_volatile_scoping(self): |
|
||||
env = Environment(extensions=['jinja2.ext.autoescape']) |
|
||||
tmplsource = ''' |
|
||||
{% autoescape val %} |
|
||||
{% macro foo(x) %} |
|
||||
[{{ x }}] |
|
||||
{% endmacro %} |
|
||||
{{ 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] == text_type.__name__ |
|
||||
|
|
||||
# looking at the source we should see <testing> there in raw |
|
||||
# (and then escaped as well) |
|
||||
env = Environment(extensions=['jinja2.ext.autoescape']) |
|
||||
pysource = env.compile(tmplsource, raw=True) |
|
||||
assert '<testing>\\n' in pysource |
|
||||
|
|
||||
env = Environment(extensions=['jinja2.ext.autoescape'], |
|
||||
autoescape=True) |
|
||||
pysource = env.compile(tmplsource, raw=True) |
|
||||
assert '<testing>\\n' in pysource |
|
@ -1,558 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.filters |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests for the jinja filters. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
from jinja2 import Markup, Environment |
|
||||
from jinja2._compat import text_type, implements_to_string |
|
||||
|
|
||||
|
|
||||
@pytest.mark.filter |
|
||||
class TestFilter(): |
|
||||
|
|
||||
def test_filter_calling(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_center(self, env): |
|
||||
tmpl = env.from_string('{{ "foo"|center(9) }}') |
|
||||
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' |
|
||||
|
|
||||
def test_dictsort(self, env): |
|
||||
tmpl = env.from_string( |
|
||||
'{{ foo|dictsort }}|' |
|
||||
'{{ foo|dictsort(true) }}|' |
|
||||
'{{ foo|dictsort(false, "value") }}' |
|
||||
) |
|
||||
out = tmpl.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3}) |
|
||||
assert out == ("[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]|" |
|
||||
"[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]|" |
|
||||
"[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]") |
|
||||
|
|
||||
def test_batch(self, env): |
|
||||
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']]") |
|
||||
|
|
||||
def test_slice(self, env): |
|
||||
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']]") |
|
||||
|
|
||||
def test_escape(self, env): |
|
||||
tmpl = env.from_string('''{{ '<">&'|escape }}''') |
|
||||
out = tmpl.render() |
|
||||
assert out == '<">&' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
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) }}' |
|
||||
) |
|
||||
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' |
|
||||
) |
|
||||
|
|
||||
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) }}' |
|
||||
) |
|
||||
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' |
|
||||
) |
|
||||
|
|
||||
def test_first(self, env): |
|
||||
tmpl = env.from_string('{{ foo|first }}') |
|
||||
out = tmpl.render(foo=list(range(10))) |
|
||||
assert out == '0' |
|
||||
|
|
||||
def test_float(self, env): |
|
||||
tmpl = env.from_string('{{ "42"|float }}|' |
|
||||
'{{ "ajsghasjgd"|float }}|' |
|
||||
'{{ "32.32"|float }}') |
|
||||
out = tmpl.render() |
|
||||
assert out == '42.0|0.0|32.32' |
|
||||
|
|
||||
def test_format(self, env): |
|
||||
tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''') |
|
||||
out = tmpl.render() |
|
||||
assert out == 'a|b' |
|
||||
|
|
||||
def test_indent(self, env): |
|
||||
tmpl = env.from_string('{{ foo|indent(2) }}|{{ foo|indent(2, true) }}') |
|
||||
text = '\n'.join([' '.join(['foo', 'bar'] * 2)] * 2) |
|
||||
out = tmpl.render(foo=text) |
|
||||
assert out == ('foo bar foo bar\n foo bar foo bar| ' |
|
||||
'foo bar foo bar\n foo bar foo bar') |
|
||||
|
|
||||
def test_int(self, env): |
|
||||
tmpl = env.from_string('{{ "42"|int }}|{{ "ajsghasjgd"|int }}|' |
|
||||
'{{ "32.32"|int }}|{{ "0x4d32"|int(0, 16) }}|' |
|
||||
'{{ "011"|int(0, 8)}}|{{ "0x33FU"|int(0, 16) }}') |
|
||||
out = tmpl.render() |
|
||||
assert out == '42|0|32|19762|9|0' |
|
||||
|
|
||||
def test_join(self, env): |
|
||||
tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}') |
|
||||
out = tmpl.render() |
|
||||
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>' |
|
||||
|
|
||||
def test_join_attribute(self, env): |
|
||||
class User(object): |
|
||||
def __init__(self, username): |
|
||||
self.username = 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 }}''') |
|
||||
out = tmpl.render(foo=list(range(10))) |
|
||||
assert out == '9' |
|
||||
|
|
||||
def test_length(self, env): |
|
||||
tmpl = env.from_string('''{{ "hello world"|length }}''') |
|
||||
out = tmpl.render() |
|
||||
assert out == '11' |
|
||||
|
|
||||
def test_lower(self, env): |
|
||||
tmpl = env.from_string('''{{ "FOO"|lower }}''') |
|
||||
out = tmpl.render() |
|
||||
assert out == 'foo' |
|
||||
|
|
||||
def test_pprint(self, env): |
|
||||
from pprint import pformat |
|
||||
tmpl = env.from_string('''{{ data|pprint }}''') |
|
||||
data = list(range(1000)) |
|
||||
assert tmpl.render(data=data) == pformat(data) |
|
||||
|
|
||||
def test_random(self, env): |
|
||||
tmpl = env.from_string('''{{ seq|random }}''') |
|
||||
seq = list(range(100)) |
|
||||
for _ in range(10): |
|
||||
assert int(tmpl.render(seq=seq)) in seq |
|
||||
|
|
||||
def test_reverse(self, env): |
|
||||
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) == text_type(x) |
|
||||
|
|
||||
def test_title(self, env): |
|
||||
tmpl = env.from_string('''{{ "foo bar"|title }}''') |
|
||||
assert tmpl.render() == "Foo Bar" |
|
||||
tmpl = env.from_string('''{{ "foo's bar"|title }}''') |
|
||||
assert tmpl.render() == "Foo's Bar" |
|
||||
tmpl = env.from_string('''{{ "foo bar"|title }}''') |
|
||||
assert tmpl.render() == "Foo Bar" |
|
||||
tmpl = env.from_string('''{{ "f bar f"|title }}''') |
|
||||
assert tmpl.render() == "F Bar F" |
|
||||
tmpl = env.from_string('''{{ "foo-bar"|title }}''') |
|
||||
assert tmpl.render() == "Foo-Bar" |
|
||||
tmpl = env.from_string('''{{ "foo\tbar"|title }}''') |
|
||||
assert tmpl.render() == "Foo\tBar" |
|
||||
tmpl = env.from_string('''{{ "FOO\tBAR"|title }}''') |
|
||||
assert tmpl.render() == "Foo\tBar" |
|
||||
|
|
||||
class Foo: |
|
||||
def __str__(self): |
|
||||
return 'foo-bar' |
|
||||
|
|
||||
tmpl = env.from_string('''{{ data|title }}''') |
|
||||
out = tmpl.render(data=Foo()) |
|
||||
assert out == 'Foo-Bar' |
|
||||
|
|
||||
def test_truncate(self, env): |
|
||||
tmpl = env.from_string( |
|
||||
'{{ data|truncate(15, true, ">>>") }}|' |
|
||||
'{{ data|truncate(15, false, ">>>") }}|' |
|
||||
'{{ smalldata|truncate(15) }}' |
|
||||
) |
|
||||
out = tmpl.render(data='foobar baz bar' * 1000, |
|
||||
smalldata='foobar baz bar') |
|
||||
msg = 'Current output: %s' % out |
|
||||
assert out == 'foobar baz b>>>|foobar baz >>>|foobar baz bar', msg |
|
||||
|
|
||||
def test_truncate_very_short(self, env): |
|
||||
tmpl = env.from_string( |
|
||||
'{{ "foo bar baz"|truncate(9) }}|' |
|
||||
'{{ "foo bar baz"|truncate(9, true) }}' |
|
||||
) |
|
||||
out = tmpl.render() |
|
||||
assert out == 'foo ...|foo ba...', out |
|
||||
|
|
||||
def test_truncate_end_length(self, env): |
|
||||
tmpl = env.from_string('{{ "Joel is a slug"|truncate(9, true) }}') |
|
||||
out = tmpl.render() |
|
||||
assert out == 'Joel i...', 'Current output: %s' % out |
|
||||
|
|
||||
def test_upper(self, env): |
|
||||
tmpl = env.from_string('{{ "foo"|upper }}') |
|
||||
assert tmpl.render() == 'FOO' |
|
||||
|
|
||||
def test_urlize(self, env): |
|
||||
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' |
|
||||
|
|
||||
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/" target="_blank">'\ |
|
||||
'http://www.example.com/</a> bar' |
|
||||
tmpl = env.from_string( |
|
||||
'{{ "foo http://www.example.com/ bar"|urlize(target=42) }}' |
|
||||
) |
|
||||
assert tmpl.render() == 'foo <a href="http://www.example.com/">'\ |
|
||||
'http://www.example.com/</a> bar' |
|
||||
|
|
||||
def test_wordcount(self, env): |
|
||||
tmpl = env.from_string('{{ "foo bar baz"|wordcount }}') |
|
||||
assert tmpl.render() == '3' |
|
||||
|
|
||||
def test_block(self, env): |
|
||||
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>' |
|
||||
|
|
||||
def test_sum(self, env): |
|
||||
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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_abs(self, env): |
|
||||
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() |
|
||||
|
|
||||
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() |
|
||||
|
|
||||
def test_xmlattr(self, env): |
|
||||
tmpl = env.from_string( |
|
||||
"{{ {'foo': 42, 'bar': 23, 'fish': none, " |
|
||||
"'spam': missing, 'blub:blub': '<?>'}|xmlattr }}") |
|
||||
out = tmpl.render().split() |
|
||||
assert len(out) == 3 |
|
||||
assert 'foo="42"' in out |
|
||||
assert 'bar="23"' in out |
|
||||
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]' |
|
||||
|
|
||||
def test_sort2(self, env): |
|
||||
tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}') |
|
||||
assert tmpl.render() == 'AbcD' |
|
||||
|
|
||||
def test_sort3(self, env): |
|
||||
tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''') |
|
||||
assert tmpl.render() == "['Bar', 'blah', 'foo']" |
|
||||
|
|
||||
def test_sort4(self, env): |
|
||||
@implements_to_string |
|
||||
class Magic(object): |
|
||||
def __init__(self, value): |
|
||||
self.value = value |
|
||||
|
|
||||
def __str__(self): |
|
||||
return text_type(self.value) |
|
||||
tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''') |
|
||||
assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234' |
|
||||
|
|
||||
def test_groupby(self, env): |
|
||||
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", |
|
||||
"" |
|
||||
] |
|
||||
|
|
||||
def test_groupby_tuple_index(self, env): |
|
||||
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|' |
|
||||
|
|
||||
def test_groupby_multidot(self, env): |
|
||||
class Date(object): |
|
||||
def __init__(self, day, month, year): |
|
||||
self.day = day |
|
||||
self.month = month |
|
||||
self.year = year |
|
||||
|
|
||||
class Article(object): |
|
||||
def __init__(self, title, *date): |
|
||||
self.date = Date(*date) |
|
||||
self.title = title |
|
||||
articles = [ |
|
||||
Article('aha', 1, 1, 1970), |
|
||||
Article('interesting', 2, 1, 1970), |
|
||||
Article('really?', 3, 1, 1970), |
|
||||
Article('totally not', 1, 1, 1971) |
|
||||
] |
|
||||
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]', |
|
||||
'' |
|
||||
] |
|
||||
|
|
||||
def test_filtertag(self, env): |
|
||||
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>' |
|
||||
env = Environment(autoescape=True) |
|
||||
tmpl = env.from_string('{{ string|replace("o", 42) }}') |
|
||||
assert tmpl.render(string='<foo>') == '<f4242>' |
|
||||
tmpl = env.from_string('{{ string|replace("<", 42) }}') |
|
||||
assert tmpl.render(string='<foo>') == '42foo>' |
|
||||
tmpl = env.from_string('{{ string|replace("o", ">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 />')) == u'<div />' |
|
||||
|
|
||||
def test_safe(self, env): |
|
||||
env = Environment(autoescape=True) |
|
||||
tmpl = env.from_string('{{ "<div>foo</div>"|safe }}') |
|
||||
assert tmpl.render() == '<div>foo</div>' |
|
||||
tmpl = env.from_string('{{ "<div>foo</div>" }}') |
|
||||
assert tmpl.render() == '<div>foo</div>' |
|
||||
|
|
||||
def test_urlencode(self, env): |
|
||||
env = Environment(autoescape=True) |
|
||||
tmpl = env.from_string('{{ "Hello, world!"|urlencode }}') |
|
||||
assert tmpl.render() == 'Hello%2C%20world%21' |
|
||||
tmpl = env.from_string('{{ o|urlencode }}') |
|
||||
assert tmpl.render(o=u"Hello, world\u203d") \ |
|
||||
== "Hello%2C%20world%E2%80%BD" |
|
||||
assert tmpl.render(o=(("f", 1),)) == "f=1" |
|
||||
assert tmpl.render(o=(('f', 1), ("z", 2))) == "f=1&z=2" |
|
||||
assert tmpl.render(o=((u"\u203d", 1),)) == "%E2%80%BD=1" |
|
||||
assert tmpl.render(o={u"\u203d": 1}) == "%E2%80%BD=1" |
|
||||
assert tmpl.render(o={0: 1}) == "0=1" |
|
||||
|
|
||||
def test_simple_map(self, env): |
|
||||
env = Environment() |
|
||||
tmpl = env.from_string('{{ ["1", "2", "3"]|map("int")|sum }}') |
|
||||
assert tmpl.render() == '6' |
|
||||
|
|
||||
def test_attribute_map(self, env): |
|
||||
class User(object): |
|
||||
def __init__(self, name): |
|
||||
self.name = name |
|
||||
env = Environment() |
|
||||
users = [ |
|
||||
User('john'), |
|
||||
User('jane'), |
|
||||
User('mike'), |
|
||||
] |
|
||||
tmpl = env.from_string('{{ users|map(attribute="name")|join("|") }}') |
|
||||
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_simple_select(self, env): |
|
||||
env = Environment() |
|
||||
tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|select("odd")|join("|") }}') |
|
||||
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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_simple_select_attr(self, env): |
|
||||
class User(object): |
|
||||
def __init__(self, name, is_active): |
|
||||
self.name = name |
|
||||
self.is_active = is_active |
|
||||
env = Environment() |
|
||||
users = [ |
|
||||
User('john', True), |
|
||||
User('jane', True), |
|
||||
User('mike', False), |
|
||||
] |
|
||||
tmpl = env.from_string( |
|
||||
'{{ users|selectattr("is_active")|' |
|
||||
'map(attribute="name")|join("|") }}' |
|
||||
) |
|
||||
assert tmpl.render(users=users) == 'john|jane' |
|
||||
|
|
||||
def test_simple_reject_attr(self, env): |
|
||||
class User(object): |
|
||||
def __init__(self, name, is_active): |
|
||||
self.name = name |
|
||||
self.is_active = is_active |
|
||||
env = Environment() |
|
||||
users = [ |
|
||||
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' |
|
||||
|
|
||||
def test_func_select_attr(self, env): |
|
||||
class User(object): |
|
||||
def __init__(self, id, name): |
|
||||
self.id = id |
|
||||
self.name = name |
|
||||
env = Environment() |
|
||||
users = [ |
|
||||
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' |
|
||||
|
|
||||
def test_func_reject_attr(self, env): |
|
||||
class User(object): |
|
||||
def __init__(self, id, name): |
|
||||
self.id = id |
|
||||
self.name = name |
|
||||
env = Environment() |
|
||||
users = [ |
|
||||
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' |
|
@ -1,148 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.imports |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests the import features (with includes). |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Environment, DictLoader |
|
||||
from jinja2.exceptions import TemplateNotFound, TemplatesNotFound |
|
||||
|
|
||||
|
|
||||
@pytest.fixture |
|
||||
def test_env(): |
|
||||
env = Environment(loader=DictLoader(dict( |
|
||||
module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}', |
|
||||
header='[{{ foo }}|{{ 23 }}]', |
|
||||
o_printer='({{ o }})' |
|
||||
))) |
|
||||
env.globals['bar'] = 23 |
|
||||
return env |
|
||||
|
|
||||
|
|
||||
@pytest.mark.imports |
|
||||
class TestImports(): |
|
||||
|
|
||||
def test_context_imports(self, test_env): |
|
||||
t = test_env.from_string('{% import "module" as m %}{{ m.test() }}') |
|
||||
assert t.render(foo=42) == '[|23]' |
|
||||
t = test_env.from_string( |
|
||||
'{% import "module" as m without context %}{{ m.test() }}' |
|
||||
) |
|
||||
assert t.render(foo=42) == '[|23]' |
|
||||
t = test_env.from_string( |
|
||||
'{% import "module" as m with context %}{{ m.test() }}' |
|
||||
) |
|
||||
assert t.render(foo=42) == '[42|23]' |
|
||||
t = test_env.from_string('{% from "module" import test %}{{ test() }}') |
|
||||
assert t.render(foo=42) == '[|23]' |
|
||||
t = test_env.from_string( |
|
||||
'{% from "module" import test without context %}{{ test() }}' |
|
||||
) |
|
||||
assert t.render(foo=42) == '[|23]' |
|
||||
t = test_env.from_string( |
|
||||
'{% from "module" import test with context %}{{ test() }}' |
|
||||
) |
|
||||
assert t.render(foo=42) == '[42|23]' |
|
||||
|
|
||||
def test_trailing_comma(self, test_env): |
|
||||
test_env.from_string('{% from "foo" import bar, baz with context %}') |
|
||||
test_env.from_string('{% from "foo" import bar, baz, with context %}') |
|
||||
test_env.from_string('{% from "foo" import bar, with context %}') |
|
||||
test_env.from_string('{% from "foo" import bar, with, context %}') |
|
||||
test_env.from_string('{% from "foo" import bar, with with context %}') |
|
||||
|
|
||||
def test_exports(self, test_env): |
|
||||
m = test_env.from_string(''' |
|
||||
{% macro toplevel() %}...{% endmacro %} |
|
||||
{% macro __private() %}...{% endmacro %} |
|
||||
{% set variable = 42 %} |
|
||||
{% for item in [1] %} |
|
||||
{% macro notthere() %}{% endmacro %} |
|
||||
{% endfor %} |
|
||||
''').module |
|
||||
assert m.toplevel() == '...' |
|
||||
assert not hasattr(m, '__missing') |
|
||||
assert m.variable == 42 |
|
||||
assert not hasattr(m, 'notthere') |
|
||||
|
|
||||
|
|
||||
@pytest.mark.imports |
|
||||
@pytest.mark.includes |
|
||||
class TestIncludes(): |
|
||||
|
|
||||
def test_context_include(self, test_env): |
|
||||
t = test_env.from_string('{% include "header" %}') |
|
||||
assert t.render(foo=42) == '[42|23]' |
|
||||
t = test_env.from_string('{% include "header" with context %}') |
|
||||
assert t.render(foo=42) == '[42|23]' |
|
||||
t = test_env.from_string('{% include "header" without context %}') |
|
||||
assert t.render(foo=42) == '[|23]' |
|
||||
|
|
||||
def test_choice_includes(self, test_env): |
|
||||
t = test_env.from_string('{% include ["missing", "header"] %}') |
|
||||
assert t.render(foo=42) == '[42|23]' |
|
||||
|
|
||||
t = test_env.from_string( |
|
||||
'{% include ["missing", "missing2"] ignore missing %}' |
|
||||
) |
|
||||
assert t.render(foo=42) == '' |
|
||||
|
|
||||
t = test_env.from_string('{% include ["missing", "missing2"] %}') |
|
||||
pytest.raises(TemplateNotFound, t.render) |
|
||||
try: |
|
||||
t.render() |
|
||||
except TemplatesNotFound as e: |
|
||||
assert e.templates == ['missing', 'missing2'] |
|
||||
assert e.name == 'missing2' |
|
||||
else: |
|
||||
assert False, 'thou shalt raise' |
|
||||
|
|
||||
def test_includes(t, **ctx): |
|
||||
ctx['foo'] = 42 |
|
||||
assert t.render(ctx) == '[42|23]' |
|
||||
|
|
||||
t = test_env.from_string('{% include ["missing", "header"] %}') |
|
||||
test_includes(t) |
|
||||
t = test_env.from_string('{% include x %}') |
|
||||
test_includes(t, x=['missing', 'header']) |
|
||||
t = test_env.from_string('{% include [x, "header"] %}') |
|
||||
test_includes(t, x='missing') |
|
||||
t = test_env.from_string('{% include x %}') |
|
||||
test_includes(t, x='header') |
|
||||
t = test_env.from_string('{% include x %}') |
|
||||
test_includes(t, x='header') |
|
||||
t = test_env.from_string('{% include [x] %}') |
|
||||
test_includes(t, x='header') |
|
||||
|
|
||||
def test_include_ignoring_missing(self, test_env): |
|
||||
t = test_env.from_string('{% include "missing" %}') |
|
||||
pytest.raises(TemplateNotFound, t.render) |
|
||||
for extra in '', 'with context', 'without context': |
|
||||
t = test_env.from_string('{% include "missing" ignore missing ' + |
|
||||
extra + ' %}') |
|
||||
assert t.render() == '' |
|
||||
|
|
||||
def test_context_include_with_overrides(self, test_env): |
|
||||
env = Environment(loader=DictLoader(dict( |
|
||||
main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}", |
|
||||
item="{{ item }}" |
|
||||
))) |
|
||||
assert env.get_template("main").render() == "123" |
|
||||
|
|
||||
def test_unoptimized_scopes(self, test_env): |
|
||||
t = test_env.from_string(""" |
|
||||
{% macro outer(o) %} |
|
||||
{% macro inner() %} |
|
||||
{% include "o_printer" %} |
|
||||
{% endmacro %} |
|
||||
{{ inner() }} |
|
||||
{% endmacro %} |
|
||||
{{ outer("FOO") }} |
|
||||
""") |
|
||||
assert t.render().strip() == '(FOO)' |
|
@ -1,248 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.inheritance |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests the template inheritance feature. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Environment, DictLoader, TemplateError |
|
||||
|
|
||||
|
|
||||
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 %}|''' |
|
||||
|
|
||||
LEVEL1TEMPLATE = '''\ |
|
||||
{% extends "layout" %} |
|
||||
{% block block1 %}block 1 from level1{% endblock %}''' |
|
||||
|
|
||||
LEVEL2TEMPLATE = '''\ |
|
||||
{% extends "level1" %} |
|
||||
{% block block2 %}{% block block5 %}nested block 5 from level2{% |
|
||||
endblock %}{% endblock %}''' |
|
||||
|
|
||||
LEVEL3TEMPLATE = '''\ |
|
||||
{% extends "level2" %} |
|
||||
{% block block5 %}block 5 from level3{% endblock %} |
|
||||
{% block block4 %}block 4 from level3{% endblock %} |
|
||||
''' |
|
||||
|
|
||||
LEVEL4TEMPLATE = '''\ |
|
||||
{% extends "level3" %} |
|
||||
{% block block3 %}block 3 from level4{% endblock %} |
|
||||
''' |
|
||||
|
|
||||
WORKINGTEMPLATE = '''\ |
|
||||
{% extends "layout" %} |
|
||||
{% block block1 %} |
|
||||
{% if false %} |
|
||||
{% block block2 %} |
|
||||
this should workd |
|
||||
{% endblock %} |
|
||||
{% endif %} |
|
||||
{% endblock %} |
|
||||
''' |
|
||||
|
|
||||
DOUBLEEXTENDS = '''\ |
|
||||
{% extends "layout" %} |
|
||||
{% extends "layout" %} |
|
||||
{% block block1 %} |
|
||||
{% if false %} |
|
||||
{% block block2 %} |
|
||||
this should workd |
|
||||
{% 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) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.inheritance |
|
||||
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|') |
|
||||
|
|
||||
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|') |
|
||||
|
|
||||
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|') |
|
||||
|
|
||||
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|') |
|
||||
|
|
||||
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|') |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_working(self, env): |
|
||||
tmpl = 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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_dynamic_inheritance(self, env): |
|
||||
env = Environment(loader=DictLoader({ |
|
||||
'master1': 'MASTER1{% block x %}{% endblock %}', |
|
||||
'master2': 'MASTER2{% block x %}{% endblock %}', |
|
||||
'child': '{% extends master %}{% block x %}CHILD{% endblock %}' |
|
||||
})) |
|
||||
tmpl = env.get_template('child') |
|
||||
for m in range(1, 3): |
|
||||
assert tmpl.render(master='master%d' % m) == 'MASTER%dCHILD' % m |
|
||||
|
|
||||
def test_multi_inheritance(self, env): |
|
||||
env = Environment(loader=DictLoader({ |
|
||||
'master1': 'MASTER1{% block x %}{% endblock %}', |
|
||||
'master2': 'MASTER2{% block x %}{% endblock %}', |
|
||||
'child': |
|
||||
'''{% if master %}{% extends master %}{% else %}{% extends |
|
||||
'master1' %}{% endif %}{% block x %}CHILD{% endblock %}''' |
|
||||
})) |
|
||||
tmpl = env.get_template('child') |
|
||||
assert tmpl.render(master='master2') == 'MASTER2CHILD' |
|
||||
assert tmpl.render(master='master1') == 'MASTER1CHILD' |
|
||||
assert tmpl.render() == 'MASTER1CHILD' |
|
||||
|
|
||||
def test_scoped_block(self, env): |
|
||||
env = Environment(loader=DictLoader({ |
|
||||
'master.html': '{% for item in seq %}[{% block item scoped %}' |
|
||||
'{% endblock %}]{% endfor %}' |
|
||||
})) |
|
||||
t = env.from_string('{% extends "master.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({ |
|
||||
'master.html': '{% for item in seq %}[{% block item scoped %}' |
|
||||
'{{ item }}{% endblock %}]{% endfor %}' |
|
||||
})) |
|
||||
t = env.from_string('{% extends "master.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': ''' |
|
||||
{% block useless %}{% endblock %} |
|
||||
''', |
|
||||
'index.html': ''' |
|
||||
{%- extends 'layout.html' %} |
|
||||
{% from 'helpers.html' import foo with context %} |
|
||||
{% block useless %} |
|
||||
{% for x in [1, 2, 3] %} |
|
||||
{% block testing scoped %} |
|
||||
{{ foo(x) }} |
|
||||
{% endblock %} |
|
||||
{% endfor %} |
|
||||
{% endblock %} |
|
||||
''', |
|
||||
'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'] |
|
||||
|
|
||||
|
|
||||
@pytest.mark.inheritance |
|
||||
class TestBugFix(): |
|
||||
|
|
||||
def test_fixed_macro_scoping_bug(self, env): |
|
||||
assert Environment(loader=DictLoader({ |
|
||||
'test.html': '''\ |
|
||||
{% extends 'details.html' %} |
|
||||
|
|
||||
{% macro my_macro() %} |
|
||||
my_macro |
|
||||
{% endmacro %} |
|
||||
|
|
||||
{% block inner_box %} |
|
||||
{{ my_macro() }} |
|
||||
{% endblock %} |
|
||||
''', |
|
||||
'details.html': '''\ |
|
||||
{% extends 'standard.html' %} |
|
||||
|
|
||||
{% macro my_macro() %} |
|
||||
my_macro |
|
||||
{% endmacro %} |
|
||||
|
|
||||
{% block content %} |
|
||||
{% block outer_box %} |
|
||||
outer_box |
|
||||
{% block inner_box %} |
|
||||
inner_box |
|
||||
{% endblock %} |
|
||||
{% endblock %} |
|
||||
{% endblock %} |
|
||||
''', |
|
||||
'standard.html': ''' |
|
||||
{% block content %} {% endblock %} |
|
||||
''' |
|
||||
})).get_template("test.html").render().split() \ |
|
||||
== [u'outer_box', u'my_macro'] |
|
||||
|
|
||||
def test_double_extends(self, env): |
|
||||
"""Ensures that a template with more than 1 {% extends ... %} usage |
|
||||
raises a ``TemplateError``. |
|
||||
""" |
|
||||
try: |
|
||||
tmpl = env.get_template('doublee') |
|
||||
except Exception as e: |
|
||||
assert isinstance(e, TemplateError) |
|
@ -1,609 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.lexnparse |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
All the unittests regarding lexing, parsing and syntax. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Environment, Template, TemplateSyntaxError, \ |
|
||||
UndefinedError, nodes |
|
||||
from jinja2._compat import iteritems, text_type, PY2 |
|
||||
from jinja2.lexer import Token, TokenStream, TOKEN_EOF, \ |
|
||||
TOKEN_BLOCK_BEGIN, TOKEN_BLOCK_END |
|
||||
|
|
||||
|
|
||||
# how does a string look like in jinja syntax? |
|
||||
if PY2: |
|
||||
def jinja_string_repr(string): |
|
||||
return repr(string)[1:] |
|
||||
else: |
|
||||
jinja_string_repr = repr |
|
||||
|
|
||||
|
|
||||
@pytest.mark.lexnparse |
|
||||
@pytest.mark.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") |
|
||||
assert ts.current.type is TOKEN_BLOCK_BEGIN |
|
||||
assert bool(ts) |
|
||||
assert not bool(ts.eos) |
|
||||
next(ts) |
|
||||
assert ts.current.type is TOKEN_BLOCK_END |
|
||||
assert bool(ts) |
|
||||
assert not bool(ts.eos) |
|
||||
next(ts) |
|
||||
assert ts.current.type is TOKEN_EOF |
|
||||
assert not bool(ts) |
|
||||
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', ] |
|
||||
|
|
||||
|
|
||||
@pytest.mark.lexnparse |
|
||||
@pytest.mark.lexer |
|
||||
class TestLexer(): |
|
||||
|
|
||||
def test_raw1(self, env): |
|
||||
tmpl = env.from_string( |
|
||||
'{% 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_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}" |
|
||||
|
|
||||
def test_comments(self, env): |
|
||||
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>") |
|
||||
|
|
||||
def test_string_escapes(self, env): |
|
||||
for char in u'\0', u'\u2668', u'\xe4', u'\t', u'\r', u'\n': |
|
||||
tmpl = env.from_string('{{ %s }}' % jinja_string_repr(char)) |
|
||||
assert tmpl.render() == char |
|
||||
assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u'\u2668' |
|
||||
|
|
||||
def test_bytefallback(self, env): |
|
||||
from pprint import pformat |
|
||||
tmpl = env.from_string(u'''{{ 'foo'|pprint }}|{{ 'bär'|pprint }}''') |
|
||||
assert tmpl.render() == pformat('foo') + '|' + pformat(u'bär') |
|
||||
|
|
||||
def test_operators(self, env): |
|
||||
from jinja2.lexer import operators |
|
||||
for test, expect in iteritems(operators): |
|
||||
if test in '([{}])': |
|
||||
continue |
|
||||
stream = env.lexer.tokenize('{{ %s }}' % test) |
|
||||
next(stream) |
|
||||
assert stream.current.type == expect |
|
||||
|
|
||||
def test_normalizing(self, env): |
|
||||
for seq in '\r', '\r\n', '\n': |
|
||||
env = Environment(newline_sequence=seq) |
|
||||
tmpl = env.from_string('1\n2\r\n3\n4\n') |
|
||||
result = tmpl.render() |
|
||||
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'}), |
|
||||
]: |
|
||||
tmpl = env.from_string(template) |
|
||||
expect = expected.get(keep, template) |
|
||||
result = tmpl.render() |
|
||||
assert result == expect, (keep, template, result, expect) |
|
||||
|
|
||||
|
|
||||
@pytest.mark.lexnparse |
|
||||
@pytest.mark.parser |
|
||||
class TestParser(): |
|
||||
|
|
||||
def test_php_syntax(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_erb_syntax(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_comment_syntax(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_balancing(self, env): |
|
||||
tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''') |
|
||||
assert tmpl.render() == 'bar' |
|
||||
|
|
||||
def test_start_comment(self, env): |
|
||||
tmpl = env.from_string('''{# foo comment |
|
||||
and bar comment #} |
|
||||
{% macro blub() %}foo{% endmacro %} |
|
||||
{{ blub() }}''') |
|
||||
assert tmpl.render().strip() == 'foo' |
|
||||
|
|
||||
def test_line_syntax(self, env): |
|
||||
env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%') |
|
||||
tmpl = env.from_string('''\ |
|
||||
<%# regular comment %> |
|
||||
% for item in seq: |
|
||||
${item} |
|
||||
% endfor''') |
|
||||
assert [ |
|
||||
int(x.strip()) for x in tmpl.render(seq=list(range(5))).split() |
|
||||
] == list(range(5)) |
|
||||
|
|
||||
env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##') |
|
||||
tmpl = env.from_string('''\ |
|
||||
<%# regular comment %> |
|
||||
% for item in seq: |
|
||||
${item} ## the rest of the stuff |
|
||||
% 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('''\ |
|
||||
/* 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('''\ |
|
||||
/* 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' |
|
||||
|
|
||||
def test_error_messages(self, env): |
|
||||
def assert_error(code, expected): |
|
||||
try: |
|
||||
Template(code) |
|
||||
except TemplateSyntaxError as e: |
|
||||
assert str(e) == expected, 'unexpected error message' |
|
||||
else: |
|
||||
assert False, 'that was supposed to be an error' |
|
||||
|
|
||||
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 %}', |
|
||||
"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'.") |
|
||||
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 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'.") |
|
||||
|
|
||||
|
|
||||
@pytest.mark.lexnparse |
|
||||
@pytest.mark.syntax |
|
||||
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' |
|
||||
|
|
||||
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]' |
|
||||
|
|
||||
def test_attr(self, env): |
|
||||
tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}") |
|
||||
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' |
|
||||
|
|
||||
def test_tuple(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_div(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_concat(self, env): |
|
||||
tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}") |
|
||||
assert tmpl.render() == '[1, 2]foo' |
|
||||
|
|
||||
def test_compare(self, env): |
|
||||
tmpl = env.from_string('{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|' |
|
||||
'{{ 2 == 2 }}|{{ 1 <= 1 }}') |
|
||||
assert tmpl.render() == 'True|True|True|True|True' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_literals(self, env): |
|
||||
tmpl = env.from_string('{{ [] }}|{{ {} }}|{{ () }}') |
|
||||
assert tmpl.render().lower() == '[]|{}|()' |
|
||||
|
|
||||
def test_bool(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_django_attr(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_short_conditional_expression(self, env): |
|
||||
tmpl = env.from_string('<{{ 1 if false }}>') |
|
||||
assert tmpl.render() == '<>' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_function_calls(self, env): |
|
||||
tests = [ |
|
||||
(True, '*foo, bar'), |
|
||||
(True, '*foo, *bar'), |
|
||||
(True, '*foo, bar=42'), |
|
||||
(True, '**foo, *bar'), |
|
||||
(True, '**foo, bar'), |
|
||||
(False, 'foo, bar'), |
|
||||
(False, 'foo, bar=42'), |
|
||||
(False, 'foo, bar=23, *args'), |
|
||||
(False, 'a, b=c, *d, **e'), |
|
||||
(False, '*foo, **bar') |
|
||||
] |
|
||||
for should_fail, sig in tests: |
|
||||
if should_fail: |
|
||||
pytest.raises(TemplateSyntaxError, |
|
||||
env.from_string, '{{ foo(%s) }}' % sig) |
|
||||
else: |
|
||||
env.from_string('foo(%s)' % 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 %}' |
|
||||
]: |
|
||||
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}' |
|
||||
|
|
||||
def test_block_end_name(self, env): |
|
||||
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: |
|
||||
tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % ( |
|
||||
str(const), str(const).lower(), str(const).upper() |
|
||||
)) |
|
||||
assert tmpl.render() == '%s|%s|' % (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' |
|
||||
|
|
||||
def test_string_concatenation(self, env): |
|
||||
tmpl = env.from_string('{{ "foo" "bar" "baz" }}') |
|
||||
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) == text_type(not 42 in bar) |
|
||||
|
|
||||
def test_implicit_subscribed_tuple(self, env): |
|
||||
class Foo(object): |
|
||||
def __getitem__(self, x): |
|
||||
return x |
|
||||
t = env.from_string('{{ foo[1, 2] }}') |
|
||||
assert t.render(foo=Foo()) == u'(1, 2)' |
|
||||
|
|
||||
def test_raw2(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_neg_filter_priority(self, env): |
|
||||
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 %}''' |
|
||||
for tmpl in constass1, constass2: |
|
||||
pytest.raises(TemplateSyntaxError, env.from_string, tmpl) |
|
||||
|
|
||||
def test_localset(self, env): |
|
||||
tmpl = env.from_string('''{% set foo = 0 %}\ |
|
||||
{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\ |
|
||||
{{ foo }}''') |
|
||||
assert tmpl.render() == '0' |
|
||||
|
|
||||
def test_parse_unary(self, env): |
|
||||
tmpl = env.from_string('{{ -foo["bar"] }}') |
|
||||
assert tmpl.render(foo={'bar': 42}) == '-42' |
|
||||
tmpl = env.from_string('{{ -foo["bar"]|abs }}') |
|
||||
assert tmpl.render(foo={'bar': 42}) == '42' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.lexnparse |
|
||||
@pytest.mark.lstripblocks |
|
||||
class TestLstripBlocks(): |
|
||||
|
|
||||
def test_lstrip(self, env): |
|
||||
env = Environment(lstrip_blocks=True, trim_blocks=False) |
|
||||
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 %}''') |
|
||||
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 %}''') |
|
||||
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 %}''') |
|
||||
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 ' |
|
||||
|
|
||||
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 ' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
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 %} ' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_lstrip_comment(self, env): |
|
||||
env = Environment(lstrip_blocks=True, trim_blocks=False) |
|
||||
tmpl = env.from_string(''' {# if True #} |
|
||||
hello |
|
||||
{#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 ' |
|
||||
|
|
||||
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 ' |
|
||||
|
|
||||
def test_lstrip_angle_bracket(self, env): |
|
||||
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('%s\n' % x 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('''\ |
|
||||
<%#regular comment%> |
|
||||
<%for item in seq%> |
|
||||
${item} ## the rest of the stuff |
|
||||
<%endfor%>''') |
|
||||
assert tmpl.render(seq=range(5)) == \ |
|
||||
''.join('%s\n' % x for x in range(5)) |
|
||||
|
|
||||
def test_php_syntax_with_manual(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_php_syntax(self, env): |
|
||||
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(' %s\n' % x for x in range(5)) |
|
||||
|
|
||||
def test_php_syntax_compact(self, env): |
|
||||
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(' %s\n' % x for x in range(5)) |
|
||||
|
|
||||
def test_erb_syntax(self, env): |
|
||||
env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', |
|
||||
lstrip_blocks=True, trim_blocks=True) |
|
||||
# env.from_string('') |
|
||||
# for n,r in env.lexer.rules.iteritems(): |
|
||||
# print n |
|
||||
# print env.lexer.rules['root'][0][0].pattern |
|
||||
# print "'%s'" % tmpl.render(seq=range(5)) |
|
||||
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(' %s\n' % x 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('''\ |
|
||||
<%# I'm a comment, I'm not interesting %> |
|
||||
<% for item in seq -%> |
|
||||
<%= item %> |
|
||||
<%- 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('''\ |
|
||||
<%# I'm a comment, I'm not interesting %> |
|
||||
<%+ for item in seq -%> |
|
||||
<%= item %> |
|
||||
<%- 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('''\ |
|
||||
<!--# I'm a comment, I'm not interesting -->\ |
|
||||
<!-- for item in seq ---> |
|
||||
${item} |
|
||||
<!--- endfor -->''') |
|
||||
assert tmpl.render(seq=range(5)) == '01234' |
|
@ -1,220 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.loader |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Test the loaders. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import os |
|
||||
import sys |
|
||||
import tempfile |
|
||||
import shutil |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Environment, loaders |
|
||||
from jinja2._compat import PYPY, PY2 |
|
||||
from jinja2.loaders import split_template_path |
|
||||
from jinja2.exceptions import TemplateNotFound |
|
||||
|
|
||||
|
|
||||
@pytest.mark.loaders |
|
||||
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') |
|
||||
|
|
||||
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(self, filesystem_loader): |
|
||||
env = Environment(loader=filesystem_loader) |
|
||||
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_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') |
|
||||
|
|
||||
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') |
|
||||
|
|
||||
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') |
|
||||
|
|
||||
def test_caching(self): |
|
||||
changed = False |
|
||||
|
|
||||
class TestLoader(loaders.BaseLoader): |
|
||||
def get_source(self, environment, template): |
|
||||
return u'foo', None, lambda: not changed |
|
||||
env = Environment(loader=TestLoader(), cache_size=-1) |
|
||||
tmpl = env.get_template('template') |
|
||||
assert tmpl is env.get_template('template') |
|
||||
changed = True |
|
||||
assert tmpl is not env.get_template('template') |
|
||||
changed = False |
|
||||
|
|
||||
env = Environment(loader=TestLoader(), cache_size=0) |
|
||||
assert env.get_template('template') \ |
|
||||
is not env.get_template('template') |
|
||||
|
|
||||
env = Environment(loader=TestLoader(), 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') |
|
||||
t3 = env.get_template('three') |
|
||||
assert 'one' in env.cache |
|
||||
assert 'two' not in env.cache |
|
||||
assert 'three' in env.cache |
|
||||
|
|
||||
def test_dict_loader_cache_invalidates(self): |
|
||||
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" |
|
||||
|
|
||||
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') |
|
||||
|
|
||||
|
|
||||
@pytest.mark.loaders |
|
||||
@pytest.mark.moduleloader |
|
||||
class TestModuleLoader(): |
|
||||
archive = None |
|
||||
|
|
||||
def compile_down(self, prefix_loader, zip='deflated', py_compile=False): |
|
||||
log = [] |
|
||||
self.reg_env = Environment(loader=prefix_loader) |
|
||||
if zip is not None: |
|
||||
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, |
|
||||
py_compile=py_compile) |
|
||||
self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive)) |
|
||||
return ''.join(log) |
|
||||
|
|
||||
def teardown(self): |
|
||||
if hasattr(self, 'mod_env'): |
|
||||
if os.path.isfile(self.archive): |
|
||||
os.remove(self.archive) |
|
||||
else: |
|
||||
shutil.rmtree(self.archive) |
|
||||
self.archive = None |
|
||||
|
|
||||
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 |
|
||||
|
|
||||
def _test_common(self): |
|
||||
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') |
|
||||
assert tmpl1.render() == tmpl2.render() |
|
||||
|
|
||||
def test_deflated_zip_compile(self, prefix_loader): |
|
||||
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._test_common() |
|
||||
|
|
||||
def test_filesystem_compile(self, prefix_loader): |
|
||||
self.compile_down(prefix_loader, zip=None) |
|
||||
self._test_common() |
|
||||
|
|
||||
def test_weak_references(self, prefix_loader): |
|
||||
self.compile_down(prefix_loader) |
|
||||
tmpl = 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 = tmpl = None |
|
||||
|
|
||||
try: |
|
||||
import gc |
|
||||
gc.collect() |
|
||||
except: |
|
||||
pass |
|
||||
|
|
||||
assert name not in sys.modules |
|
||||
|
|
||||
# This test only makes sense on non-pypy python 2 |
|
||||
@pytest.mark.skipif( |
|
||||
not (PY2 and not PYPY), |
|
||||
reason='This test only makes sense on non-pypy python 2') |
|
||||
def test_byte_compilation(self, prefix_loader): |
|
||||
log = self.compile_down(prefix_loader, py_compile=True) |
|
||||
assert 'Byte-compiled "a/test.html"' in log |
|
||||
tmpl1 = self.mod_env.get_template('a/test.html') |
|
||||
mod = self.mod_env.loader.module. \ |
|
||||
tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490 |
|
||||
assert mod.__file__.endswith('.pyc') |
|
||||
|
|
||||
def test_choice_loader(self, prefix_loader): |
|
||||
log = 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): |
|
||||
log = 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' |
|
@ -1,278 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.regression |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests corner cases and bugs. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Template, Environment, DictLoader, TemplateSyntaxError, \ |
|
||||
TemplateNotFound, PrefixLoader |
|
||||
from jinja2._compat import text_type |
|
||||
|
|
||||
|
|
||||
@pytest.mark.regression |
|
||||
class TestCorner(): |
|
||||
|
|
||||
def test_assigned_scoping(self, env): |
|
||||
t = env.from_string(''' |
|
||||
{%- for item in (1, 2, 3, 4) -%} |
|
||||
[{{ item }}] |
|
||||
{%- endfor %} |
|
||||
{{- item -}} |
|
||||
''') |
|
||||
assert t.render(item=42) == '[1][2][3][4]42' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_closure_scoping(self, env): |
|
||||
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>' |
|
||||
|
|
||||
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>' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.regression |
|
||||
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' |
|
||||
|
|
||||
def test_extends_output_bugs(self, env): |
|
||||
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 %}' |
|
||||
) |
|
||||
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">'\ |
|
||||
'http://www.example.org/<foo</a>' |
|
||||
|
|
||||
def test_loop_call_loop(self, env): |
|
||||
tmpl = env.from_string(''' |
|
||||
|
|
||||
{% macro test() %} |
|
||||
{{ caller() }} |
|
||||
{% endmacro %} |
|
||||
|
|
||||
{% for num1 in range(5) %} |
|
||||
{% call test() %} |
|
||||
{% for num2 in range(10) %} |
|
||||
{{ loop.index }} |
|
||||
{% endfor %} |
|
||||
{% endcall %} |
|
||||
{% endfor %} |
|
||||
|
|
||||
''') |
|
||||
|
|
||||
assert tmpl.render().split() \ |
|
||||
== [text_type(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') |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
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' |
|
||||
|
|
||||
def test_stacked_locals_scoping_bug(self, env): |
|
||||
env = Environment(line_statement_prefix='#') |
|
||||
t = env.from_string('''\ |
|
||||
# for j in [1, 2]: |
|
||||
# set x = 1 |
|
||||
# for i in [1, 2]: |
|
||||
# print x |
|
||||
# if i % 2 == 0: |
|
||||
# set x = x + 1 |
|
||||
# endif |
|
||||
# endfor |
|
||||
# endfor |
|
||||
# if a |
|
||||
# print 'A' |
|
||||
# elif b |
|
||||
# print 'B' |
|
||||
# elif c == d |
|
||||
# print 'C' |
|
||||
# else |
|
||||
# print 'D' |
|
||||
# endif |
|
||||
''') |
|
||||
assert t.render(a=0, b=False, c=42, d=42.0) == '1111C' |
|
||||
|
|
||||
def test_stacked_locals_scoping_bug_twoframe(self, env): |
|
||||
t = Template(''' |
|
||||
{% set x = 1 %} |
|
||||
{% for item in foo %} |
|
||||
{% if item == 1 %} |
|
||||
{% set x = 2 %} |
|
||||
{% endif %} |
|
||||
{% endfor %} |
|
||||
{{ x }} |
|
||||
''') |
|
||||
rv = t.render(foo=[1]).strip() |
|
||||
assert rv == u'1' |
|
||||
|
|
||||
def test_call_with_args(self, env): |
|
||||
t = Template("""{% macro dump_users(users) -%} |
|
||||
<ul> |
|
||||
{%- for user in users -%} |
|
||||
<li><p>{{ user.username|e }}</p>{{ caller(user) }}</li> |
|
||||
{%- endfor -%} |
|
||||
</ul> |
|
||||
{%- endmacro -%} |
|
||||
|
|
||||
{% call(user) dump_users(list_of_user) -%} |
|
||||
<dl> |
|
||||
<dl>Realname</dl> |
|
||||
<dd>{{ user.realname|e }}</dd> |
|
||||
<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()] == [ |
|
||||
u'<ul><li><p>apo</p><dl>', |
|
||||
u'<dl>Realname</dl>', |
|
||||
u'<dd>something else</dd>', |
|
||||
u'<dl>Description</dl>', |
|
||||
u'<dd>test</dd>', |
|
||||
u'</dl>', |
|
||||
u'</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_bug(self, env): |
|
||||
tpl1 = 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 %} |
|
||||
{% endfor %} |
|
||||
""") |
|
||||
|
|
||||
tpl2 = 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 %} |
|
||||
{% endfor %} |
|
||||
""") |
|
||||
|
|
||||
def test_else_loop_bug(self, env): |
|
||||
t = Template(''' |
|
||||
{% for x in y %} |
|
||||
{{ loop.index0 }} |
|
||||
{% else %} |
|
||||
{% for i in range(3) %}{{ i }}{% endfor %} |
|
||||
{% endfor %} |
|
||||
''') |
|
||||
assert t.render(y=[]).strip() == '012' |
|
||||
|
|
||||
def test_correct_prefix_loader_name(self, env): |
|
||||
env = Environment(loader=PrefixLoader({ |
|
||||
'foo': DictLoader({}) |
|
||||
})) |
|
||||
try: |
|
||||
env.get_template('foo/bar.html') |
|
||||
except TemplateNotFound as e: |
|
||||
assert e.name == 'foo/bar.html' |
|
||||
else: |
|
||||
assert False, 'expected error here' |
|
||||
|
|
||||
def test_contextfunction_callable_classes(self, env): |
|
||||
from jinja2.utils import contextfunction |
|
||||
|
|
||||
class CallableClass(object): |
|
||||
@contextfunction |
|
||||
def __call__(self, ctx): |
|
||||
return ctx.resolve('hello') |
|
||||
|
|
||||
tpl = Template("""{{ callableclass() }}""") |
|
||||
output = tpl.render(callableclass=CallableClass(), hello='TEST') |
|
||||
expected = 'TEST' |
|
||||
|
|
||||
assert output == expected |
|
@ -1,161 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.security |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Checks the sandbox and other security features. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Environment |
|
||||
from jinja2.sandbox import SandboxedEnvironment, \ |
|
||||
ImmutableSandboxedEnvironment, unsafe |
|
||||
from jinja2 import Markup, escape |
|
||||
from jinja2.exceptions import SecurityError, TemplateSyntaxError, \ |
|
||||
TemplateRuntimeError |
|
||||
from jinja2._compat import text_type |
|
||||
|
|
||||
|
|
||||
class PrivateStuff(object): |
|
||||
|
|
||||
def bar(self): |
|
||||
return 23 |
|
||||
|
|
||||
@unsafe |
|
||||
def foo(self): |
|
||||
return 42 |
|
||||
|
|
||||
def __repr__(self): |
|
||||
return 'PrivateStuff' |
|
||||
|
|
||||
|
|
||||
class PublicStuff(object): |
|
||||
bar = lambda self: 23 |
|
||||
_foo = lambda self: 42 |
|
||||
|
|
||||
def __repr__(self): |
|
||||
return 'PublicStuff' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.sandbox |
|
||||
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) == '' |
|
||||
# security error comes from __class__ already. |
|
||||
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) |
|
||||
|
|
||||
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 %}") |
|
||||
|
|
||||
def test_markup_operations(self, env): |
|
||||
# adding two strings should escape the unsafe one |
|
||||
unsafe = '<script type="application/x-some-script">alert("foo");</script>' |
|
||||
safe = Markup('<em>username</em>') |
|
||||
assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) |
|
||||
|
|
||||
# string interpolations are safe to use too |
|
||||
assert Markup('<em>%s</em>') % '<bad user>' == \ |
|
||||
'<em><bad user></em>' |
|
||||
assert Markup('<em>%(username)s</em>') % { |
|
||||
'username': '<bad user>' |
|
||||
} == '<em><bad user></em>' |
|
||||
|
|
||||
# an escaped object is markup too |
|
||||
assert type(Markup('foo') + 'bar') is Markup |
|
||||
|
|
||||
# and it implements __html__ by returning itself |
|
||||
x = Markup("foo") |
|
||||
assert x.__html__() is x |
|
||||
|
|
||||
# it also knows how to treat __html__ objects |
|
||||
class Foo(object): |
|
||||
def __html__(self): |
|
||||
return '<em>awesome</em>' |
|
||||
|
|
||||
def __unicode__(self): |
|
||||
return 'awesome' |
|
||||
assert Markup(Foo()) == '<em>awesome</em>' |
|
||||
assert Markup('<strong>%s</strong>') % Foo() == \ |
|
||||
'<strong><em>awesome</em></strong>' |
|
||||
|
|
||||
# escaping and unescaping |
|
||||
assert escape('"<>&\'') == '"<>&'' |
|
||||
assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar" |
|
||||
assert Markup("<test>").unescape() == "<test>" |
|
||||
|
|
||||
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>' |
|
||||
assert t.render() == escaped_out |
|
||||
assert text_type(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('<blink>foo</blink>')) == escaped_out |
|
||||
|
|
||||
def test_attr_filter(self, env): |
|
||||
env = SandboxedEnvironment() |
|
||||
tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}') |
|
||||
pytest.raises(SecurityError, tmpl.render, cls=int) |
|
||||
|
|
||||
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'): |
|
||||
env = SandboxedEnvironment() |
|
||||
env.binop_table['+'] = disable_op |
|
||||
t = env.from_string('{{ %s }}' % expr) |
|
||||
assert t.render(ctx) == rv |
|
||||
env.intercepted_binops = frozenset(['+']) |
|
||||
t = env.from_string('{{ %s }}' % expr) |
|
||||
try: |
|
||||
t.render(ctx) |
|
||||
except TemplateRuntimeError as e: |
|
||||
pass |
|
||||
else: |
|
||||
assert False, 'expected runtime error' |
|
||||
|
|
||||
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'): |
|
||||
env = SandboxedEnvironment() |
|
||||
env.unop_table['-'] = disable_op |
|
||||
t = env.from_string('{{ %s }}' % expr) |
|
||||
assert t.render(ctx) == rv |
|
||||
env.intercepted_unops = frozenset(['-']) |
|
||||
t = env.from_string('{{ %s }}' % expr) |
|
||||
try: |
|
||||
t.render(ctx) |
|
||||
except TemplateRuntimeError as e: |
|
||||
pass |
|
||||
else: |
|
||||
assert False, 'expected runtime error' |
|
@ -1,104 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.tests |
|
||||
~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Who tests the tests? |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import pytest |
|
||||
|
|
||||
from jinja2 import Markup, Environment |
|
||||
|
|
||||
|
|
||||
@pytest.mark.test_tests |
|
||||
class TestTestsCase(): |
|
||||
|
|
||||
def test_defined(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_odd(self, env): |
|
||||
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' |
|
||||
|
|
||||
def test_typechecks(self, env): |
|
||||
tmpl = env.from_string(''' |
|
||||
{{ 42 is undefined }} |
|
||||
{{ 42 is defined }} |
|
||||
{{ 42 is none }} |
|
||||
{{ none is none }} |
|
||||
{{ 42 is number }} |
|
||||
{{ 42 is string }} |
|
||||
{{ "foo" is string }} |
|
||||
{{ "foo" is sequence }} |
|
||||
{{ [1] is sequence }} |
|
||||
{{ range is callable }} |
|
||||
{{ 42 is callable }} |
|
||||
{{ range(5) is iterable }} |
|
||||
{{ {} is mapping }} |
|
||||
{{ mydict is mapping }} |
|
||||
{{ [] is mapping }} |
|
||||
{{ 10 is number }} |
|
||||
{{ (10 ** 100) is number }} |
|
||||
{{ 3.14159 is number }} |
|
||||
{{ complex is number }} |
|
||||
''') |
|
||||
|
|
||||
class MyDict(dict): |
|
||||
pass |
|
||||
|
|
||||
assert tmpl.render(mydict=MyDict(), complex=complex(1, 2)).split() == [ |
|
||||
'False', 'True', 'False', 'True', 'True', 'False', |
|
||||
'True', 'True', 'True', 'True', 'False', 'True', |
|
||||
'True', 'True', 'False', 'True', 'True', 'True', 'True' |
|
||||
] |
|
||||
|
|
||||
def test_sequence(self, env): |
|
||||
tmpl = env.from_string( |
|
||||
'{{ [1, 2, 3] is sequence }}|' |
|
||||
'{{ "foo" is sequence }}|' |
|
||||
'{{ 42 is sequence }}' |
|
||||
) |
|
||||
assert tmpl.render() == 'True|True|False' |
|
||||
|
|
||||
def test_upper(self, env): |
|
||||
tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}') |
|
||||
assert tmpl.render() == 'True|False' |
|
||||
|
|
||||
def test_equalto(self, env): |
|
||||
tmpl = env.from_string('{{ foo is equalto 12 }}|' |
|
||||
'{{ foo is equalto 0 }}|' |
|
||||
'{{ foo is equalto (3 * 4) }}|' |
|
||||
'{{ bar is equalto "baz" }}|' |
|
||||
'{{ bar is equalto "zab" }}|' |
|
||||
'{{ bar is equalto ("ba" + "z") }}|' |
|
||||
'{{ bar is equalto bar }}|' |
|
||||
'{{ bar is equalto foo }}') |
|
||||
assert tmpl.render(foo=12, bar="baz") \ |
|
||||
== 'True|False|True|True|False|True|True|False' |
|
||||
|
|
||||
def test_sameas(self, env): |
|
||||
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' |
|
||||
|
|
||||
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' |
|
@ -1,76 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
""" |
|
||||
jinja2.testsuite.utils |
|
||||
~~~~~~~~~~~~~~~~~~~~~~ |
|
||||
|
|
||||
Tests utilities jinja uses. |
|
||||
|
|
||||
:copyright: (c) 2010 by the Jinja Team. |
|
||||
:license: BSD, see LICENSE for more details. |
|
||||
""" |
|
||||
import gc |
|
||||
|
|
||||
import pytest |
|
||||
|
|
||||
import pickle |
|
||||
|
|
||||
from jinja2.utils import LRUCache, escape, object_type_repr |
|
||||
|
|
||||
|
|
||||
@pytest.mark.utils |
|
||||
@pytest.mark.lrucache |
|
||||
class TestLRUCache(): |
|
||||
|
|
||||
def test_simple(self): |
|
||||
d = LRUCache(3) |
|
||||
d["a"] = 1 |
|
||||
d["b"] = 2 |
|
||||
d["c"] = 3 |
|
||||
d["a"] |
|
||||
d["d"] = 4 |
|
||||
assert len(d) == 3 |
|
||||
assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d |
|
||||
|
|
||||
def test_pickleable(self): |
|
||||
cache = LRUCache(2) |
|
||||
cache["foo"] = 42 |
|
||||
cache["bar"] = 23 |
|
||||
cache["foo"] |
|
||||
|
|
||||
for protocol in range(3): |
|
||||
copy = pickle.loads(pickle.dumps(cache, protocol)) |
|
||||
assert copy.capacity == cache.capacity |
|
||||
assert copy._mapping == cache._mapping |
|
||||
assert copy._queue == cache._queue |
|
||||
|
|
||||
|
|
||||
@pytest.mark.utils |
|
||||
@pytest.mark.helpers |
|
||||
class TestHelpers(): |
|
||||
|
|
||||
def test_object_type_repr(self): |
|
||||
class X(object): |
|
||||
pass |
|
||||
assert object_type_repr(42) == 'int object' |
|
||||
assert object_type_repr([]) == 'list object' |
|
||||
assert object_type_repr(X()) == 'test_utils.X object' |
|
||||
assert object_type_repr(None) == 'None' |
|
||||
assert object_type_repr(Ellipsis) == 'Ellipsis' |
|
||||
|
|
||||
|
|
||||
@pytest.mark.utils |
|
||||
@pytest.mark.markupleak |
|
||||
@pytest.mark.skipif(hasattr(escape, 'func_code'), |
|
||||
reason='this test only tests the c extension') |
|
||||
class TestMarkupLeak(): |
|
||||
|
|
||||
def test_markup_leaks(self): |
|
||||
counts = set() |
|
||||
for count in range(20): |
|
||||
for item in range(1000): |
|
||||
escape("foo") |
|
||||
escape("<foo>") |
|
||||
escape(u"foo") |
|
||||
escape(u"<foo>") |
|
||||
counts.add(len(gc.get_objects())) |
|
||||
assert len(counts) == 1, 'ouch, c extension seems to leak objects' |
|
Loading…
Reference in new issue