summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2020-01-10 07:46:18 -0800
committerDavid Lord <davidism@gmail.com>2020-01-10 07:54:39 -0800
commit04c8787155137206d58d6ee147d06482c1a8b598 (patch)
tree3d0abf84d0296a15fdd949bcbf8570ae4b93d912
parentd177eeb295e2552227bf569d703ca32bb509a628 (diff)
downloadjinja2-04c8787155137206d58d6ee147d06482c1a8b598.tar.gz
apply black
-rw-r--r--docs/cache_extension.py16
-rw-r--r--examples/basic/cycle.py12
-rw-r--r--examples/basic/debugger.py4
-rw-r--r--examples/basic/inheritance.py16
-rw-r--r--examples/basic/test.py20
-rw-r--r--examples/basic/test_filter_and_linestatements.py10
-rw-r--r--examples/basic/test_loop_filter.py6
-rw-r--r--examples/basic/translate.py20
-rw-r--r--examples/bench.py169
-rw-r--r--examples/profile.py13
-rw-r--r--examples/rwbench/djangoext.py55
-rw-r--r--examples/rwbench/rwbench.py62
-rw-r--r--ext/django2jinja/django2jinja.py333
-rw-r--r--ext/django2jinja/example.py5
-rw-r--r--ext/djangojinja2.py27
-rw-r--r--ext/inlinegettext.py41
-rwxr-xr-xscripts/generate_identifier_pattern.py27
-rwxr-xr-xscripts/jinja2-debug.py27
-rw-r--r--scripts/make-release.py76
-rw-r--r--src/jinja2/_compat.py14
-rw-r--r--src/jinja2/_identifier.py2
-rw-r--r--src/jinja2/asyncfilters.py48
-rw-r--r--src/jinja2/asyncsupport.py34
-rw-r--r--src/jinja2/bccache.py64
-rw-r--r--src/jinja2/compiler.py899
-rw-r--r--src/jinja2/constants.py4
-rw-r--r--src/jinja2/debug.py7
-rw-r--r--src/jinja2/defaults.py41
-rw-r--r--src/jinja2/environment.py366
-rw-r--r--src/jinja2/exceptions.py22
-rw-r--r--src/jinja2/ext.py269
-rw-r--r--src/jinja2/filters.py308
-rw-r--r--src/jinja2/idtracking.py53
-rw-r--r--src/jinja2/lexer.py581
-rw-r--r--src/jinja2/loaders.py70
-rw-r--r--src/jinja2/meta.py15
-rw-r--r--src/jinja2/nativetypes.py2
-rw-r--r--src/jinja2/nodes.py370
-rw-r--r--src/jinja2/parser.py525
-rw-r--r--src/jinja2/runtime.py305
-rw-r--r--src/jinja2/sandbox.py148
-rw-r--r--src/jinja2/tests.py78
-rw-r--r--src/jinja2/utils.py195
-rw-r--r--src/jinja2/visitor.py2
-rw-r--r--tests/conftest.py114
-rw-r--r--tests/res/templates/mojibake.txt1
-rw-r--r--tests/res/templates/variable_encoding.txt1
-rw-r--r--tests/test_api.py266
-rw-r--r--tests/test_async.py470
-rw-r--r--tests/test_asyncfilters.py199
-rw-r--r--tests/test_bytecode_cache.py26
-rw-r--r--tests/test_core_tags.py615
-rw-r--r--tests/test_debug.py82
-rw-r--r--tests/test_ext.py579
-rw-r--r--tests/test_features.py18
-rw-r--r--tests/test_filters.py595
-rw-r--r--tests/test_idtracking.py343
-rw-r--r--tests/test_imports.py120
-rw-r--r--tests/test_inheritance.py277
-rw-r--r--tests/test_lexnparse.py679
-rw-r--r--tests/test_loader.py242
-rw-r--r--tests/test_nativetypes.py39
-rw-r--r--tests/test_regression.py496
-rw-r--r--tests/test_runtime.py6
-rw-r--r--tests/test_security.py152
-rw-r--r--tests/test_tests.py314
-rw-r--r--tests/test_utils.py86
67 files changed, 6198 insertions, 4883 deletions
diff --git a/docs/cache_extension.py b/docs/cache_extension.py
index 992b595..387cd46 100644
--- a/docs/cache_extension.py
+++ b/docs/cache_extension.py
@@ -4,16 +4,13 @@ from jinja2.ext import Extension
class FragmentCacheExtension(Extension):
# a set of names that trigger the extension.
- tags = {'cache'}
+ tags = {"cache"}
def __init__(self, environment):
super(FragmentCacheExtension, self).__init__(environment)
# add the defaults to the environment
- environment.extend(
- fragment_cache_prefix='',
- fragment_cache=None
- )
+ environment.extend(fragment_cache_prefix="", fragment_cache=None)
def parse(self, parser):
# the first token is the token that started the tag. In our case
@@ -27,19 +24,20 @@ class FragmentCacheExtension(Extension):
# if there is a comma, the user provided a timeout. If not use
# None as second parameter.
- if parser.stream.skip_if('comma'):
+ if parser.stream.skip_if("comma"):
args.append(parser.parse_expression())
else:
args.append(nodes.Const(None))
# now we parse the body of the cache block up to `endcache` and
# drop the needle (which would always be `endcache` in that case)
- body = parser.parse_statements(['name:endcache'], drop_needle=True)
+ body = parser.parse_statements(["name:endcache"], drop_needle=True)
# now return a `CallBlock` node that calls our _cache_support
# helper method on this extension.
- return nodes.CallBlock(self.call_method('_cache_support', args),
- [], [], body).set_lineno(lineno)
+ return nodes.CallBlock(
+ self.call_method("_cache_support", args), [], [], body
+ ).set_lineno(lineno)
def _cache_support(self, name, timeout, caller):
"""Helper callback."""
diff --git a/examples/basic/cycle.py b/examples/basic/cycle.py
index 63d8a42..25dcb0b 100644
--- a/examples/basic/cycle.py
+++ b/examples/basic/cycle.py
@@ -2,11 +2,17 @@ from __future__ import print_function
from jinja2 import Environment
-env = Environment(line_statement_prefix="#", variable_start_string="${", variable_end_string="}")
-print(env.from_string("""\
+env = Environment(
+ line_statement_prefix="#", variable_start_string="${", variable_end_string="}"
+)
+print(
+ env.from_string(
+ """\
<ul>
# for item in range(10)
<li class="${loop.cycle('odd', 'even')}">${item}</li>
# endfor
</ul>\
-""").render())
+"""
+ ).render()
+)
diff --git a/examples/basic/debugger.py b/examples/basic/debugger.py
index b74125b..d3c1a60 100644
--- a/examples/basic/debugger.py
+++ b/examples/basic/debugger.py
@@ -3,6 +3,6 @@ from __future__ import print_function
from jinja2 import Environment
from jinja2.loaders import FileSystemLoader
-env = Environment(loader=FileSystemLoader('templates'))
-tmpl = env.get_template('broken.html')
+env = Environment(loader=FileSystemLoader("templates"))
+tmpl = env.get_template("broken.html")
print(tmpl.render(seq=[3, 2, 4, 5, 3, 2, 0, 2, 1]))
diff --git a/examples/basic/inheritance.py b/examples/basic/inheritance.py
index 647f42c..a3073a5 100644
--- a/examples/basic/inheritance.py
+++ b/examples/basic/inheritance.py
@@ -3,9 +3,13 @@ from __future__ import print_function
from jinja2 import Environment
from jinja2.loaders import DictLoader
-env = Environment(loader=DictLoader({
-'a': '''[A[{% block body %}{% endblock %}]]''',
-'b': '''{% extends 'a' %}{% block body %}[B]{% endblock %}''',
-'c': '''{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}'''
-}))
-print(env.get_template('c').render())
+env = Environment(
+ loader=DictLoader(
+ {
+ "a": """[A[{% block body %}{% endblock %}]]""",
+ "b": """{% extends 'a' %}{% block body %}[B]{% endblock %}""",
+ "c": """{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}""",
+ }
+ )
+)
+print(env.get_template("c").render())
diff --git a/examples/basic/test.py b/examples/basic/test.py
index 04a6adc..80b9d1f 100644
--- a/examples/basic/test.py
+++ b/examples/basic/test.py
@@ -3,8 +3,10 @@ from __future__ import print_function
from jinja2 import Environment
from jinja2.loaders import DictLoader
-env = Environment(loader=DictLoader({
-'child.html': u'''\
+env = Environment(
+ loader=DictLoader(
+ {
+ "child.html": u"""\
{% extends master_layout or 'master.html' %}
{% include helpers = 'helpers.html' %}
{% macro get_the_answer() %}42{% endmacro %}
@@ -13,15 +15,17 @@ env = Environment(loader=DictLoader({
{{ get_the_answer() }}
{{ helpers.conspirate() }}
{% endblock %}
-''',
-'master.html': u'''\
+""",
+ "master.html": u"""\
<!doctype html>
<title>{{ title }}</title>
{% block body %}{% endblock %}
-''',
-'helpers.html': u'''\
+""",
+ "helpers.html": u"""\
{% macro conspirate() %}23{% endmacro %}
-'''
-}))
+""",
+ }
+ )
+)
tmpl = env.get_template("child.html")
print(tmpl.render())
diff --git a/examples/basic/test_filter_and_linestatements.py b/examples/basic/test_filter_and_linestatements.py
index c18731c..673b67e 100644
--- a/examples/basic/test_filter_and_linestatements.py
+++ b/examples/basic/test_filter_and_linestatements.py
@@ -2,8 +2,11 @@ from __future__ import print_function
from jinja2 import Environment
-env = Environment(line_statement_prefix='%', variable_start_string="${", variable_end_string="}")
-tmpl = env.from_string("""\
+env = Environment(
+ line_statement_prefix="%", variable_start_string="${", variable_end_string="}"
+)
+tmpl = env.from_string(
+ """\
% macro foo()
${caller(42)}
% endmacro
@@ -21,5 +24,6 @@ tmpl = env.from_string("""\
- ${item}
% endfor
% endfilter
-""")
+"""
+)
print(tmpl.render(seq=range(10)))
diff --git a/examples/basic/test_loop_filter.py b/examples/basic/test_loop_filter.py
index d5b908b..39be08d 100644
--- a/examples/basic/test_loop_filter.py
+++ b/examples/basic/test_loop_filter.py
@@ -2,12 +2,14 @@ from __future__ import print_function
from jinja2 import Environment
-tmpl = Environment().from_string("""\
+tmpl = Environment().from_string(
+ """\
<ul>
{%- for item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] if item % 2 == 0 %}
<li>{{ loop.index }} / {{ loop.length }}: {{ item }}</li>
{%- endfor %}
</ul>
if condition: {{ 1 if foo else 0 }}
-""")
+"""
+)
print(tmpl.render(foo=True))
diff --git a/examples/basic/translate.py b/examples/basic/translate.py
index bbe445a..fda8f7a 100644
--- a/examples/basic/translate.py
+++ b/examples/basic/translate.py
@@ -2,15 +2,17 @@ from __future__ import print_function
from jinja2 import Environment
-env = Environment(extensions=['jinja2.ext.i18n'])
-env.globals['gettext'] = {
- 'Hello %(user)s!': 'Hallo %(user)s!'
-}.__getitem__
-env.globals['ngettext'] = lambda s, p, n: {
- '%(count)s user': '%(count)d Benutzer',
- '%(count)s users': '%(count)d Benutzer'
+env = Environment(extensions=["jinja2.ext.i18n"])
+env.globals["gettext"] = {"Hello %(user)s!": "Hallo %(user)s!"}.__getitem__
+env.globals["ngettext"] = lambda s, p, n: {
+ "%(count)s user": "%(count)d Benutzer",
+ "%(count)s users": "%(count)d Benutzer",
}[n == 1 and s or p]
-print(env.from_string("""\
+print(
+ env.from_string(
+ """\
{% trans %}Hello {{ user }}!{% endtrans %}
{% trans count=users|count %}{{ count }} user{% pluralize %}{{ count }} users{% endtrans %}
-""").render(user="someone", users=[1, 2, 3]))
+"""
+ ).render(user="someone", users=[1, 2, 3])
+)
diff --git a/examples/bench.py b/examples/bench.py
index 7d988cd..473928b 100644
--- a/examples/bench.py
+++ b/examples/bench.py
@@ -10,15 +10,16 @@ from timeit import Timer
from jinja2 import Environment as JinjaEnvironment
context = {
- 'page_title': 'mitsuhiko\'s benchmark',
- 'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)]
+ "page_title": "mitsuhiko's benchmark",
+ "table": [
+ dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10) for x in range(1000)
+ ],
}
jinja_template = JinjaEnvironment(
- line_statement_prefix='%',
- variable_start_string="${",
- variable_end_string="}"
-).from_string("""\
+ line_statement_prefix="%", variable_start_string="${", variable_end_string="}"
+).from_string(
+ """\
<!doctype html>
<html>
<head>
@@ -50,17 +51,21 @@ jinja_template = JinjaEnvironment(
</div>
</body>
</html>\
-""")
+"""
+)
+
def test_jinja():
jinja_template.render(context)
+
try:
from tornado.template import Template
except ImportError:
test_tornado = None
else:
- tornado_template = Template("""\
+ tornado_template = Template(
+ """\
<!doctype html>
<html>
<head>
@@ -92,19 +97,23 @@ else:
</div>
</body>
</html>\
-""")
+"""
+ )
def test_tornado():
tornado_template.generate(**context)
+
try:
from django.conf import settings
+
settings.configure()
from django.template import Template as DjangoTemplate, Context as DjangoContext
except ImportError:
test_django = None
else:
- django_template = DjangoTemplate("""\
+ django_template = DjangoTemplate(
+ """\
<!doctype html>
<html>
<head>
@@ -132,20 +141,26 @@ else:
</div>
</body>
</html>\
-""")
+"""
+ )
def test_django():
c = DjangoContext(context)
- c['navigation'] = [('index.html', 'Index'), ('downloads.html', 'Downloads'),
- ('products.html', 'Products')]
+ c["navigation"] = [
+ ("index.html", "Index"),
+ ("downloads.html", "Downloads"),
+ ("products.html", "Products"),
+ ]
django_template.render(c)
+
try:
from mako.template import Template as MakoTemplate
except ImportError:
test_mako = None
else:
- mako_template = MakoTemplate("""\
+ mako_template = MakoTemplate(
+ """\
<!doctype html>
<html>
<head>
@@ -173,17 +188,20 @@ else:
</div>
</body>
</html>\
-""")
+"""
+ )
def test_mako():
mako_template.render(**context)
+
try:
from genshi.template import MarkupTemplate as GenshiTemplate
except ImportError:
test_genshi = None
else:
- genshi_template = GenshiTemplate("""\
+ genshi_template = GenshiTemplate(
+ """\
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
<head>
<title>${page_title}</title>
@@ -207,17 +225,20 @@ else:
</div>
</body>
</html>\
-""")
+"""
+ )
def test_genshi():
- genshi_template.generate(**context).render('html', strip_whitespace=False)
+ genshi_template.generate(**context).render("html", strip_whitespace=False)
+
try:
from Cheetah.Template import Template as CheetahTemplate
except ImportError:
test_cheetah = None
else:
- cheetah_template = CheetahTemplate("""\
+ cheetah_template = CheetahTemplate(
+ """\
#import cgi
<!doctype html>
<html>
@@ -246,18 +267,22 @@ else:
</div>
</body>
</html>\
-""", searchList=[dict(context)])
+""",
+ searchList=[dict(context)],
+ )
def test_cheetah():
unicode(cheetah_template)
+
try:
import tenjin
except ImportError:
test_tenjin = None
else:
tenjin_template = tenjin.Template()
- tenjin_template.convert("""\
+ tenjin_template.convert(
+ """\
<!doctype html>
<html>
<head>
@@ -285,19 +310,23 @@ else:
</div>
</body>
</html>\
-""")
+"""
+ )
def test_tenjin():
from tenjin.helpers import escape, to_str
+
tenjin_template.render(context, locals())
+
try:
from spitfire.compiler import util as SpitfireTemplate
from spitfire.compiler.analyzer import o2_options as spitfire_optimizer
except ImportError:
test_spitfire = None
else:
- spitfire_template = SpitfireTemplate.load_template("""\
+ spitfire_template = SpitfireTemplate.load_template(
+ """\
<!doctype html>
<html>
<head>
@@ -325,8 +354,12 @@ else:
</div>
</body>
</html>\
-""", 'spitfire_tmpl', spitfire_optimizer, {'enable_filters': False})
- spitfire_context = dict(context, **{'cgi': cgi})
+""",
+ "spitfire_tmpl",
+ spitfire_optimizer,
+ {"enable_filters": False},
+ )
+ spitfire_context = dict(context, **{"cgi": cgi})
def test_spitfire():
spitfire_template(search_list=[spitfire_context]).main()
@@ -337,7 +370,8 @@ try:
except ImportError:
test_chameleon = None
else:
- chameleon_template = PageTemplate("""\
+ chameleon_template = PageTemplate(
+ """\
<html xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
<title tal:content="page_title">Page Title</title>
@@ -358,23 +392,27 @@ else:
</div>
</body>
</html>\
-""")
+"""
+ )
chameleon_context = dict(context)
- chameleon_context['sections'] = [
- ('index.html', 'Index'),
- ('downloads.html', 'Downloads'),
- ('products.html', 'Products')
+ chameleon_context["sections"] = [
+ ("index.html", "Index"),
+ ("downloads.html", "Downloads"),
+ ("products.html", "Products"),
]
+
def test_chameleon():
chameleon_template.render(**chameleon_context)
+
try:
from chameleon.zpt.template import PageTemplate
from chameleon.genshi import language
except ImportError:
test_chameleon_genshi = None
else:
- chameleon_genshi_template = PageTemplate("""\
+ chameleon_genshi_template = PageTemplate(
+ """\
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
<head>
<title>${page_title}</title>
@@ -395,40 +433,63 @@ else:
</div>
</body>
</html>\
-""", parser=language.Parser())
+""",
+ parser=language.Parser(),
+ )
chameleon_genshi_context = dict(context)
- chameleon_genshi_context['sections'] = [
- ('index.html', 'Index'),
- ('downloads.html', 'Downloads'),
- ('products.html', 'Products')
+ chameleon_genshi_context["sections"] = [
+ ("index.html", "Index"),
+ ("downloads.html", "Downloads"),
+ ("products.html", "Products"),
]
+
def test_chameleon_genshi():
chameleon_genshi_template.render(**chameleon_genshi_context)
-sys.stdout.write('\r' + '\n'.join((
- '=' * 80,
- 'Template Engine BigTable Benchmark'.center(80),
- '=' * 80,
- __doc__,
- '-' * 80
-)) + '\n')
+sys.stdout.write(
+ "\r"
+ + "\n".join(
+ (
+ "=" * 80,
+ "Template Engine BigTable Benchmark".center(80),
+ "=" * 80,
+ __doc__,
+ "-" * 80,
+ )
+ )
+ + "\n"
+)
-for test in 'jinja', 'mako', 'tornado', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah', 'chameleon', 'chameleon_genshi':
- if locals()['test_' + test] is None:
- sys.stdout.write(' %-20s*not installed*\n' % test)
+for test in (
+ "jinja",
+ "mako",
+ "tornado",
+ "tenjin",
+ "spitfire",
+ "django",
+ "genshi",
+ "cheetah",
+ "chameleon",
+ "chameleon_genshi",
+):
+ if locals()["test_" + test] is None:
+ sys.stdout.write(" %-20s*not installed*\n" % test)
continue
- t = Timer(setup='from __main__ import test_%s as bench' % test,
- stmt='bench()')
- sys.stdout.write(' >> %-20s<running>' % test)
+ t = Timer(setup="from __main__ import test_%s as bench" % test, stmt="bench()")
+ sys.stdout.write(" >> %-20s<running>" % test)
sys.stdout.flush()
- sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50))
-sys.stdout.write('-' * 80 + '\n')
-sys.stdout.write('''\
+ sys.stdout.write("\r %-20s%.4f seconds\n" % (test, t.timeit(number=50) / 50))
+sys.stdout.write("-" * 80 + "\n")
+sys.stdout.write(
+ """\
WARNING: The results of this benchmark are useless to compare the
performance of template engines and should not be taken seriously in any
way. It's testing the performance of simple loops and has no real-world
usefulnes. It only used to check if changes on the Jinja code affect
performance in a good or bad way and how it roughly compares to others.
-''' + '=' * 80 + '\n')
+"""
+ + "=" * 80
+ + "\n"
+)
diff --git a/examples/profile.py b/examples/profile.py
index e6deb47..b16d99f 100644
--- a/examples/profile.py
+++ b/examples/profile.py
@@ -1,4 +1,5 @@
from __future__ import print_function
+
try:
from cProfile import Profile
except ImportError:
@@ -7,8 +8,10 @@ from pstats import Stats
from jinja2 import Environment as JinjaEnvironment
context = {
- 'page_title': 'mitsuhiko\'s benchmark',
- 'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)]
+ "page_title": "mitsuhiko's benchmark",
+ "table": [
+ dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10) for x in range(1000)
+ ],
}
source = """\
@@ -39,9 +42,7 @@ source = """\
</html>\
"""
jinja_template = JinjaEnvironment(
- line_statement_prefix='%',
- variable_start_string="${",
- variable_end_string="}"
+ line_statement_prefix="%", variable_start_string="${", variable_end_string="}"
).from_string(source)
print(jinja_template.environment.compile(source, raw=True))
@@ -49,5 +50,5 @@ print(jinja_template.environment.compile(source, raw=True))
p = Profile()
p.runcall(lambda: jinja_template.render(context))
stats = Stats(p)
-stats.sort_stats('time', 'calls')
+stats.sort_stats("time", "calls")
stats.print_stats()
diff --git a/examples/rwbench/djangoext.py b/examples/rwbench/djangoext.py
index 06897e5..0052aef 100644
--- a/examples/rwbench/djangoext.py
+++ b/examples/rwbench/djangoext.py
@@ -12,12 +12,13 @@ from rwbench import dateformat
from rwbench import ROOT
settings.configure(
- TEMPLATE_DIRS=(join(ROOT, 'django'),),
+ TEMPLATE_DIRS=(join(ROOT, "django"),),
TEMPLATE_LOADERS=(
- ('django.template.loaders.cached.Loader', (
- 'django.template.loaders.filesystem.Loader',
- )),
- )
+ (
+ "django.template.loaders.cached.Loader",
+ ("django.template.loaders.filesystem.Loader",),
+ ),
+ ),
)
# for django extensions. We monkey patch our extensions in so that
@@ -58,13 +59,12 @@ def form(parser, token):
args = []
while p.more():
args.append(p.value())
- body = parser.parse(('endform',))
+ body = parser.parse(("endform",))
parser.delete_first_token()
return FormNode(body, *args)
class InputFieldNode(Node):
-
def __init__(self, name, type=None, value=None):
self.name = var_or_none(name)
self.type = var_or_none(type)
@@ -72,22 +72,17 @@ class InputFieldNode(Node):
def render(self, context):
name = self.name.resolve(context)
- type = 'text'
- value = ''
+ type = "text"
+ value = ""
if self.type is not None:
type = self.type.resolve(context)
if self.value is not None:
value = self.value.resolve(context)
- tmpl = django_loader.get_template('_input_field.html')
- return tmpl.render(DjangoContext({
- 'name': name,
- 'type': type,
- 'value': value
- }))
+ tmpl = django_loader.get_template("_input_field.html")
+ return tmpl.render(DjangoContext({"name": name, "type": type, "value": value}))
class TextareaNode(Node):
-
def __init__(self, name, rows=None, cols=None, value=None):
self.name = var_or_none(name)
self.rows = var_or_none(rows)
@@ -98,24 +93,20 @@ class TextareaNode(Node):
name = self.name.resolve(context)
rows = 10
cols = 40
- value = ''
+ value = ""
if self.rows is not None:
rows = int(self.rows.resolve(context))
if self.cols is not None:
cols = int(self.cols.resolve(context))
if self.value is not None:
value = self.value.resolve(context)
- tmpl = django_loader.get_template('_textarea.html')
- return tmpl.render(DjangoContext({
- 'name': name,
- 'rows': rows,
- 'cols': cols,
- 'value': value
- }))
+ tmpl = django_loader.get_template("_textarea.html")
+ return tmpl.render(
+ DjangoContext({"name": name, "rows": rows, "cols": cols, "value": value})
+ )
class FormNode(Node):
-
def __init__(self, body, action=None, method=None):
self.body = body
self.action = action
@@ -123,15 +114,13 @@ class FormNode(Node):
def render(self, context):
body = self.body.render(context)
- action = ''
- method = 'post'
+ action = ""
+ method = "post"
if self.action is not None:
action = self.action.resolve(context)
if self.method is not None:
method = self.method.resolve(context)
- tmpl = django_loader.get_template('_form.html')
- return tmpl.render(DjangoContext({
- 'body': body,
- 'action': action,
- 'method': method
- }))
+ tmpl = django_loader.get_template("_form.html")
+ return tmpl.render(
+ DjangoContext({"body": body, "action": action, "method": method})
+ )
diff --git a/examples/rwbench/rwbench.py b/examples/rwbench/rwbench.py
index af5d40b..957216a 100644
--- a/examples/rwbench/rwbench.py
+++ b/examples/rwbench/rwbench.py
@@ -40,20 +40,19 @@ ROOT = abspath(dirname(__file__))
def dateformat(x):
- return x.strftime('%Y-%m-%d')
+ return x.strftime("%Y-%m-%d")
-jinja_env = Environment(loader=FileSystemLoader(join(ROOT, 'jinja')))
-jinja_env.filters['dateformat'] = dateformat
-mako_lookup = TemplateLookup(directories=[join(ROOT, 'mako')])
-genshi_loader = GenshiTemplateLoader([join(ROOT, 'genshi')])
+jinja_env = Environment(loader=FileSystemLoader(join(ROOT, "jinja")))
+jinja_env.filters["dateformat"] = dateformat
+mako_lookup = TemplateLookup(directories=[join(ROOT, "mako")])
+genshi_loader = GenshiTemplateLoader([join(ROOT, "genshi")])
class Article(object):
-
def __init__(self, id):
self.id = id
- self.href = '/article/%d' % self.id
+ self.href = "/article/%d" % self.id
self.title = generate_lorem_ipsum(1, False, 5, 10)
self.user = choice(users)
self.body = generate_lorem_ipsum()
@@ -62,33 +61,33 @@ class Article(object):
class User(object):
-
def __init__(self, username):
- self.href = '/user/%s' % username
+ self.href = "/user/%s" % username
self.username = username
-users = map(User, [u'John Doe', u'Jane Doe', u'Peter Somewhat'])
+users = map(User, [u"John Doe", u"Jane Doe", u"Peter Somewhat"])
articles = map(Article, range(20))
navigation = [
- ('index', 'Index'),
- ('about', 'About'),
- ('foo?bar=1', 'Foo with Bar'),
- ('foo?bar=2&s=x', 'Foo with X'),
- ('blah', 'Blub Blah'),
- ('hehe', 'Haha'),
+ ("index", "Index"),
+ ("about", "About"),
+ ("foo?bar=1", "Foo with Bar"),
+ ("foo?bar=2&s=x", "Foo with X"),
+ ("blah", "Blub Blah"),
+ ("hehe", "Haha"),
] * 5
context = dict(users=users, articles=articles, page_navigation=navigation)
-jinja_template = jinja_env.get_template('index.html')
-mako_template = mako_lookup.get_template('index.html')
-genshi_template = genshi_loader.load('index.html')
+jinja_template = jinja_env.get_template("index.html")
+mako_template = mako_lookup.get_template("index.html")
+genshi_template = genshi_loader.load("index.html")
def test_jinja():
jinja_template.render(context)
+
def test_mako():
mako_template.render_unicode(**context)
@@ -96,27 +95,28 @@ def test_mako():
def test_django():
# not cached because django is not thread safe and does
# not cache by itself so it would be unfair to cache it here.
- django_template = django_loader.get_template('index.html')
+ django_template = django_loader.get_template("index.html")
django_template.render(DjangoContext(context))
def test_genshi():
- genshi_template.generate(**context).render('html', doctype='html')
+ genshi_template.generate(**context).render("html", doctype="html")
-if __name__ == '__main__':
- sys.stdout.write('Realworldish Benchmark:\n')
- for test in 'jinja', 'mako', 'django', 'genshi':
- t = Timer(setup='from __main__ import test_%s as bench' % test,
- stmt='bench()')
- sys.stdout.write(' >> %-20s<running>' % test)
+if __name__ == "__main__":
+ sys.stdout.write("Realworldish Benchmark:\n")
+ for test in "jinja", "mako", "django", "genshi":
+ t = Timer(setup="from __main__ import test_%s as bench" % test, stmt="bench()")
+ sys.stdout.write(" >> %-20s<running>" % test)
sys.stdout.flush()
- sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=200) / 200))
+ sys.stdout.write(
+ "\r %-20s%.4f seconds\n" % (test, t.timeit(number=200) / 200)
+ )
- if '-p' in sys.argv:
- print('Jinja profile')
+ if "-p" in sys.argv:
+ print("Jinja profile")
p = Profile()
p.runcall(test_jinja)
stats = Stats(p)
- stats.sort_stats('time', 'calls')
+ stats.sort_stats("time", "calls")
stats.print_stats()
diff --git a/ext/django2jinja/django2jinja.py b/ext/django2jinja/django2jinja.py
index e9c19b2..25b116e 100644
--- a/ext/django2jinja/django2jinja.py
+++ b/ext/django2jinja/django2jinja.py
@@ -92,7 +92,7 @@ from jinja2.defaults import *
_node_handlers = {}
_resolved_filters = {}
-_newline_re = re.compile(r'(?:\r\n|\r|\n)')
+_newline_re = re.compile(r"(?:\r\n|\r|\n)")
# Django stores an itertools object on the cycle node. Not only is this
@@ -100,9 +100,13 @@ _newline_re = re.compile(r'(?:\r\n|\r|\n)')
# string values passed to the constructor to create a jinja loop.cycle()
# call from it.
_old_cycle_init = core_tags.CycleNode.__init__
+
+
def _fixed_cycle_init(self, cyclevars, variable_name=None):
self.raw_cycle_vars = map(Variable, cyclevars)
_old_cycle_init(self, cyclevars, variable_name)
+
+
core_tags.CycleNode.__init__ = _fixed_cycle_init
@@ -110,11 +114,13 @@ def node(cls):
def proxy(f):
_node_handlers[cls] = f
return f
+
return proxy
-def convert_templates(output_dir, extensions=('.html', '.txt'), writer=None,
- callback=None):
+def convert_templates(
+ output_dir, extensions=(".html", ".txt"), writer=None, callback=None
+):
"""Iterates over all templates in the template dirs configured and
translates them and writes the new templates into the output directory.
"""
@@ -136,12 +142,13 @@ def convert_templates(output_dir, extensions=('.html', '.txt'), writer=None,
writer.stream = original
if callback is None:
+
def callback(template):
print(template)
for directory in settings.TEMPLATE_DIRS:
for dirname, _, files in os.walk(directory):
- dirname = dirname[len(directory) + 1:]
+ dirname = dirname[len(directory) + 1 :]
for filename in filter_templates(files):
source = os.path.normpath(os.path.join(dirname, filename))
target = os.path.join(output_dir, dirname, filename)
@@ -149,7 +156,7 @@ def convert_templates(output_dir, extensions=('.html', '.txt'), writer=None,
if not os.path.exists(basetarget):
os.makedirs(basetarget)
callback(source)
- f = file(target, 'w')
+ f = file(target, "w")
try:
translate(f, source)
finally:
@@ -159,18 +166,22 @@ def convert_templates(output_dir, extensions=('.html', '.txt'), writer=None,
class Writer(object):
"""The core writer class."""
- def __init__(self, stream=None, error_stream=None,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- initial_autoescape=True,
- use_jinja_autoescape=False,
- custom_node_handlers=None,
- var_re=[],
- env=None):
+ def __init__(
+ self,
+ stream=None,
+ error_stream=None,
+ block_start_string=BLOCK_START_STRING,
+ block_end_string=BLOCK_END_STRING,
+ variable_start_string=VARIABLE_START_STRING,
+ variable_end_string=VARIABLE_END_STRING,
+ comment_start_string=COMMENT_START_STRING,
+ comment_end_string=COMMENT_END_STRING,
+ initial_autoescape=True,
+ use_jinja_autoescape=False,
+ custom_node_handlers=None,
+ var_re=[],
+ env=None,
+ ):
if stream is None:
stream = sys.stdout
if error_stream is None:
@@ -186,8 +197,7 @@ class Writer(object):
self.autoescape = initial_autoescape
self.spaceless = False
self.use_jinja_autoescape = use_jinja_autoescape
- self.node_handlers = dict(_node_handlers,
- **(custom_node_handlers or {}))
+ self.node_handlers = dict(_node_handlers, **(custom_node_handlers or {}))
self._loop_depth = 0
self._filters_warned = set()
self.var_re = var_re
@@ -220,15 +230,15 @@ class Writer(object):
def _post_open(self):
if self.spaceless:
- self.write('- ')
+ self.write("- ")
else:
- self.write(' ')
+ self.write(" ")
def _pre_close(self):
if self.spaceless:
- self.write(' -')
+ self.write(" -")
else:
- self.write(' ')
+ self.write(" ")
def start_variable(self):
"""Start a variable."""
@@ -237,9 +247,8 @@ class Writer(object):
def end_variable(self, always_safe=False):
"""End a variable."""
- if not always_safe and self.autoescape and \
- not self.use_jinja_autoescape:
- self.write('|e')
+ if not always_safe and self.autoescape and not self.use_jinja_autoescape:
+ self.write("|e")
self._pre_close()
self.write(self.variable_end_string)
@@ -276,52 +285,50 @@ class Writer(object):
for filter, args in filters:
name = self.get_filter_name(filter)
if name is None:
- self.warn('Could not find filter %s' % name)
+ self.warn("Could not find filter %s" % name)
continue
- if name not in DEFAULT_FILTERS and \
- name not in self._filters_warned:
+ if name not in DEFAULT_FILTERS and name not in self._filters_warned:
self._filters_warned.add(name)
- self.warn('Filter %s probably doesn\'t exist in Jinja' %
- name)
+ self.warn("Filter %s probably doesn't exist in Jinja" % name)
if not want_pipe:
want_pipe = True
else:
- self.write('|')
+ self.write("|")
self.write(name)
if args:
- self.write('(')
+ self.write("(")
for idx, (is_var, value) in enumerate(args):
if idx:
- self.write(', ')
+ self.write(", ")
if is_var:
self.node(value)
else:
self.literal(value)
- self.write(')')
+ self.write(")")
def get_location(self, origin, position):
"""Returns the location for an origin and position tuple as name
and lineno.
"""
- if hasattr(origin, 'source'):
+ if hasattr(origin, "source"):
source = origin.source
- name = '<unknown source>'
+ name = "<unknown source>"
else:
source = origin.loader(origin.loadname, origin.dirs)[0]
name = origin.loadname
- lineno = len(_newline_re.findall(source[:position[0]])) + 1
+ lineno = len(_newline_re.findall(source[: position[0]])) + 1
return name, lineno
def warn(self, message, node=None):
"""Prints a warning to the error stream."""
- if node is not None and hasattr(node, 'source'):
+ if node is not None and hasattr(node, "source"):
filename, lineno = self.get_location(*node.source)
- message = '[%s:%d] %s' % (filename, lineno, message)
+ message = "[%s:%d] %s" % (filename, lineno, message)
print(message, file=self.error_stream)
def translate_variable_name(self, var):
"""Performs variable name translation."""
- if self.in_loop and var == 'forloop' or var.startswith('forloop.'):
+ if self.in_loop and var == "forloop" or var.startswith("forloop."):
var = var[3:]
for reg, rep, unless in self.var_re:
@@ -348,10 +355,11 @@ class Writer(object):
handler(self, node)
break
else:
- self.warn('Untranslatable node %s.%s found' % (
- node.__module__,
- node.__class__.__name__
- ), node)
+ self.warn(
+ "Untranslatable node %s.%s found"
+ % (node.__module__, node.__class__.__name__),
+ node,
+ )
def body(self, nodes):
"""Calls node() for every node in the iterable passed."""
@@ -367,22 +375,24 @@ def text_node(writer, node):
@node(Variable)
def variable(writer, node):
if node.translate:
- writer.warn('i18n system used, make sure to install translations', node)
- writer.write('_(')
+ writer.warn("i18n system used, make sure to install translations", node)
+ writer.write("_(")
if node.literal is not None:
writer.literal(node.literal)
else:
writer.variable(node.var)
if node.translate:
- writer.write(')')
+ writer.write(")")
@node(VariableNode)
def variable_node(writer, node):
writer.start_variable()
- if node.filter_expression.var.var == 'block.super' \
- and not node.filter_expression.filters:
- writer.write('super()')
+ if (
+ node.filter_expression.var.var == "block.super"
+ and not node.filter_expression.filters
+ ):
+ writer.write("super()")
else:
writer.node(node.filter_expression)
writer.end_variable()
@@ -401,86 +411,89 @@ def comment_tag(writer, node):
@node(core_tags.DebugNode)
def comment_tag(writer, node):
- writer.warn('Debug tag detected. Make sure to add a global function '
- 'called debug to the namespace.', node=node)
- writer.print_expr('debug()')
+ writer.warn(
+ "Debug tag detected. Make sure to add a global function "
+ "called debug to the namespace.",
+ node=node,
+ )
+ writer.print_expr("debug()")
@node(core_tags.ForNode)
def for_loop(writer, node):
writer.start_block()
- writer.write('for ')
+ writer.write("for ")
for idx, var in enumerate(node.loopvars):
if idx:
- writer.write(', ')
+ writer.write(", ")
writer.variable(var)
- writer.write(' in ')
+ writer.write(" in ")
if node.is_reversed:
- writer.write('(')
+ writer.write("(")
writer.node(node.sequence)
if node.is_reversed:
- writer.write(')|reverse')
+ writer.write(")|reverse")
writer.end_block()
writer.enter_loop()
writer.body(node.nodelist_loop)
writer.leave_loop()
- writer.tag('endfor')
+ writer.tag("endfor")
@node(core_tags.IfNode)
def if_condition(writer, node):
writer.start_block()
- writer.write('if ')
- join_with = 'and'
+ writer.write("if ")
+ join_with = "and"
if node.link_type == core_tags.IfNode.LinkTypes.or_:
- join_with = 'or'
+ join_with = "or"
for idx, (ifnot, expr) in enumerate(node.bool_exprs):
if idx:
- writer.write(' %s ' % join_with)
+ writer.write(" %s " % join_with)
if ifnot:
- writer.write('not ')
+ writer.write("not ")
writer.node(expr)
writer.end_block()
writer.body(node.nodelist_true)
if node.nodelist_false:
- writer.tag('else')
+ writer.tag("else")
writer.body(node.nodelist_false)
- writer.tag('endif')
+ writer.tag("endif")
@node(core_tags.IfEqualNode)
def if_equal(writer, node):
writer.start_block()
- writer.write('if ')
+ writer.write("if ")
writer.node(node.var1)
if node.negate:
- writer.write(' != ')
+ writer.write(" != ")
else:
- writer.write(' == ')
+ writer.write(" == ")
writer.node(node.var2)
writer.end_block()
writer.body(node.nodelist_true)
if node.nodelist_false:
- writer.tag('else')
+ writer.tag("else")
writer.body(node.nodelist_false)
- writer.tag('endif')
+ writer.tag("endif")
@node(loader_tags.BlockNode)
def block(writer, node):
- writer.tag('block ' + node.name.replace('-', '_').rstrip('_'))
+ writer.tag("block " + node.name.replace("-", "_").rstrip("_"))
node = node
while node.parent is not None:
node = node.parent
writer.body(node.nodelist)
- writer.tag('endblock')
+ writer.tag("endblock")
@node(loader_tags.ExtendsNode)
def extends(writer, node):
writer.start_block()
- writer.write('extends ')
+ writer.write("extends ")
if node.parent_name_expr:
writer.node(node.parent_name_expr)
else:
@@ -493,8 +506,8 @@ def extends(writer, node):
@node(loader_tags.IncludeNode)
def include(writer, node):
writer.start_block()
- writer.write('include ')
- if hasattr(node, 'template'):
+ writer.write("include ")
+ if hasattr(node, "template"):
writer.literal(node.template.name)
else:
writer.node(node.template_name)
@@ -504,19 +517,19 @@ def include(writer, node):
@node(core_tags.CycleNode)
def cycle(writer, node):
if not writer.in_loop:
- writer.warn('Untranslatable free cycle (cycle outside loop)', node=node)
+ writer.warn("Untranslatable free cycle (cycle outside loop)", node=node)
return
if node.variable_name is not None:
writer.start_block()
- writer.write('set %s = ' % node.variable_name)
+ writer.write("set %s = " % node.variable_name)
else:
writer.start_variable()
- writer.write('loop.cycle(')
+ writer.write("loop.cycle(")
for idx, var in enumerate(node.raw_cycle_vars):
if idx:
- writer.write(', ')
+ writer.write(", ")
writer.node(var)
- writer.write(')')
+ writer.write(")")
if node.variable_name is not None:
writer.end_block()
else:
@@ -526,11 +539,11 @@ def cycle(writer, node):
@node(core_tags.FilterNode)
def filter(writer, node):
writer.start_block()
- writer.write('filter ')
+ writer.write("filter ")
writer.filters(node.filter_expr.filters, True)
writer.end_block()
writer.body(node.nodelist)
- writer.tag('endfilter')
+ writer.tag("endfilter")
@node(core_tags.AutoEscapeControlNode)
@@ -545,7 +558,7 @@ def autoescape_control(writer, node):
def spaceless(writer, node):
original = writer.spaceless
writer.spaceless = True
- writer.warn('entering spaceless mode with different semantics', node)
+ writer.warn("entering spaceless mode with different semantics", node)
# do the initial stripping
nodelist = list(node.nodelist)
if nodelist:
@@ -560,14 +573,14 @@ def spaceless(writer, node):
@node(core_tags.TemplateTagNode)
def template_tag(writer, node):
tag = {
- 'openblock': writer.block_start_string,
- 'closeblock': writer.block_end_string,
- 'openvariable': writer.variable_start_string,
- 'closevariable': writer.variable_end_string,
- 'opencomment': writer.comment_start_string,
- 'closecomment': writer.comment_end_string,
- 'openbrace': '{',
- 'closebrace': '}'
+ "openblock": writer.block_start_string,
+ "closeblock": writer.block_end_string,
+ "openvariable": writer.variable_start_string,
+ "closevariable": writer.variable_end_string,
+ "opencomment": writer.comment_start_string,
+ "closecomment": writer.comment_end_string,
+ "openbrace": "{",
+ "closebrace": "}",
}.get(node.tagtype)
if tag:
writer.start_variable()
@@ -577,23 +590,22 @@ def template_tag(writer, node):
@node(core_tags.URLNode)
def url_tag(writer, node):
- writer.warn('url node used. make sure to provide a proper url() '
- 'function', node)
+ writer.warn("url node used. make sure to provide a proper url() function", node)
if node.asvar:
writer.start_block()
- writer.write('set %s = ' % node.asvar)
+ writer.write("set %s = " % node.asvar)
else:
writer.start_variable()
autoescape = writer.autoescape
- writer.write('url(')
+ writer.write("url(")
writer.literal(node.view_name)
for arg in node.args:
- writer.write(', ')
+ writer.write(", ")
writer.node(arg)
for key, arg in node.kwargs.items():
- writer.write(', %s=' % key)
+ writer.write(", %s=" % key)
writer.node(arg)
- writer.write(')')
+ writer.write(")")
if node.asvar:
writer.end_block()
else:
@@ -602,25 +614,31 @@ def url_tag(writer, node):
@node(core_tags.WidthRatioNode)
def width_ratio(writer, node):
- writer.warn('widthratio expanded into formula. You may want to provide '
- 'a helper function for this calculation', node)
+ writer.warn(
+ "widthratio expanded into formula. You may want to provide "
+ "a helper function for this calculation",
+ node,
+ )
writer.start_variable()
- writer.write('(')
+ writer.write("(")
writer.node(node.val_expr)
- writer.write(' / ')
+ writer.write(" / ")
writer.node(node.max_expr)
- writer.write(' * ')
+ writer.write(" * ")
writer.write(str(int(node.max_width)))
- writer.write(')|round|int')
+ writer.write(")|round|int")
writer.end_variable(always_safe=True)
@node(core_tags.WithNode)
def with_block(writer, node):
- writer.warn('with block expanded into set statement. This could cause '
- 'variables following that block to be overridden.', node)
+ writer.warn(
+ "with block expanded into set statement. This could cause "
+ "variables following that block to be overridden.",
+ node,
+ )
writer.start_block()
- writer.write('set %s = ' % node.name)
+ writer.write("set %s = " % node.name)
writer.node(node.var)
writer.end_block()
writer.body(node.nodelist)
@@ -629,55 +647,67 @@ def with_block(writer, node):
@node(core_tags.RegroupNode)
def regroup(writer, node):
if node.expression.var.literal:
- writer.warn('literal in groupby filter used. Behavior in that '
- 'situation is undefined and translation is skipped.', node)
+ writer.warn(
+ "literal in groupby filter used. Behavior in that "
+ "situation is undefined and translation is skipped.",
+ node,
+ )
return
elif node.expression.filters:
- writer.warn('filters in groupby filter used. Behavior in that '
- 'situation is undefined which is most likely a bug '
- 'in your code. Filters were ignored.', node)
+ writer.warn(
+ "filters in groupby filter used. Behavior in that "
+ "situation is undefined which is most likely a bug "
+ "in your code. Filters were ignored.",
+ node,
+ )
writer.start_block()
- writer.write('set %s = ' % node.var_name)
+ writer.write("set %s = " % node.var_name)
writer.node(node.target)
- writer.write('|groupby(')
+ writer.write("|groupby(")
writer.literal(node.expression.var.var)
- writer.write(')')
+ writer.write(")")
writer.end_block()
@node(core_tags.LoadNode)
def warn_load(writer, node):
- writer.warn('load statement used which was ignored on conversion', node)
+ writer.warn("load statement used which was ignored on conversion", node)
@node(i18n_tags.GetAvailableLanguagesNode)
def get_available_languages(writer, node):
- writer.warn('make sure to provide a get_available_languages function', node)
- writer.tag('set %s = get_available_languages()' %
- writer.translate_variable_name(node.variable))
+ writer.warn("make sure to provide a get_available_languages function", node)
+ writer.tag(
+ "set %s = get_available_languages()"
+ % writer.translate_variable_name(node.variable)
+ )
@node(i18n_tags.GetCurrentLanguageNode)
def get_current_language(writer, node):
- writer.warn('make sure to provide a get_current_language function', node)
- writer.tag('set %s = get_current_language()' %
- writer.translate_variable_name(node.variable))
+ writer.warn("make sure to provide a get_current_language function", node)
+ writer.tag(
+ "set %s = get_current_language()"
+ % writer.translate_variable_name(node.variable)
+ )
@node(i18n_tags.GetCurrentLanguageBidiNode)
def get_current_language_bidi(writer, node):
- writer.warn('make sure to provide a get_current_language_bidi function', node)
- writer.tag('set %s = get_current_language_bidi()' %
- writer.translate_variable_name(node.variable))
+ writer.warn("make sure to provide a get_current_language_bidi function", node)
+ writer.tag(
+ "set %s = get_current_language_bidi()"
+ % writer.translate_variable_name(node.variable)
+ )
@node(i18n_tags.TranslateNode)
def simple_gettext(writer, node):
- writer.warn('i18n system used, make sure to install translations', node)
+ writer.warn("i18n system used, make sure to install translations", node)
writer.start_variable()
- writer.write('_(')
+ writer.write("_(")
writer.node(node.value)
- writer.write(')')
+ writer.write(")")
writer.end_variable()
@@ -699,14 +729,14 @@ def translate_block(writer, node):
writer.print_expr(token.contents)
touch_var(token.contents)
- writer.warn('i18n system used, make sure to install translations', node)
+ writer.warn("i18n system used, make sure to install translations", node)
writer.start_block()
- writer.write('trans')
+ writer.write("trans")
idx = -1
for idx, (key, var) in enumerate(node.extra_context.items()):
if idx:
- writer.write(',')
- writer.write(' %s=' % key)
+ writer.write(",")
+ writer.write(" %s=" % key)
touch_var(key)
writer.node(var.filter_expression)
@@ -717,61 +747,64 @@ def translate_block(writer, node):
plural_var = node.countervar
if plural_var not in variables:
if idx > -1:
- writer.write(',')
+ writer.write(",")
touch_var(plural_var)
- writer.write(' %s=' % plural_var)
+ writer.write(" %s=" % plural_var)
writer.node(node.counter)
writer.end_block()
dump_token_list(node.singular)
if node.plural and node.countervar and node.counter:
writer.start_block()
- writer.write('pluralize')
+ writer.write("pluralize")
if node.countervar != first_var[0]:
- writer.write(' ' + node.countervar)
+ writer.write(" " + node.countervar)
writer.end_block()
dump_token_list(node.plural)
- writer.tag('endtrans')
+ writer.tag("endtrans")
+
@node("SimpleNode")
def simple_tag(writer, node):
"""Check if the simple tag exist as a filter in """
name = node.tag_name
- if writer.env and \
- name not in writer.env.filters and \
- name not in writer._filters_warned:
+ if (
+ writer.env
+ and name not in writer.env.filters
+ and name not in writer._filters_warned
+ ):
writer._filters_warned.add(name)
- writer.warn('Filter %s probably doesn\'t exist in Jinja' %
- name)
+ writer.warn("Filter %s probably doesn't exist in Jinja" % name)
if not node.vars_to_resolve:
# No argument, pass the request
writer.start_variable()
- writer.write('request|')
+ writer.write("request|")
writer.write(name)
writer.end_variable()
return
- first_var = node.vars_to_resolve[0]
+ first_var = node.vars_to_resolve[0]
args = node.vars_to_resolve[1:]
writer.start_variable()
# Copied from Writer.filters()
writer.node(first_var)
- writer.write('|')
+ writer.write("|")
writer.write(name)
if args:
- writer.write('(')
+ writer.write("(")
for idx, var in enumerate(args):
if idx:
- writer.write(', ')
+ writer.write(", ")
if var.var:
writer.node(var)
else:
writer.literal(var.literal)
- writer.write(')')
+ writer.write(")")
writer.end_variable()
+
# get rid of node now, it shouldn't be used normally
del node
diff --git a/ext/django2jinja/example.py b/ext/django2jinja/example.py
index 2d4ab9a..9bd6b97 100644
--- a/ext/django2jinja/example.py
+++ b/ext/django2jinja/example.py
@@ -1,7 +1,8 @@
from django.conf import settings
-settings.configure(TEMPLATE_DIRS=['templates'], TEMPLATE_DEBUG=True)
+
+settings.configure(TEMPLATE_DIRS=["templates"], TEMPLATE_DEBUG=True)
from django2jinja import convert_templates, Writer
writer = Writer(use_jinja_autoescape=True)
-convert_templates('converted', writer=writer)
+convert_templates("converted", writer=writer)
diff --git a/ext/djangojinja2.py b/ext/djangojinja2.py
index 3c0c425..48c7cf6 100644
--- a/ext/djangojinja2.py
+++ b/ext/djangojinja2.py
@@ -45,10 +45,12 @@ def get_env():
def create_env():
"""Create a new Jinja environment."""
searchpath = list(settings.JINJA2_TEMPLATE_DIRS)
- return Environment(loader=FileSystemLoader(searchpath),
- auto_reload=settings.TEMPLATE_DEBUG,
- cache_size=getattr(settings, 'JINJA2_CACHE_SIZE', 400),
- extensions=getattr(settings, 'JINJA2_EXTENSIONS', ()))
+ return Environment(
+ loader=FileSystemLoader(searchpath),
+ auto_reload=settings.TEMPLATE_DEBUG,
+ cache_size=getattr(settings, "JINJA2_CACHE_SIZE", 400),
+ extensions=getattr(settings, "JINJA2_EXTENSIONS", ()),
+ )
def get_template(template_name, globals=None):
@@ -67,22 +69,23 @@ def select_template(templates, globals=None):
return env.get_template(template, globals=globals)
except TemplateNotFound:
continue
- raise TemplateDoesNotExist(', '.join(templates))
+ raise TemplateDoesNotExist(", ".join(templates))
-def render_to_string(template_name, context=None, request=None,
- processors=None):
+def render_to_string(template_name, context=None, request=None, processors=None):
"""Render a template into a string."""
context = dict(context or {})
if request is not None:
- context['request'] = request
+ context["request"] = request
for processor in chain(get_standard_processors(), processors or ()):
context.update(processor(request))
return get_template(template_name).render(context)
-def render_to_response(template_name, context=None, request=None,
- processors=None, mimetype=None):
+def render_to_response(
+ template_name, context=None, request=None, processors=None, mimetype=None
+):
"""Render a template into a response object."""
- return HttpResponse(render_to_string(template_name, context, request,
- processors), mimetype=mimetype)
+ return HttpResponse(
+ render_to_string(template_name, context, request, processors), mimetype=mimetype
+ )
diff --git a/ext/inlinegettext.py b/ext/inlinegettext.py
index fd545b7..50a0671 100644
--- a/ext/inlinegettext.py
+++ b/ext/inlinegettext.py
@@ -17,8 +17,8 @@ from jinja2.lexer import count_newlines
from jinja2.lexer import Token
-_outside_re = re.compile(r'\\?(gettext|_)\(')
-_inside_re = re.compile(r'\\?[()]')
+_outside_re = re.compile(r"\\?(gettext|_)\(")
+_inside_re = re.compile(r"\\?[()]")
class InlineGettext(Extension):
@@ -34,7 +34,7 @@ class InlineGettext(Extension):
paren_stack = 0
for token in stream:
- if token.type != 'data':
+ if token.type != "data":
yield token
continue
@@ -51,30 +51,33 @@ class InlineGettext(Extension):
new_pos = match.start()
if new_pos > pos:
preval = token.value[pos:new_pos]
- yield Token(lineno, 'data', preval)
+ yield Token(lineno, "data", preval)
lineno += count_newlines(preval)
gtok = match.group()
- if gtok[0] == '\\':
- yield Token(lineno, 'data', gtok[1:])
+ if gtok[0] == "\\":
+ yield Token(lineno, "data", gtok[1:])
elif not paren_stack:
- yield Token(lineno, 'block_begin', None)
- yield Token(lineno, 'name', 'trans')
- yield Token(lineno, 'block_end', None)
+ yield Token(lineno, "block_begin", None)
+ yield Token(lineno, "name", "trans")
+ yield Token(lineno, "block_end", None)
paren_stack = 1
else:
- if gtok == '(' or paren_stack > 1:
- yield Token(lineno, 'data', gtok)
- paren_stack += gtok == ')' and -1 or 1
+ if gtok == "(" or paren_stack > 1:
+ yield Token(lineno, "data", gtok)
+ paren_stack += gtok == ")" and -1 or 1
if not paren_stack:
- yield Token(lineno, 'block_begin', None)
- yield Token(lineno, 'name', 'endtrans')
- yield Token(lineno, 'block_end', None)
+ yield Token(lineno, "block_begin", None)
+ yield Token(lineno, "name", "endtrans")
+ yield Token(lineno, "block_end", None)
pos = match.end()
if pos < len(token.value):
- yield Token(lineno, 'data', token.value[pos:])
+ yield Token(lineno, "data", token.value[pos:])
if paren_stack:
- raise TemplateSyntaxError('unclosed gettext expression',
- token.lineno, stream.name,
- stream.filename)
+ raise TemplateSyntaxError(
+ "unclosed gettext expression",
+ token.lineno,
+ stream.name,
+ stream.filename,
+ )
diff --git a/scripts/generate_identifier_pattern.py b/scripts/generate_identifier_pattern.py
index 7db5f4a..96fedba 100755
--- a/scripts/generate_identifier_pattern.py
+++ b/scripts/generate_identifier_pattern.py
@@ -5,7 +5,7 @@ import re
import sys
if sys.version_info[0] < 3:
- raise RuntimeError('This needs to run on Python 3.')
+ raise RuntimeError("This needs to run on Python 3.")
def get_characters():
@@ -23,7 +23,7 @@ def get_characters():
for cp in range(sys.maxunicode + 1):
s = chr(cp)
- if ('a' + s).isidentifier() and not re.match(r'\w', s):
+ if ("a" + s).isidentifier() and not re.match(r"\w", s):
yield s
@@ -33,10 +33,7 @@ def collapse_ranges(data):
Source: https://stackoverflow.com/a/4629241/400617
"""
- for a, b in itertools.groupby(
- enumerate(data),
- lambda x: ord(x[1]) - x[0]
- ):
+ for a, b in itertools.groupby(enumerate(data), lambda x: ord(x[1]) - x[0]):
b = list(b)
yield b[0][1], b[-1][1]
@@ -55,23 +52,23 @@ def build_pattern(ranges):
out.append(a)
out.append(b)
else:
- out.append(f'{a}-{b}')
+ out.append("{}-{}".format(a, b))
- return ''.join(out)
+ return "".join(out)
def main():
"""Build the regex pattern and write it to the file
:file:`jinja2/_identifier.py`."""
pattern = build_pattern(collapse_ranges(get_characters()))
- filename = os.path.abspath(os.path.join(
- os.path.dirname(__file__), '..', 'jinja2', '_identifier.py'
- ))
+ filename = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), "..", "src", "jinja2", "_identifier.py")
+ )
- with open(filename, 'w', encoding='utf8') as f:
- f.write('# generated by scripts/generate_identifier_pattern.py\n')
- f.write(f'pattern = \'{pattern}\'\n')
+ with open(filename, "w", encoding="utf8") as f:
+ f.write("# generated by scripts/generate_identifier_pattern.py\n")
+ f.write('pattern = "{}"\n'.format(pattern))
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/scripts/jinja2-debug.py b/scripts/jinja2-debug.py
index 4f04436..96628dd 100755
--- a/scripts/jinja2-debug.py
+++ b/scripts/jinja2-debug.py
@@ -17,21 +17,23 @@ from werkzeug import script
import jinja2
-env = jinja2.Environment(extensions=['jinja2.ext.i18n', 'jinja2.ext.do',
- 'jinja2.ext.loopcontrols',
- 'jinja2.ext.with_',
- 'jinja2.ext.autoescape'],
- autoescape=True)
+env = jinja2.Environment(
+ extensions=[
+ "jinja2.ext.i18n",
+ "jinja2.ext.do",
+ "jinja2.ext.loopcontrols",
+ "jinja2.ext.with_",
+ "jinja2.ext.autoescape",
+ ],
+ autoescape=True,
+)
+
def shell_init_func():
def _compile(x):
print(env.compile(x, raw=True))
- result = {
- 'e': env,
- 'c': _compile,
- 't': env.from_string,
- 'p': env.parse
- }
+
+ result = {"e": env, "c": _compile, "t": env.from_string, "p": env.parse}
for key in jinja2.__all__:
result[key] = getattr(jinja2, key)
return result
@@ -40,8 +42,9 @@ def shell_init_func():
def action_compile():
print(env.compile(sys.stdin.read(), raw=True))
+
action_shell = script.make_shell(shell_init_func)
-if __name__ == '__main__':
+if __name__ == "__main__":
script.run()
diff --git a/scripts/make-release.py b/scripts/make-release.py
index 0b158dd..cd6ce23 100644
--- a/scripts/make-release.py
+++ b/scripts/make-release.py
@@ -20,21 +20,21 @@ from datetime import datetime
from subprocess import PIPE
from subprocess import Popen
-_date_strip_re = re.compile(r'(?<=\d)(st|nd|rd|th)')
+_date_strip_re = re.compile(r"(?<=\d)(st|nd|rd|th)")
def parse_changelog():
- with open('CHANGES.rst') as f:
+ with open("CHANGES.rst") as f:
lineiter = iter(f)
for line in lineiter:
- match = re.search('^Version\s+(.*)', line.strip())
+ match = re.search("^Version\s+(.*)", line.strip())
if match is None:
continue
version = match.group(1).strip()
- if next(lineiter).count('-') != len(match.group(0)):
+ if next(lineiter).count("-") != len(match.group(0)):
continue
while 1:
@@ -44,8 +44,8 @@ def parse_changelog():
break
match = re.search(
- r'(?:codename (.*),\s*)?released on (\w+\s+\d+\w+\s+\d+)(?i)',
- change_info
+ r"(?:codename (.*),\s*)?released on (\w+\s+\d+\w+\s+\d+)(?i)",
+ change_info,
)
if match is None:
@@ -57,17 +57,17 @@ def parse_changelog():
def bump_version(version):
try:
- parts = [int(i) for i in version.split('.')]
+ parts = [int(i) for i in version.split(".")]
except ValueError:
- fail('Current version is not numeric')
+ fail("Current version is not numeric")
parts[-1] += 1
- return '.'.join(map(str, parts))
+ return ".".join(map(str, parts))
def parse_date(string):
- string = _date_strip_re.sub('', string)
- return datetime.strptime(string, '%B %d %Y')
+ string = _date_strip_re.sub("", string)
+ return datetime.strptime(string, "%B %d %Y")
def set_filename_version(filename, version_number, pattern):
@@ -80,34 +80,33 @@ def set_filename_version(filename, version_number, pattern):
with open(filename) as f:
contents = re.sub(
- r"^(\s*%s\s*=\s*')(.+?)(')(?sm)" % pattern,
- inject_version, f.read()
+ r"^(\s*%s\s*=\s*')(.+?)(')(?sm)" % pattern, inject_version, f.read()
)
if not changed:
- fail('Could not find %s in %s', pattern, filename)
+ fail("Could not find %s in %s", pattern, filename)
- with open(filename, 'w') as f:
+ with open(filename, "w") as f:
f.write(contents)
def set_init_version(version):
- info('Setting __init__.py version to %s', version)
- set_filename_version('jinja2/__init__.py', version, '__version__')
+ info("Setting __init__.py version to %s", version)
+ set_filename_version("jinja2/__init__.py", version, "__version__")
def set_setup_version(version):
- info('Setting setup.py version to %s', version)
- set_filename_version('setup.py', version, 'version')
+ info("Setting setup.py version to %s", version)
+ set_filename_version("setup.py", version, "version")
def build_and_upload():
- cmd = [sys.executable, 'setup.py', 'sdist', 'bdist_wheel']
+ cmd = [sys.executable, "setup.py", "sdist", "bdist_wheel"]
Popen(cmd).wait()
def fail(message, *args):
- print('Error:', message % args, file=sys.stderr)
+ print("Error:", message % args, file=sys.stderr)
sys.exit(1)
@@ -116,39 +115,39 @@ def info(message, *args):
def get_git_tags():
- return set(
- Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines()
- )
+ return set(Popen(["git", "tag"], stdout=PIPE).communicate()[0].splitlines())
def git_is_clean():
- return Popen(['git', 'diff', '--quiet']).wait() == 0
+ return Popen(["git", "diff", "--quiet"]).wait() == 0
def make_git_commit(message, *args):
message = message % args
- Popen(['git', 'commit', '-am', message]).wait()
+ Popen(["git", "commit", "-am", message]).wait()
def make_git_tag(tag):
info('Tagging "%s"', tag)
- Popen(['git', 'tag', tag]).wait()
+ Popen(["git", "tag", tag]).wait()
def main():
- os.chdir(os.path.join(os.path.dirname(__file__), '..'))
+ os.chdir(os.path.join(os.path.dirname(__file__), ".."))
rv = parse_changelog()
if rv is None:
- fail('Could not parse changelog')
+ fail("Could not parse changelog")
version, release_date, codename = rv
- dev_version = bump_version(version) + '.dev'
+ dev_version = bump_version(version) + ".dev"
info(
- 'Releasing %s (codename %s, release date %s)',
- version, codename, release_date.strftime('%d/%m/%Y')
+ "Releasing %s (codename %s, release date %s)",
+ version,
+ codename,
+ release_date.strftime("%d/%m/%Y"),
)
tags = get_git_tags()
@@ -156,27 +155,24 @@ def main():
fail('Version "%s" is already tagged', version)
if release_date.date() != date.today():
- fail(
- 'Release date is not today (%s != %s)',
- release_date.date(), date.today()
- )
+ fail("Release date is not today (%s != %s)", release_date.date(), date.today())
if not git_is_clean():
- fail('You have uncommitted changes in git')
+ fail("You have uncommitted changes in git")
try:
import wheel
except ImportError:
- fail('You need to install the wheel package.')
+ fail("You need to install the wheel package.")
set_init_version(version)
set_setup_version(version)
- make_git_commit('Bump version number to %s', version)
+ make_git_commit("Bump version number to %s", version)
make_git_tag(version)
build_and_upload()
set_init_version(dev_version)
set_setup_version(dev_version)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/src/jinja2/_compat.py b/src/jinja2/_compat.py
index 55d8e35..02aed34 100644
--- a/src/jinja2/_compat.py
+++ b/src/jinja2/_compat.py
@@ -13,7 +13,7 @@
import sys
PY2 = sys.version_info[0] == 2
-PYPY = hasattr(sys, 'pypy_translation_info')
+PYPY = hasattr(sys, "pypy_translation_info")
_identity = lambda x: x
if not PY2:
@@ -29,6 +29,7 @@ if not PY2:
import pickle
from io import BytesIO, StringIO
+
NativeStringIO = StringIO
def reraise(tp, value, tb=None):
@@ -58,11 +59,13 @@ else:
import cPickle as pickle
from cStringIO import StringIO as BytesIO, StringIO
+
NativeStringIO = BytesIO
- exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
+ exec("def reraise(tp, value, tb=None):\n raise tp, value, tb")
from itertools import imap, izip, ifilter
+
intern = intern
def implements_iterator(cls):
@@ -72,12 +75,12 @@ else:
def implements_to_string(cls):
cls.__unicode__ = cls.__str__
- cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
+ cls.__str__ = lambda x: x.__unicode__().encode("utf-8")
return cls
def encode_filename(filename):
if isinstance(filename, unicode):
- return filename.encode('utf-8')
+ return filename.encode("utf-8")
return filename
@@ -89,7 +92,8 @@ def with_metaclass(meta, *bases):
class metaclass(type):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
- return type.__new__(metaclass, 'temporary_class', (), {})
+
+ return type.__new__(metaclass, "temporary_class", (), {})
try:
diff --git a/src/jinja2/_identifier.py b/src/jinja2/_identifier.py
index 2eac35d..0a93e95 100644
--- a/src/jinja2/_identifier.py
+++ b/src/jinja2/_identifier.py
@@ -1,2 +1,2 @@
# generated by scripts/generate_identifier_pattern.py
-pattern = '·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯'
+pattern = "·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯"
diff --git a/src/jinja2/asyncfilters.py b/src/jinja2/asyncfilters.py
index 967dcb4..451d3f8 100644
--- a/src/jinja2/asyncfilters.py
+++ b/src/jinja2/asyncfilters.py
@@ -7,7 +7,7 @@ from .asyncsupport import auto_await
async def auto_to_seq(value):
seq = []
- if hasattr(value, '__aiter__'):
+ if hasattr(value, "__aiter__"):
async for item in value:
seq.append(item)
else:
@@ -17,8 +17,7 @@ async def auto_to_seq(value):
async def async_select_or_reject(args, kwargs, modfunc, lookup_attr):
- seq, func = filters.prepare_select_or_reject(
- args, kwargs, modfunc, lookup_attr)
+ seq, func = filters.prepare_select_or_reject(args, kwargs, modfunc, lookup_attr)
if seq:
async for item in auto_aiter(seq):
if func(item):
@@ -27,12 +26,13 @@ async def async_select_or_reject(args, kwargs, modfunc, lookup_attr):
def dualfilter(normal_filter, async_filter):
wrap_evalctx = False
- if getattr(normal_filter, 'environmentfilter', False):
+ if getattr(normal_filter, "environmentfilter", False):
is_async = lambda args: args[0].is_async
wrap_evalctx = False
else:
- if not getattr(normal_filter, 'evalcontextfilter', False) and \
- not getattr(normal_filter, 'contextfilter', False):
+ if not getattr(normal_filter, "evalcontextfilter", False) and not getattr(
+ normal_filter, "contextfilter", False
+ ):
wrap_evalctx = True
is_async = lambda args: args[0].environment.is_async
@@ -56,6 +56,7 @@ def dualfilter(normal_filter, async_filter):
def asyncfiltervariant(original):
def decorator(f):
return dualfilter(original, f)
+
return decorator
@@ -64,19 +65,22 @@ async def do_first(environment, seq):
try:
return await auto_aiter(seq).__anext__()
except StopAsyncIteration:
- return environment.undefined('No first item, sequence was empty.')
+ return environment.undefined("No first item, sequence was empty.")
@asyncfiltervariant(filters.do_groupby)
async def do_groupby(environment, value, attribute):
expr = filters.make_attrgetter(environment, attribute)
- return [filters._GroupTuple(key, await auto_to_seq(values))
- for key, values in filters.groupby(sorted(
- await auto_to_seq(value), key=expr), expr)]
+ return [
+ filters._GroupTuple(key, await auto_to_seq(values))
+ for key, values in filters.groupby(
+ sorted(await auto_to_seq(value), key=expr), expr
+ )
+ ]
@asyncfiltervariant(filters.do_join)
-async def do_join(eval_ctx, value, d=u'', attribute=None):
+async def do_join(eval_ctx, value, d=u"", attribute=None):
return filters.do_join(eval_ctx, await auto_to_seq(value), d, attribute)
@@ -131,17 +135,17 @@ async def do_slice(value, slices, fill_with=None):
ASYNC_FILTERS = {
- 'first': do_first,
- 'groupby': do_groupby,
- 'join': do_join,
- 'list': do_list,
+ "first": do_first,
+ "groupby": do_groupby,
+ "join": do_join,
+ "list": do_list,
# we intentionally do not support do_last because that would be
# ridiculous
- 'reject': do_reject,
- 'rejectattr': do_rejectattr,
- 'map': do_map,
- 'select': do_select,
- 'selectattr': do_selectattr,
- 'sum': do_sum,
- 'slice': do_slice,
+ "reject": do_reject,
+ "rejectattr": do_rejectattr,
+ "map": do_map,
+ "select": do_select,
+ "selectattr": do_selectattr,
+ "sum": do_sum,
+ "slice": do_slice,
}
diff --git a/src/jinja2/asyncsupport.py b/src/jinja2/asyncsupport.py
index f7da273..b2fd6e4 100644
--- a/src/jinja2/asyncsupport.py
+++ b/src/jinja2/asyncsupport.py
@@ -23,9 +23,11 @@ from .utils import missing
async def concat_async(async_gen):
rv = []
+
async def collect():
async for event in async_gen:
rv.append(event)
+
await collect()
return concat(rv)
@@ -47,17 +49,18 @@ def wrap_generate_func(original_generate):
yield loop.run_until_complete(async_gen.__anext__())
except StopAsyncIteration:
pass
+
def generate(self, *args, **kwargs):
if not self.environment.is_async:
return original_generate(self, *args, **kwargs)
return _convert_generator(self, asyncio.get_event_loop(), args, kwargs)
+
return update_wrapper(generate, original_generate)
async def render_async(self, *args, **kwargs):
if not self.environment.is_async:
- raise RuntimeError('The environment was not created with async mode '
- 'enabled.')
+ raise RuntimeError("The environment was not created with async mode enabled.")
vars = dict(*args, **kwargs)
ctx = self.new_context(vars)
@@ -74,6 +77,7 @@ def wrap_render_func(original_render):
return original_render(self, *args, **kwargs)
loop = asyncio.get_event_loop()
return loop.run_until_complete(self.render_async(*args, **kwargs))
+
return update_wrapper(render, original_render)
@@ -107,6 +111,7 @@ def wrap_macro_invoke(original_invoke):
if not self._environment.is_async:
return original_invoke(self, arguments, autoescape)
return async_invoke(self, arguments, autoescape)
+
return update_wrapper(_invoke, original_invoke)
@@ -122,9 +127,9 @@ def wrap_default_module(original_default_module):
@internalcode
def _get_default_module(self):
if self.environment.is_async:
- raise RuntimeError('Template module attribute is unavailable '
- 'in async mode')
+ raise RuntimeError("Template module attribute is unavailable in async mode")
return original_default_module(self)
+
return _get_default_module
@@ -138,29 +143,29 @@ async def make_module_async(self, vars=None, shared=False, locals=None):
def patch_template():
from jinja2 import Template
+
Template.generate = wrap_generate_func(Template.generate)
- Template.generate_async = update_wrapper(
- generate_async, Template.generate_async)
- Template.render_async = update_wrapper(
- render_async, Template.render_async)
+ Template.generate_async = update_wrapper(generate_async, Template.generate_async)
+ Template.render_async = update_wrapper(render_async, Template.render_async)
Template.render = wrap_render_func(Template.render)
- Template._get_default_module = wrap_default_module(
- Template._get_default_module)
+ Template._get_default_module = wrap_default_module(Template._get_default_module)
Template._get_default_module_async = get_default_module_async
Template.make_module_async = update_wrapper(
- make_module_async, Template.make_module_async)
+ make_module_async, Template.make_module_async
+ )
def patch_runtime():
from jinja2.runtime import BlockReference, Macro
- BlockReference.__call__ = wrap_block_reference_call(
- BlockReference.__call__)
+
+ BlockReference.__call__ = wrap_block_reference_call(BlockReference.__call__)
Macro._invoke = wrap_macro_invoke(Macro._invoke)
def patch_filters():
from jinja2.filters import FILTERS
from jinja2.asyncfilters import ASYNC_FILTERS
+
FILTERS.update(ASYNC_FILTERS)
@@ -177,7 +182,7 @@ async def auto_await(value):
async def auto_aiter(iterable):
- if hasattr(iterable, '__aiter__'):
+ if hasattr(iterable, "__aiter__"):
async for item in iterable:
yield item
return
@@ -252,6 +257,7 @@ class AsyncLoopContext(LoopContext):
async def make_async_loop_context(iterable, undefined, recurse=None, depth0=0):
import warnings
+
warnings.warn(
"This template must be recompiled with at least Jinja 2.11, or"
" it will fail in 3.0.",
diff --git a/src/jinja2/bccache.py b/src/jinja2/bccache.py
index 6466df6..c2b877e 100644
--- a/src/jinja2/bccache.py
+++ b/src/jinja2/bccache.py
@@ -56,9 +56,11 @@ bc_version = 3
# reason for this is that Python tends to segfault if fed earlier bytecode
# versions because someone thought it would be a good idea to reuse opcodes
# or make Python incompatible with earlier versions.
-bc_magic = 'j2'.encode('ascii') + \
- pickle.dumps(bc_version, 2) + \
- pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
+bc_magic = (
+ "j2".encode("ascii")
+ + pickle.dumps(bc_version, 2)
+ + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
+)
class Bucket(object):
@@ -102,7 +104,7 @@ class Bucket(object):
def write_bytecode(self, f):
"""Dump the bytecode into the file or file like object passed."""
if self.code is None:
- raise TypeError('can\'t write empty bucket')
+ raise TypeError("can't write empty bucket")
f.write(bc_magic)
pickle.dump(self.checksum, f, 2)
marshal_dump(self.code, f)
@@ -169,17 +171,17 @@ class BytecodeCache(object):
def get_cache_key(self, name, filename=None):
"""Returns the unique hash key for this template name."""
- hash = sha1(name.encode('utf-8'))
+ hash = sha1(name.encode("utf-8"))
if filename is not None:
- filename = '|' + filename
+ filename = "|" + filename
if isinstance(filename, text_type):
- filename = filename.encode('utf-8')
+ filename = filename.encode("utf-8")
hash.update(filename)
return hash.hexdigest()
def get_source_checksum(self, source):
"""Returns a checksum for the source."""
- return sha1(source.encode('utf-8')).hexdigest()
+ return sha1(source.encode("utf-8")).hexdigest()
def get_bucket(self, environment, name, filename, source):
"""Return a cache bucket for the given template. All arguments are
@@ -214,7 +216,7 @@ class FileSystemBytecodeCache(BytecodeCache):
This bytecode cache supports clearing of the cache using the clear method.
"""
- def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
+ def __init__(self, directory=None, pattern="__jinja2_%s.cache"):
if directory is None:
directory = self._get_default_cache_dir()
self.directory = directory
@@ -222,19 +224,21 @@ class FileSystemBytecodeCache(BytecodeCache):
def _get_default_cache_dir(self):
def _unsafe_dir():
- raise RuntimeError('Cannot determine safe temp directory. You '
- 'need to explicitly provide one.')
+ raise RuntimeError(
+ "Cannot determine safe temp directory. You "
+ "need to explicitly provide one."
+ )
tmpdir = tempfile.gettempdir()
# On windows the temporary directory is used specific unless
# explicitly forced otherwise. We can just use that.
- if os.name == 'nt':
+ if os.name == "nt":
return tmpdir
- if not hasattr(os, 'getuid'):
+ if not hasattr(os, "getuid"):
_unsafe_dir()
- dirname = '_jinja2-cache-%d' % os.getuid()
+ dirname = "_jinja2-cache-%d" % os.getuid()
actual_dir = os.path.join(tmpdir, dirname)
try:
@@ -245,18 +249,22 @@ class FileSystemBytecodeCache(BytecodeCache):
try:
os.chmod(actual_dir, stat.S_IRWXU)
actual_dir_stat = os.lstat(actual_dir)
- if actual_dir_stat.st_uid != os.getuid() \
- or not stat.S_ISDIR(actual_dir_stat.st_mode) \
- or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU:
+ if (
+ actual_dir_stat.st_uid != os.getuid()
+ or not stat.S_ISDIR(actual_dir_stat.st_mode)
+ or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU
+ ):
_unsafe_dir()
except OSError as e:
if e.errno != errno.EEXIST:
raise
actual_dir_stat = os.lstat(actual_dir)
- if actual_dir_stat.st_uid != os.getuid() \
- or not stat.S_ISDIR(actual_dir_stat.st_mode) \
- or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU:
+ if (
+ actual_dir_stat.st_uid != os.getuid()
+ or not stat.S_ISDIR(actual_dir_stat.st_mode)
+ or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU
+ ):
_unsafe_dir()
return actual_dir
@@ -265,7 +273,7 @@ class FileSystemBytecodeCache(BytecodeCache):
return path.join(self.directory, self.pattern % bucket.key)
def load_bytecode(self, bucket):
- f = open_if_exists(self._get_cache_filename(bucket), 'rb')
+ f = open_if_exists(self._get_cache_filename(bucket), "rb")
if f is not None:
try:
bucket.load_bytecode(f)
@@ -273,7 +281,7 @@ class FileSystemBytecodeCache(BytecodeCache):
f.close()
def dump_bytecode(self, bucket):
- f = open(self._get_cache_filename(bucket), 'wb')
+ f = open(self._get_cache_filename(bucket), "wb")
try:
bucket.write_bytecode(f)
finally:
@@ -284,7 +292,8 @@ class FileSystemBytecodeCache(BytecodeCache):
# write access on the file system and the function does not exist
# normally.
from os import remove
- files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
+
+ files = fnmatch.filter(listdir(self.directory), self.pattern % "*")
for filename in files:
try:
remove(path.join(self.directory, filename))
@@ -337,8 +346,13 @@ class MemcachedBytecodeCache(BytecodeCache):
`ignore_memcache_errors` parameter.
"""
- def __init__(self, client, prefix='jinja2/bytecode/', timeout=None,
- ignore_memcache_errors=True):
+ def __init__(
+ self,
+ client,
+ prefix="jinja2/bytecode/",
+ timeout=None,
+ ignore_memcache_errors=True,
+ ):
self.client = client
self.prefix = prefix
self.timeout = timeout
diff --git a/src/jinja2/compiler.py b/src/jinja2/compiler.py
index 308042a..324bc0f 100644
--- a/src/jinja2/compiler.py
+++ b/src/jinja2/compiler.py
@@ -35,35 +35,35 @@ from .utils import Markup
from .visitor import NodeVisitor
operators = {
- 'eq': '==',
- 'ne': '!=',
- 'gt': '>',
- 'gteq': '>=',
- 'lt': '<',
- 'lteq': '<=',
- 'in': 'in',
- 'notin': 'not in'
+ "eq": "==",
+ "ne": "!=",
+ "gt": ">",
+ "gteq": ">=",
+ "lt": "<",
+ "lteq": "<=",
+ "in": "in",
+ "notin": "not in",
}
# what method to iterate over items do we want to use for dict iteration
# in generated code? on 2.x let's go with iteritems, on 3.x with items
-if hasattr(dict, 'iteritems'):
- dict_item_iter = 'iteritems'
+if hasattr(dict, "iteritems"):
+ dict_item_iter = "iteritems"
else:
- dict_item_iter = 'items'
+ dict_item_iter = "items"
-code_features = ['division']
+code_features = ["division"]
# does this python version support generator stops? (PEP 0479)
try:
- exec('from __future__ import generator_stop')
- code_features.append('generator_stop')
+ exec("from __future__ import generator_stop")
+ code_features.append("generator_stop")
except SyntaxError:
pass
# does this python version support yield from?
try:
- exec('def f(): yield from x()')
+ exec("def f(): yield from x()")
except SyntaxError:
supports_yield_from = False
else:
@@ -78,17 +78,19 @@ def optimizeconst(f):
if new_node != node:
return self.visit(new_node, frame)
return f(self, node, frame, **kwargs)
+
return update_wrapper(new_func, f)
-def generate(node, environment, name, filename, stream=None,
- defer_init=False, optimized=True):
+def generate(
+ node, environment, name, filename, stream=None, defer_init=False, optimized=True
+):
"""Generate the python source for a node tree."""
if not isinstance(node, nodes.Template):
- raise TypeError('Can\'t compile non template nodes')
- generator = environment.code_generator_class(environment, name, filename,
- stream, defer_init,
- optimized)
+ raise TypeError("Can't compile non template nodes")
+ generator = environment.code_generator_class(
+ environment, name, filename, stream, defer_init, optimized
+ )
generator.visit(node)
if stream is None:
return generator.stream.getvalue()
@@ -129,7 +131,6 @@ def find_undeclared(nodes, names):
class MacroRef(object):
-
def __init__(self, node):
self.node = node
self.accesses_caller = False
@@ -142,8 +143,7 @@ class Frame(object):
def __init__(self, eval_ctx, parent=None, level=None):
self.eval_ctx = eval_ctx
- self.symbols = Symbols(parent and parent.symbols or None,
- level=level)
+ self.symbols = Symbols(parent and parent.symbols or None, level=level)
# a toplevel frame is the root + soft frames such as if conditions.
self.toplevel = False
@@ -233,7 +233,7 @@ class UndeclaredNameVisitor(NodeVisitor):
self.undeclared = set()
def visit_Name(self, node):
- if node.ctx == 'load' and node.name in self.names:
+ if node.ctx == "load" and node.name in self.names:
self.undeclared.add(node.name)
if self.undeclared == self.names:
raise VisitorExit()
@@ -252,9 +252,9 @@ class CompilerExit(Exception):
class CodeGenerator(NodeVisitor):
-
- def __init__(self, environment, name, filename, stream=None,
- defer_init=False, optimized=True):
+ def __init__(
+ self, environment, name, filename, stream=None, defer_init=False, optimized=True
+ ):
if stream is None:
stream = NativeStringIO()
self.environment = environment
@@ -316,7 +316,7 @@ class CodeGenerator(NodeVisitor):
self._param_def_block = []
# Tracks the current context.
- self._context_reference_stack = ['context']
+ self._context_reference_stack = ["context"]
# -- Various compilation helpers
@@ -327,30 +327,30 @@ class CodeGenerator(NodeVisitor):
def temporary_identifier(self):
"""Get a new unique identifier."""
self._last_identifier += 1
- return 't_%d' % self._last_identifier
+ return "t_%d" % self._last_identifier
def buffer(self, frame):
"""Enable buffering for the frame from that point onwards."""
frame.buffer = self.temporary_identifier()
- self.writeline('%s = []' % frame.buffer)
+ self.writeline("%s = []" % frame.buffer)
def return_buffer_contents(self, frame, force_unescaped=False):
"""Return the buffer contents of the frame."""
if not force_unescaped:
if frame.eval_ctx.volatile:
- self.writeline('if context.eval_ctx.autoescape:')
+ self.writeline("if context.eval_ctx.autoescape:")
self.indent()
- self.writeline('return Markup(concat(%s))' % frame.buffer)
+ self.writeline("return Markup(concat(%s))" % frame.buffer)
self.outdent()
- self.writeline('else:')
+ self.writeline("else:")
self.indent()
- self.writeline('return concat(%s)' % frame.buffer)
+ self.writeline("return concat(%s)" % frame.buffer)
self.outdent()
return
elif frame.eval_ctx.autoescape:
- self.writeline('return Markup(concat(%s))' % frame.buffer)
+ self.writeline("return Markup(concat(%s))" % frame.buffer)
return
- self.writeline('return concat(%s)' % frame.buffer)
+ self.writeline("return concat(%s)" % frame.buffer)
def indent(self):
"""Indent by one."""
@@ -363,14 +363,14 @@ class CodeGenerator(NodeVisitor):
def start_write(self, frame, node=None):
"""Yield or write into the frame buffer."""
if frame.buffer is None:
- self.writeline('yield ', node)
+ self.writeline("yield ", node)
else:
- self.writeline('%s.append(' % frame.buffer, node)
+ self.writeline("%s.append(" % frame.buffer, node)
def end_write(self, frame):
"""End the writing process started by `start_write`."""
if frame.buffer is not None:
- self.write(')')
+ self.write(")")
def simple_write(self, s, frame, node=None):
"""Simple shortcut for start_write + write + end_write."""
@@ -383,7 +383,7 @@ class CodeGenerator(NodeVisitor):
is no buffer a dummy ``if 0: yield None`` is written automatically.
"""
try:
- self.writeline('pass')
+ self.writeline("pass")
for node in nodes:
self.visit(node, frame)
except CompilerExit:
@@ -393,14 +393,13 @@ class CodeGenerator(NodeVisitor):
"""Write a string into the output stream."""
if self._new_lines:
if not self._first_write:
- self.stream.write('\n' * self._new_lines)
+ self.stream.write("\n" * self._new_lines)
self.code_lineno += self._new_lines
if self._write_debug_info is not None:
- self.debug_info.append((self._write_debug_info,
- self.code_lineno))
+ self.debug_info.append((self._write_debug_info, self.code_lineno))
self._write_debug_info = None
self._first_write = False
- self.stream.write(' ' * self._indentation)
+ self.stream.write(" " * self._indentation)
self._new_lines = 0
self.stream.write(x)
@@ -432,41 +431,41 @@ class CodeGenerator(NodeVisitor):
break
for arg in node.args:
- self.write(', ')
+ self.write(", ")
self.visit(arg, frame)
if not kwarg_workaround:
for kwarg in node.kwargs:
- self.write(', ')
+ self.write(", ")
self.visit(kwarg, frame)
if extra_kwargs is not None:
for key, value in iteritems(extra_kwargs):
- self.write(', %s=%s' % (key, value))
+ self.write(", %s=%s" % (key, value))
if node.dyn_args:
- self.write(', *')
+ self.write(", *")
self.visit(node.dyn_args, frame)
if kwarg_workaround:
if node.dyn_kwargs is not None:
- self.write(', **dict({')
+ self.write(", **dict({")
else:
- self.write(', **{')
+ self.write(", **{")
for kwarg in node.kwargs:
- self.write('%r: ' % kwarg.key)
+ self.write("%r: " % kwarg.key)
self.visit(kwarg.value, frame)
- self.write(', ')
+ self.write(", ")
if extra_kwargs is not None:
for key, value in iteritems(extra_kwargs):
- self.write('%r: %s, ' % (key, value))
+ self.write("%r: %s, " % (key, value))
if node.dyn_kwargs is not None:
- self.write('}, **')
+ self.write("}, **")
self.visit(node.dyn_kwargs, frame)
- self.write(')')
+ self.write(")")
else:
- self.write('}')
+ self.write("}")
elif node.dyn_kwargs is not None:
- self.write(', **')
+ self.write(", **")
self.visit(node.dyn_kwargs, frame)
def pull_dependencies(self, nodes):
@@ -474,13 +473,14 @@ class CodeGenerator(NodeVisitor):
visitor = DependencyFinderVisitor()
for node in nodes:
visitor.visit(node)
- for dependency in 'filters', 'tests':
+ for dependency in "filters", "tests":
mapping = getattr(self, dependency)
for name in getattr(visitor, dependency):
if name not in mapping:
mapping[name] = self.temporary_identifier()
- self.writeline('%s = environment.%s[%r]' %
- (mapping[name], dependency, name))
+ self.writeline(
+ "%s = environment.%s[%r]" % (mapping[name], dependency, name)
+ )
def enter_frame(self, frame):
undefs = []
@@ -488,16 +488,15 @@ class CodeGenerator(NodeVisitor):
if action == VAR_LOAD_PARAMETER:
pass
elif action == VAR_LOAD_RESOLVE:
- self.writeline('%s = %s(%r)' %
- (target, self.get_resolve_func(), param))
+ self.writeline("%s = %s(%r)" % (target, self.get_resolve_func(), param))
elif action == VAR_LOAD_ALIAS:
- self.writeline('%s = %s' % (target, param))
+ self.writeline("%s = %s" % (target, param))
elif action == VAR_LOAD_UNDEFINED:
undefs.append(target)
else:
- raise NotImplementedError('unknown load instruction')
+ raise NotImplementedError("unknown load instruction")
if undefs:
- self.writeline('%s = missing' % ' = '.join(undefs))
+ self.writeline("%s = missing" % " = ".join(undefs))
def leave_frame(self, frame, with_python_scope=False):
if not with_python_scope:
@@ -505,12 +504,12 @@ class CodeGenerator(NodeVisitor):
for target, _ in iteritems(frame.symbols.loads):
undefs.append(target)
if undefs:
- self.writeline('%s = missing' % ' = '.join(undefs))
+ self.writeline("%s = missing" % " = ".join(undefs))
def func(self, name):
if self.environment.is_async:
- return 'async def %s' % name
- return 'def %s' % name
+ return "async def %s" % name
+ return "def %s" % name
def macro_body(self, node, frame):
"""Dump the function def of a macro or call block."""
@@ -522,15 +521,15 @@ class CodeGenerator(NodeVisitor):
skip_special_params = set()
args = []
for idx, arg in enumerate(node.args):
- if arg.name == 'caller':
+ if arg.name == "caller":
explicit_caller = idx
- if arg.name in ('kwargs', 'varargs'):
+ if arg.name in ("kwargs", "varargs"):
skip_special_params.add(arg.name)
args.append(frame.symbols.ref(arg.name))
- undeclared = find_undeclared(node.body, ('caller', 'kwargs', 'varargs'))
+ undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs"))
- if 'caller' in undeclared:
+ if "caller" in undeclared:
# In older Jinja versions there was a bug that allowed caller
# to retain the special behavior even if it was mentioned in
# the argument list. However thankfully this was only really
@@ -541,23 +540,26 @@ class CodeGenerator(NodeVisitor):
try:
node.defaults[explicit_caller - len(node.args)]
except IndexError:
- self.fail('When defining macros or call blocks the '
- 'special "caller" argument must be omitted '
- 'or be given a default.', node.lineno)
+ self.fail(
+ "When defining macros or call blocks the "
+ 'special "caller" argument must be omitted '
+ "or be given a default.",
+ node.lineno,
+ )
else:
- args.append(frame.symbols.declare_parameter('caller'))
+ args.append(frame.symbols.declare_parameter("caller"))
macro_ref.accesses_caller = True
- if 'kwargs' in undeclared and not 'kwargs' in skip_special_params:
- args.append(frame.symbols.declare_parameter('kwargs'))
+ if "kwargs" in undeclared and not "kwargs" in skip_special_params:
+ args.append(frame.symbols.declare_parameter("kwargs"))
macro_ref.accesses_kwargs = True
- if 'varargs' in undeclared and not 'varargs' in skip_special_params:
- args.append(frame.symbols.declare_parameter('varargs'))
+ if "varargs" in undeclared and not "varargs" in skip_special_params:
+ args.append(frame.symbols.declare_parameter("varargs"))
macro_ref.accesses_varargs = True
# macros are delayed, they never require output checks
frame.require_output_check = False
frame.symbols.analyze_node(node)
- self.writeline('%s(%s):' % (self.func('macro'), ', '.join(args)), node)
+ self.writeline("%s(%s):" % (self.func("macro"), ", ".join(args)), node)
self.indent()
self.buffer(frame)
@@ -566,17 +568,17 @@ class CodeGenerator(NodeVisitor):
self.push_parameter_definitions(frame)
for idx, arg in enumerate(node.args):
ref = frame.symbols.ref(arg.name)
- self.writeline('if %s is missing:' % ref)
+ self.writeline("if %s is missing:" % ref)
self.indent()
try:
default = node.defaults[idx - len(node.args)]
except IndexError:
- self.writeline('%s = undefined(%r, name=%r)' % (
- ref,
- 'parameter %r was not provided' % arg.name,
- arg.name))
+ self.writeline(
+ "%s = undefined(%r, name=%r)"
+ % (ref, "parameter %r was not provided" % arg.name, arg.name)
+ )
else:
- self.writeline('%s = ' % ref)
+ self.writeline("%s = " % ref)
self.visit(default, frame)
self.mark_parameter_stored(ref)
self.outdent()
@@ -591,38 +593,46 @@ class CodeGenerator(NodeVisitor):
def macro_def(self, macro_ref, frame):
"""Dump the macro definition for the def created by macro_body."""
- arg_tuple = ', '.join(repr(x.name) for x in macro_ref.node.args)
- name = getattr(macro_ref.node, 'name', None)
+ arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
+ name = getattr(macro_ref.node, "name", None)
if len(macro_ref.node.args) == 1:
- arg_tuple += ','
- self.write('Macro(environment, macro, %r, (%s), %r, %r, %r, '
- 'context.eval_ctx.autoescape)' %
- (name, arg_tuple, macro_ref.accesses_kwargs,
- macro_ref.accesses_varargs, macro_ref.accesses_caller))
+ arg_tuple += ","
+ self.write(
+ "Macro(environment, macro, %r, (%s), %r, %r, %r, "
+ "context.eval_ctx.autoescape)"
+ % (
+ name,
+ arg_tuple,
+ macro_ref.accesses_kwargs,
+ macro_ref.accesses_varargs,
+ macro_ref.accesses_caller,
+ )
+ )
def position(self, node):
"""Return a human readable position for the node."""
- rv = 'line %d' % node.lineno
+ rv = "line %d" % node.lineno
if self.name is not None:
- rv += ' in ' + repr(self.name)
+ rv += " in " + repr(self.name)
return rv
def dump_local_context(self, frame):
- return '{%s}' % ', '.join(
- '%r: %s' % (name, target) for name, target
- in iteritems(frame.symbols.dump_stores()))
+ return "{%s}" % ", ".join(
+ "%r: %s" % (name, target)
+ for name, target in iteritems(frame.symbols.dump_stores())
+ )
def write_commons(self):
"""Writes a common preamble that is used by root and block functions.
Primarily this sets up common local helpers and enforces a generator
through a dead branch.
"""
- self.writeline('resolve = context.resolve_or_missing')
- self.writeline('undefined = environment.undefined')
+ self.writeline("resolve = context.resolve_or_missing")
+ self.writeline("undefined = environment.undefined")
# always use the standard Undefined class for the implicit else of
# conditional expressions
- self.writeline('cond_expr_undefined = Undefined')
- self.writeline('if 0: yield None')
+ self.writeline("cond_expr_undefined = Undefined")
+ self.writeline("if 0: yield None")
def push_parameter_definitions(self, frame):
"""Pushes all parameter targets from the given frame into a local
@@ -655,12 +665,12 @@ class CodeGenerator(NodeVisitor):
def get_resolve_func(self):
target = self._context_reference_stack[-1]
- if target == 'context':
- return 'resolve'
- return '%s.resolve' % target
+ if target == "context":
+ return "resolve"
+ return "%s.resolve" % target
def derive_context(self, frame):
- return '%s.derived(%s)' % (
+ return "%s.derived(%s)" % (
self.get_context_ref(),
self.dump_local_context(frame),
)
@@ -682,44 +692,48 @@ class CodeGenerator(NodeVisitor):
vars = self._assign_stack.pop()
if not frame.toplevel or not vars:
return
- public_names = [x for x in vars if x[:1] != '_']
+ public_names = [x for x in vars if x[:1] != "_"]
if len(vars) == 1:
name = next(iter(vars))
ref = frame.symbols.ref(name)
- self.writeline('context.vars[%r] = %s' % (name, ref))
+ self.writeline("context.vars[%r] = %s" % (name, ref))
else:
- self.writeline('context.vars.update({')
+ self.writeline("context.vars.update({")
for idx, name in enumerate(vars):
if idx:
- self.write(', ')
+ self.write(", ")
ref = frame.symbols.ref(name)
- self.write('%r: %s' % (name, ref))
- self.write('})')
+ self.write("%r: %s" % (name, ref))
+ self.write("})")
if public_names:
if len(public_names) == 1:
- self.writeline('context.exported_vars.add(%r)' %
- public_names[0])
+ self.writeline("context.exported_vars.add(%r)" % public_names[0])
else:
- self.writeline('context.exported_vars.update((%s))' %
- ', '.join(imap(repr, public_names)))
+ self.writeline(
+ "context.exported_vars.update((%s))"
+ % ", ".join(imap(repr, public_names))
+ )
# -- Statement Visitors
def visit_Template(self, node, frame=None):
- assert frame is None, 'no root frame allowed'
+ assert frame is None, "no root frame allowed"
eval_ctx = EvalContext(self.environment, self.name)
from jinja2.runtime import __all__ as exported
- self.writeline('from __future__ import %s' % ', '.join(code_features))
- self.writeline('from jinja2.runtime import ' + ', '.join(exported))
+
+ self.writeline("from __future__ import %s" % ", ".join(code_features))
+ self.writeline("from jinja2.runtime import " + ", ".join(exported))
if self.environment.is_async:
- self.writeline('from jinja2.asyncsupport import auto_await, '
- 'auto_aiter, AsyncLoopContext')
+ self.writeline(
+ "from jinja2.asyncsupport import auto_await, "
+ "auto_aiter, AsyncLoopContext"
+ )
# if we want a deferred initialization we cannot move the
# environment into a local name
- envenv = not self.defer_init and ', environment=environment' or ''
+ envenv = not self.defer_init and ", environment=environment" or ""
# do we have an extends tag at all? If not, we can save some
# overhead by just not processing any inheritance code.
@@ -728,7 +742,7 @@ class CodeGenerator(NodeVisitor):
# find all blocks
for block in node.find_all(nodes.Block):
if block.name in self.blocks:
- self.fail('block %r defined twice' % block.name, block.lineno)
+ self.fail("block %r defined twice" % block.name, block.lineno)
self.blocks[block.name] = block
# find all imports and import them
@@ -736,32 +750,32 @@ class CodeGenerator(NodeVisitor):
if import_.importname not in self.import_aliases:
imp = import_.importname
self.import_aliases[imp] = alias = self.temporary_identifier()
- if '.' in imp:
- module, obj = imp.rsplit('.', 1)
- self.writeline('from %s import %s as %s' %
- (module, obj, alias))
+ if "." in imp:
+ module, obj = imp.rsplit(".", 1)
+ self.writeline("from %s import %s as %s" % (module, obj, alias))
else:
- self.writeline('import %s as %s' % (imp, alias))
+ self.writeline("import %s as %s" % (imp, alias))
# add the load name
- self.writeline('name = %r' % self.name)
+ self.writeline("name = %r" % self.name)
# generate the root render function.
- self.writeline('%s(context, missing=missing%s):' %
- (self.func('root'), envenv), extra=1)
+ self.writeline(
+ "%s(context, missing=missing%s):" % (self.func("root"), envenv), extra=1
+ )
self.indent()
self.write_commons()
# process the root
frame = Frame(eval_ctx)
- if 'self' in find_undeclared(node.body, ('self',)):
- ref = frame.symbols.declare_parameter('self')
- self.writeline('%s = TemplateReference(context)' % ref)
+ if "self" in find_undeclared(node.body, ("self",)):
+ ref = frame.symbols.declare_parameter("self")
+ self.writeline("%s = TemplateReference(context)" % ref)
frame.symbols.analyze_node(node)
frame.toplevel = frame.rootlevel = True
frame.require_output_check = have_extends and not self.has_known_extends
if have_extends:
- self.writeline('parent_template = None')
+ self.writeline("parent_template = None")
self.enter_frame(frame)
self.pull_dependencies(node.body)
self.blockvisit(node.body, frame)
@@ -772,39 +786,42 @@ class CodeGenerator(NodeVisitor):
if have_extends:
if not self.has_known_extends:
self.indent()
- self.writeline('if parent_template is not None:')
+ self.writeline("if parent_template is not None:")
self.indent()
if supports_yield_from and not self.environment.is_async:
- self.writeline('yield from parent_template.'
- 'root_render_func(context)')
+ self.writeline("yield from parent_template.root_render_func(context)")
else:
- self.writeline('%sfor event in parent_template.'
- 'root_render_func(context):' %
- (self.environment.is_async and 'async ' or ''))
+ self.writeline(
+ "%sfor event in parent_template."
+ "root_render_func(context):"
+ % (self.environment.is_async and "async " or "")
+ )
self.indent()
- self.writeline('yield event')
+ self.writeline("yield event")
self.outdent()
self.outdent(1 + (not self.has_known_extends))
# at this point we now have the blocks collected and can visit them too.
for name, block in iteritems(self.blocks):
- self.writeline('%s(context, missing=missing%s):' %
- (self.func('block_' + name), envenv),
- block, 1)
+ self.writeline(
+ "%s(context, missing=missing%s):"
+ % (self.func("block_" + name), envenv),
+ block,
+ 1,
+ )
self.indent()
self.write_commons()
# It's important that we do not make this frame a child of the
# toplevel template. This would cause a variety of
# interesting issues with identifier tracking.
block_frame = Frame(eval_ctx)
- undeclared = find_undeclared(block.body, ('self', 'super'))
- if 'self' in undeclared:
- ref = block_frame.symbols.declare_parameter('self')
- self.writeline('%s = TemplateReference(context)' % ref)
- if 'super' in undeclared:
- ref = block_frame.symbols.declare_parameter('super')
- self.writeline('%s = context.super(%r, '
- 'block_%s)' % (ref, name, name))
+ undeclared = find_undeclared(block.body, ("self", "super"))
+ if "self" in undeclared:
+ ref = block_frame.symbols.declare_parameter("self")
+ self.writeline("%s = TemplateReference(context)" % ref)
+ if "super" in undeclared:
+ ref = block_frame.symbols.declare_parameter("super")
+ self.writeline("%s = context.super(%r, block_%s)" % (ref, name, name))
block_frame.symbols.analyze_node(block)
block_frame.block = name
self.enter_frame(block_frame)
@@ -813,13 +830,15 @@ class CodeGenerator(NodeVisitor):
self.leave_frame(block_frame, with_python_scope=True)
self.outdent()
- self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
- for x in self.blocks),
- extra=1)
+ self.writeline(
+ "blocks = {%s}" % ", ".join("%r: block_%s" % (x, x) for x in self.blocks),
+ extra=1,
+ )
# add a function that returns the debug info
- self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x
- in self.debug_info))
+ self.writeline(
+ "debug_info = %r" % "&".join("%s=%s" % x for x in self.debug_info)
+ )
def visit_Block(self, node, frame):
"""Call a block and register it for the template."""
@@ -830,7 +849,7 @@ class CodeGenerator(NodeVisitor):
if self.has_known_extends:
return
if self.extends_so_far > 0:
- self.writeline('if parent_template is None:')
+ self.writeline("if parent_template is None:")
self.indent()
level += 1
@@ -839,16 +858,22 @@ class CodeGenerator(NodeVisitor):
else:
context = self.get_context_ref()
- if supports_yield_from and not self.environment.is_async and \
- frame.buffer is None:
- self.writeline('yield from context.blocks[%r][0](%s)' % (
- node.name, context), node)
+ if (
+ supports_yield_from
+ and not self.environment.is_async
+ and frame.buffer is None
+ ):
+ self.writeline(
+ "yield from context.blocks[%r][0](%s)" % (node.name, context), node
+ )
else:
- loop = self.environment.is_async and 'async for' or 'for'
- self.writeline('%s event in context.blocks[%r][0](%s):' % (
- loop, node.name, context), node)
+ loop = self.environment.is_async and "async for" or "for"
+ self.writeline(
+ "%s event in context.blocks[%r][0](%s):" % (loop, node.name, context),
+ node,
+ )
self.indent()
- self.simple_write('event', frame)
+ self.simple_write("event", frame)
self.outdent()
self.outdent(level)
@@ -856,8 +881,7 @@ class CodeGenerator(NodeVisitor):
def visit_Extends(self, node, frame):
"""Calls the extender."""
if not frame.toplevel:
- self.fail('cannot use extend from a non top-level scope',
- node.lineno)
+ self.fail("cannot use extend from a non top-level scope", node.lineno)
# if the number of extends statements in general is zero so
# far, we don't have to add a check if something extended
@@ -869,10 +893,9 @@ class CodeGenerator(NodeVisitor):
# time too, but i welcome it not to confuse users by throwing the
# same error at different times just "because we can".
if not self.has_known_extends:
- self.writeline('if parent_template is not None:')
+ self.writeline("if parent_template is not None:")
self.indent()
- self.writeline('raise TemplateRuntimeError(%r)' %
- 'extended multiple times')
+ self.writeline("raise TemplateRuntimeError(%r)" % "extended multiple times")
# if we have a known extends already we don't need that code here
# as we know that the template execution will end here.
@@ -881,14 +904,14 @@ class CodeGenerator(NodeVisitor):
else:
self.outdent()
- self.writeline('parent_template = environment.get_template(', node)
+ self.writeline("parent_template = environment.get_template(", node)
self.visit(node.template, frame)
- self.write(', %r)' % self.name)
- self.writeline('for name, parent_block in parent_template.'
- 'blocks.%s():' % dict_item_iter)
+ self.write(", %r)" % self.name)
+ self.writeline(
+ "for name, parent_block in parent_template.blocks.%s():" % dict_item_iter
+ )
self.indent()
- self.writeline('context.blocks.setdefault(name, []).'
- 'append(parent_block)')
+ self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
self.outdent()
# if this extends statement was in the root level we can take
@@ -903,52 +926,56 @@ class CodeGenerator(NodeVisitor):
def visit_Include(self, node, frame):
"""Handles includes."""
if node.ignore_missing:
- self.writeline('try:')
+ self.writeline("try:")
self.indent()
- func_name = 'get_or_select_template'
+ func_name = "get_or_select_template"
if isinstance(node.template, nodes.Const):
if isinstance(node.template.value, string_types):
- func_name = 'get_template'
+ func_name = "get_template"
elif isinstance(node.template.value, (tuple, list)):
- func_name = 'select_template'
+ func_name = "select_template"
elif isinstance(node.template, (nodes.Tuple, nodes.List)):
- func_name = 'select_template'
+ func_name = "select_template"
- self.writeline('template = environment.%s(' % func_name, node)
+ self.writeline("template = environment.%s(" % func_name, node)
self.visit(node.template, frame)
- self.write(', %r)' % self.name)
+ self.write(", %r)" % self.name)
if node.ignore_missing:
self.outdent()
- self.writeline('except TemplateNotFound:')
+ self.writeline("except TemplateNotFound:")
self.indent()
- self.writeline('pass')
+ self.writeline("pass")
self.outdent()
- self.writeline('else:')
+ self.writeline("else:")
self.indent()
skip_event_yield = False
if node.with_context:
- loop = self.environment.is_async and 'async for' or 'for'
- self.writeline('%s event in template.root_render_func('
- 'template.new_context(context.get_all(), True, '
- '%s)):' % (loop, self.dump_local_context(frame)))
+ loop = self.environment.is_async and "async for" or "for"
+ self.writeline(
+ "%s event in template.root_render_func("
+ "template.new_context(context.get_all(), True, "
+ "%s)):" % (loop, self.dump_local_context(frame))
+ )
elif self.environment.is_async:
- self.writeline('for event in (await '
- 'template._get_default_module_async())'
- '._body_stream:')
+ self.writeline(
+ "for event in (await "
+ "template._get_default_module_async())"
+ "._body_stream:"
+ )
else:
if supports_yield_from:
- self.writeline('yield from template._get_default_module()'
- '._body_stream')
+ self.writeline("yield from template._get_default_module()._body_stream")
skip_event_yield = True
else:
- self.writeline('for event in template._get_default_module()'
- '._body_stream:')
+ self.writeline(
+ "for event in template._get_default_module()._body_stream:"
+ )
if not skip_event_yield:
self.indent()
- self.simple_write('event', frame)
+ self.simple_write("event", frame)
self.outdent()
if node.ignore_missing:
@@ -956,40 +983,50 @@ class CodeGenerator(NodeVisitor):
def visit_Import(self, node, frame):
"""Visit regular imports."""
- self.writeline('%s = ' % frame.symbols.ref(node.target), node)
+ self.writeline("%s = " % frame.symbols.ref(node.target), node)
if frame.toplevel:
- self.write('context.vars[%r] = ' % node.target)
+ self.write("context.vars[%r] = " % node.target)
if self.environment.is_async:
- self.write('await ')
- self.write('environment.get_template(')
+ self.write("await ")
+ self.write("environment.get_template(")
self.visit(node.template, frame)
- self.write(', %r).' % self.name)
+ self.write(", %r)." % self.name)
if node.with_context:
- self.write('make_module%s(context.get_all(), True, %s)'
- % (self.environment.is_async and '_async' or '',
- self.dump_local_context(frame)))
+ self.write(
+ "make_module%s(context.get_all(), True, %s)"
+ % (
+ self.environment.is_async and "_async" or "",
+ self.dump_local_context(frame),
+ )
+ )
elif self.environment.is_async:
- self.write('_get_default_module_async()')
+ self.write("_get_default_module_async()")
else:
- self.write('_get_default_module()')
- if frame.toplevel and not node.target.startswith('_'):
- self.writeline('context.exported_vars.discard(%r)' % node.target)
+ self.write("_get_default_module()")
+ if frame.toplevel and not node.target.startswith("_"):
+ self.writeline("context.exported_vars.discard(%r)" % node.target)
def visit_FromImport(self, node, frame):
"""Visit named imports."""
self.newline(node)
- self.write('included_template = %senvironment.get_template('
- % (self.environment.is_async and 'await ' or ''))
+ self.write(
+ "included_template = %senvironment.get_template("
+ % (self.environment.is_async and "await " or "")
+ )
self.visit(node.template, frame)
- self.write(', %r).' % self.name)
+ self.write(", %r)." % self.name)
if node.with_context:
- self.write('make_module%s(context.get_all(), True, %s)'
- % (self.environment.is_async and '_async' or '',
- self.dump_local_context(frame)))
+ self.write(
+ "make_module%s(context.get_all(), True, %s)"
+ % (
+ self.environment.is_async and "_async" or "",
+ self.dump_local_context(frame),
+ )
+ )
elif self.environment.is_async:
- self.write('_get_default_module_async()')
+ self.write("_get_default_module_async()")
else:
- self.write('_get_default_module()')
+ self.write("_get_default_module()")
var_names = []
discarded_names = []
@@ -998,41 +1035,51 @@ class CodeGenerator(NodeVisitor):
name, alias = name
else:
alias = name
- self.writeline('%s = getattr(included_template, '
- '%r, missing)' % (frame.symbols.ref(alias), name))
- self.writeline('if %s is missing:' % frame.symbols.ref(alias))
+ self.writeline(
+ "%s = getattr(included_template, "
+ "%r, missing)" % (frame.symbols.ref(alias), name)
+ )
+ self.writeline("if %s is missing:" % frame.symbols.ref(alias))
self.indent()
- self.writeline('%s = undefined(%r %% '
- 'included_template.__name__, '
- 'name=%r)' %
- (frame.symbols.ref(alias),
- 'the template %%r (imported on %s) does '
- 'not export the requested name %s' % (
- self.position(node),
- repr(name)
- ), name))
+ self.writeline(
+ "%s = undefined(%r %% "
+ "included_template.__name__, "
+ "name=%r)"
+ % (
+ frame.symbols.ref(alias),
+ "the template %%r (imported on %s) does "
+ "not export the requested name %s"
+ % (self.position(node), repr(name)),
+ name,
+ )
+ )
self.outdent()
if frame.toplevel:
var_names.append(alias)
- if not alias.startswith('_'):
+ if not alias.startswith("_"):
discarded_names.append(alias)
if var_names:
if len(var_names) == 1:
name = var_names[0]
- self.writeline('context.vars[%r] = %s' %
- (name, frame.symbols.ref(name)))
+ self.writeline(
+ "context.vars[%r] = %s" % (name, frame.symbols.ref(name))
+ )
else:
- self.writeline('context.vars.update({%s})' % ', '.join(
- '%r: %s' % (name, frame.symbols.ref(name)) for name in var_names
- ))
+ self.writeline(
+ "context.vars.update({%s})"
+ % ", ".join(
+ "%r: %s" % (name, frame.symbols.ref(name)) for name in var_names
+ )
+ )
if discarded_names:
if len(discarded_names) == 1:
- self.writeline('context.exported_vars.discard(%r)' %
- discarded_names[0])
+ self.writeline("context.exported_vars.discard(%r)" % discarded_names[0])
else:
- self.writeline('context.exported_vars.difference_'
- 'update((%s))' % ', '.join(imap(repr, discarded_names)))
+ self.writeline(
+ "context.exported_vars.difference_"
+ "update((%s))" % ", ".join(imap(repr, discarded_names))
+ )
def visit_For(self, node, frame):
loop_frame = frame.inner()
@@ -1042,35 +1089,35 @@ class CodeGenerator(NodeVisitor):
# try to figure out if we have an extended loop. An extended loop
# is necessary if the loop is in recursive mode if the special loop
# variable is accessed in the body.
- extended_loop = node.recursive or 'loop' in \
- find_undeclared(node.iter_child_nodes(
- only=('body',)), ('loop',))
+ extended_loop = node.recursive or "loop" in find_undeclared(
+ node.iter_child_nodes(only=("body",)), ("loop",)
+ )
loop_ref = None
if extended_loop:
- loop_ref = loop_frame.symbols.declare_parameter('loop')
+ loop_ref = loop_frame.symbols.declare_parameter("loop")
- loop_frame.symbols.analyze_node(node, for_branch='body')
+ loop_frame.symbols.analyze_node(node, for_branch="body")
if node.else_:
- else_frame.symbols.analyze_node(node, for_branch='else')
+ else_frame.symbols.analyze_node(node, for_branch="else")
if node.test:
loop_filter_func = self.temporary_identifier()
- test_frame.symbols.analyze_node(node, for_branch='test')
- self.writeline('%s(fiter):' % self.func(loop_filter_func), node.test)
+ test_frame.symbols.analyze_node(node, for_branch="test")
+ self.writeline("%s(fiter):" % self.func(loop_filter_func), node.test)
self.indent()
self.enter_frame(test_frame)
- self.writeline(self.environment.is_async and 'async for ' or 'for ')
+ self.writeline(self.environment.is_async and "async for " or "for ")
self.visit(node.target, loop_frame)
- self.write(' in ')
- self.write(self.environment.is_async and 'auto_aiter(fiter)' or 'fiter')
- self.write(':')
+ self.write(" in ")
+ self.write(self.environment.is_async and "auto_aiter(fiter)" or "fiter")
+ self.write(":")
self.indent()
- self.writeline('if ', node.test)
+ self.writeline("if ", node.test)
self.visit(node.test, test_frame)
- self.write(':')
+ self.write(":")
self.indent()
- self.writeline('yield ')
+ self.writeline("yield ")
self.visit(node.target, loop_frame)
self.outdent(3)
self.leave_frame(test_frame, with_python_scope=True)
@@ -1079,8 +1126,9 @@ class CodeGenerator(NodeVisitor):
# variables at that point. Because loops can be nested but the loop
# variable is a special one we have to enforce aliasing for it.
if node.recursive:
- self.writeline('%s(reciter, loop_render_func, depth=0):' %
- self.func('loop'), node)
+ self.writeline(
+ "%s(reciter, loop_render_func, depth=0):" % self.func("loop"), node
+ )
self.indent()
self.buffer(loop_frame)
@@ -1090,57 +1138,60 @@ class CodeGenerator(NodeVisitor):
# make sure the loop variable is a special one and raise a template
# assertion error if a loop tries to write to loop
if extended_loop:
- self.writeline('%s = missing' % loop_ref)
+ self.writeline("%s = missing" % loop_ref)
for name in node.find_all(nodes.Name):
- if name.ctx == 'store' and name.name == 'loop':
- self.fail('Can\'t assign to special loop variable '
- 'in for-loop target', name.lineno)
+ if name.ctx == "store" and name.name == "loop":
+ self.fail(
+ "Can't assign to special loop variable in for-loop target",
+ name.lineno,
+ )
if node.else_:
iteration_indicator = self.temporary_identifier()
- self.writeline('%s = 1' % iteration_indicator)
+ self.writeline("%s = 1" % iteration_indicator)
- self.writeline(self.environment.is_async and 'async for ' or 'for ', node)
+ self.writeline(self.environment.is_async and "async for " or "for ", node)
self.visit(node.target, loop_frame)
if extended_loop:
if self.environment.is_async:
- self.write(', %s in AsyncLoopContext(' % loop_ref)
+ self.write(", %s in AsyncLoopContext(" % loop_ref)
else:
- self.write(', %s in LoopContext(' % loop_ref)
+ self.write(", %s in LoopContext(" % loop_ref)
else:
- self.write(' in ')
+ self.write(" in ")
if node.test:
- self.write('%s(' % loop_filter_func)
+ self.write("%s(" % loop_filter_func)
if node.recursive:
- self.write('reciter')
+ self.write("reciter")
else:
if self.environment.is_async and not extended_loop:
- self.write('auto_aiter(')
+ self.write("auto_aiter(")
self.visit(node.iter, frame)
if self.environment.is_async and not extended_loop:
- self.write(')')
+ self.write(")")
if node.test:
- self.write(')')
+ self.write(")")
if node.recursive:
- self.write(', undefined, loop_render_func, depth):')
+ self.write(", undefined, loop_render_func, depth):")
else:
- self.write(extended_loop and ', undefined):' or ':')
+ self.write(extended_loop and ", undefined):" or ":")
self.indent()
self.enter_frame(loop_frame)
self.blockvisit(node.body, loop_frame)
if node.else_:
- self.writeline('%s = 0' % iteration_indicator)
+ self.writeline("%s = 0" % iteration_indicator)
self.outdent()
- self.leave_frame(loop_frame, with_python_scope=node.recursive
- and not node.else_)
+ self.leave_frame(
+ loop_frame, with_python_scope=node.recursive and not node.else_
+ )
if node.else_:
- self.writeline('if %s:' % iteration_indicator)
+ self.writeline("if %s:" % iteration_indicator)
self.indent()
self.enter_frame(else_frame)
self.blockvisit(node.else_, else_frame)
@@ -1154,33 +1205,33 @@ class CodeGenerator(NodeVisitor):
self.outdent()
self.start_write(frame, node)
if self.environment.is_async:
- self.write('await ')
- self.write('loop(')
+ self.write("await ")
+ self.write("loop(")
if self.environment.is_async:
- self.write('auto_aiter(')
+ self.write("auto_aiter(")
self.visit(node.iter, frame)
if self.environment.is_async:
- self.write(')')
- self.write(', loop)')
+ self.write(")")
+ self.write(", loop)")
self.end_write(frame)
def visit_If(self, node, frame):
if_frame = frame.soft()
- self.writeline('if ', node)
+ self.writeline("if ", node)
self.visit(node.test, if_frame)
- self.write(':')
+ self.write(":")
self.indent()
self.blockvisit(node.body, if_frame)
self.outdent()
for elif_ in node.elif_:
- self.writeline('elif ', elif_)
+ self.writeline("elif ", elif_)
self.visit(elif_.test, if_frame)
- self.write(':')
+ self.write(":")
self.indent()
self.blockvisit(elif_.body, if_frame)
self.outdent()
if node.else_:
- self.writeline('else:')
+ self.writeline("else:")
self.indent()
self.blockvisit(node.else_, if_frame)
self.outdent()
@@ -1189,16 +1240,16 @@ class CodeGenerator(NodeVisitor):
macro_frame, macro_ref = self.macro_body(node, frame)
self.newline()
if frame.toplevel:
- if not node.name.startswith('_'):
- self.write('context.exported_vars.add(%r)' % node.name)
+ if not node.name.startswith("_"):
+ self.write("context.exported_vars.add(%r)" % node.name)
ref = frame.symbols.ref(node.name)
- self.writeline('context.vars[%r] = ' % node.name)
- self.write('%s = ' % frame.symbols.ref(node.name))
+ self.writeline("context.vars[%r] = " % node.name)
+ self.write("%s = " % frame.symbols.ref(node.name))
self.macro_def(macro_ref, macro_frame)
def visit_CallBlock(self, node, frame):
call_frame, macro_ref = self.macro_body(node, frame)
- self.writeline('caller = ')
+ self.writeline("caller = ")
self.macro_def(macro_ref, call_frame)
self.start_write(frame, node)
self.visit_Call(node.call, frame, forward_caller=True)
@@ -1222,7 +1273,7 @@ class CodeGenerator(NodeVisitor):
for idx, (target, expr) in enumerate(izip(node.targets, node.values)):
self.newline()
self.visit(target, with_frame)
- self.write(' = ')
+ self.write(" = ")
self.visit(expr, frame)
self.blockvisit(node.body, with_frame)
self.leave_frame(with_frame)
@@ -1411,7 +1462,7 @@ class CodeGenerator(NodeVisitor):
self.push_assign_tracking()
self.newline(node)
self.visit(node.target, frame)
- self.write(' = ')
+ self.write(" = ")
self.visit(node.node, frame)
self.pop_assign_tracking(frame)
@@ -1428,20 +1479,19 @@ class CodeGenerator(NodeVisitor):
self.blockvisit(node.body, block_frame)
self.newline(node)
self.visit(node.target, frame)
- self.write(' = (Markup if context.eval_ctx.autoescape '
- 'else identity)(')
+ self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
if node.filter is not None:
self.visit_Filter(node.filter, block_frame)
else:
- self.write('concat(%s)' % block_frame.buffer)
- self.write(')')
+ self.write("concat(%s)" % block_frame.buffer)
+ self.write(")")
self.pop_assign_tracking(frame)
self.leave_frame(block_frame)
# -- Expression Visitors
def visit_Name(self, node, frame):
- if node.ctx == 'store' and frame.toplevel:
+ if node.ctx == "store" and frame.toplevel:
if self._assign_stack:
self._assign_stack[-1].add(node.name)
ref = frame.symbols.ref(node.name)
@@ -1449,12 +1499,17 @@ class CodeGenerator(NodeVisitor):
# If we are looking up a variable we might have to deal with the
# case where it's undefined. We can skip that case if the load
# instruction indicates a parameter which are always defined.
- if node.ctx == 'load':
+ if node.ctx == "load":
load = frame.symbols.find_load(ref)
- if not (load is not None and load[0] == VAR_LOAD_PARAMETER and \
- not self.parameter_is_undeclared(ref)):
- self.write('(undefined(name=%r) if %s is missing else %s)' %
- (node.name, ref, ref))
+ if not (
+ load is not None
+ and load[0] == VAR_LOAD_PARAMETER
+ and not self.parameter_is_undeclared(ref)
+ ):
+ self.write(
+ "(undefined(name=%r) if %s is missing else %s)"
+ % (node.name, ref, ref)
+ )
return
self.write(ref)
@@ -1464,12 +1519,14 @@ class CodeGenerator(NodeVisitor):
# `foo.bar` notation they will be parsed as a normal attribute access
# when used anywhere but in a `set` context
ref = frame.symbols.ref(node.name)
- self.writeline('if not isinstance(%s, Namespace):' % ref)
+ self.writeline("if not isinstance(%s, Namespace):" % ref)
self.indent()
- self.writeline('raise TemplateRuntimeError(%r)' %
- 'cannot assign attribute on non-namespace object')
+ self.writeline(
+ "raise TemplateRuntimeError(%r)"
+ % "cannot assign attribute on non-namespace object"
+ )
self.outdent()
- self.writeline('%s[%r]' % (ref, node.attr))
+ self.writeline("%s[%r]" % (ref, node.attr))
def visit_Const(self, node, frame):
val = node.as_const(frame.eval_ctx)
@@ -1482,105 +1539,111 @@ class CodeGenerator(NodeVisitor):
try:
self.write(repr(node.as_const(frame.eval_ctx)))
except nodes.Impossible:
- self.write('(Markup if context.eval_ctx.autoescape else identity)(%r)'
- % node.data)
+ self.write(
+ "(Markup if context.eval_ctx.autoescape else identity)(%r)" % node.data
+ )
def visit_Tuple(self, node, frame):
- self.write('(')
+ self.write("(")
idx = -1
for idx, item in enumerate(node.items):
if idx:
- self.write(', ')
+ self.write(", ")
self.visit(item, frame)
- self.write(idx == 0 and ',)' or ')')
+ self.write(idx == 0 and ",)" or ")")
def visit_List(self, node, frame):
- self.write('[')
+ self.write("[")
for idx, item in enumerate(node.items):
if idx:
- self.write(', ')
+ self.write(", ")
self.visit(item, frame)
- self.write(']')
+ self.write("]")
def visit_Dict(self, node, frame):
- self.write('{')
+ self.write("{")
for idx, item in enumerate(node.items):
if idx:
- self.write(', ')
+ self.write(", ")
self.visit(item.key, frame)
- self.write(': ')
+ self.write(": ")
self.visit(item.value, frame)
- self.write('}')
+ self.write("}")
def binop(operator, interceptable=True):
@optimizeconst
def visitor(self, node, frame):
- if self.environment.sandboxed and \
- operator in self.environment.intercepted_binops:
- self.write('environment.call_binop(context, %r, ' % operator)
+ if (
+ self.environment.sandboxed
+ and operator in self.environment.intercepted_binops
+ ):
+ self.write("environment.call_binop(context, %r, " % operator)
self.visit(node.left, frame)
- self.write(', ')
+ self.write(", ")
self.visit(node.right, frame)
else:
- self.write('(')
+ self.write("(")
self.visit(node.left, frame)
- self.write(' %s ' % operator)
+ self.write(" %s " % operator)
self.visit(node.right, frame)
- self.write(')')
+ self.write(")")
+
return visitor
def uaop(operator, interceptable=True):
@optimizeconst
def visitor(self, node, frame):
- if self.environment.sandboxed and \
- operator in self.environment.intercepted_unops:
- self.write('environment.call_unop(context, %r, ' % operator)
+ if (
+ self.environment.sandboxed
+ and operator in self.environment.intercepted_unops
+ ):
+ self.write("environment.call_unop(context, %r, " % operator)
self.visit(node.node, frame)
else:
- self.write('(' + operator)
+ self.write("(" + operator)
self.visit(node.node, frame)
- self.write(')')
+ self.write(")")
+
return visitor
- visit_Add = binop('+')
- visit_Sub = binop('-')
- visit_Mul = binop('*')
- visit_Div = binop('/')
- visit_FloorDiv = binop('//')
- visit_Pow = binop('**')
- visit_Mod = binop('%')
- visit_And = binop('and', interceptable=False)
- visit_Or = binop('or', interceptable=False)
- visit_Pos = uaop('+')
- visit_Neg = uaop('-')
- visit_Not = uaop('not ', interceptable=False)
+ visit_Add = binop("+")
+ visit_Sub = binop("-")
+ visit_Mul = binop("*")
+ visit_Div = binop("/")
+ visit_FloorDiv = binop("//")
+ visit_Pow = binop("**")
+ visit_Mod = binop("%")
+ visit_And = binop("and", interceptable=False)
+ visit_Or = binop("or", interceptable=False)
+ visit_Pos = uaop("+")
+ visit_Neg = uaop("-")
+ visit_Not = uaop("not ", interceptable=False)
del binop, uaop
@optimizeconst
def visit_Concat(self, node, frame):
if frame.eval_ctx.volatile:
- func_name = '(context.eval_ctx.volatile and' \
- ' markup_join or unicode_join)'
+ func_name = "(context.eval_ctx.volatile and markup_join or unicode_join)"
elif frame.eval_ctx.autoescape:
- func_name = 'markup_join'
+ func_name = "markup_join"
else:
- func_name = 'unicode_join'
- self.write('%s((' % func_name)
+ func_name = "unicode_join"
+ self.write("%s((" % func_name)
for arg in node.nodes:
self.visit(arg, frame)
- self.write(', ')
- self.write('))')
+ self.write(", ")
+ self.write("))")
@optimizeconst
def visit_Compare(self, node, frame):
- self.write('(')
+ self.write("(")
self.visit(node.expr, frame)
for op in node.ops:
self.visit(op, frame)
- self.write(')')
+ self.write(")")
def visit_Operand(self, node, frame):
- self.write(' %s ' % operators[node.op])
+ self.write(" %s " % operators[node.op])
self.visit(node.expr, frame)
@optimizeconst
@@ -1588,9 +1651,9 @@ class CodeGenerator(NodeVisitor):
if self.environment.is_async:
self.write("await auto_await(")
- self.write('environment.getattr(')
+ self.write("environment.getattr(")
self.visit(node.node, frame)
- self.write(', %r)' % node.attr)
+ self.write(", %r)" % node.attr)
if self.environment.is_async:
self.write(")")
@@ -1600,18 +1663,18 @@ class CodeGenerator(NodeVisitor):
# slices bypass the environment getitem method.
if isinstance(node.arg, nodes.Slice):
self.visit(node.node, frame)
- self.write('[')
+ self.write("[")
self.visit(node.arg, frame)
- self.write(']')
+ self.write("]")
else:
if self.environment.is_async:
self.write("await auto_await(")
- self.write('environment.getitem(')
+ self.write("environment.getitem(")
self.visit(node.node, frame)
- self.write(', ')
+ self.write(", ")
self.visit(node.arg, frame)
- self.write(')')
+ self.write(")")
if self.environment.is_async:
self.write(")")
@@ -1619,107 +1682,113 @@ class CodeGenerator(NodeVisitor):
def visit_Slice(self, node, frame):
if node.start is not None:
self.visit(node.start, frame)
- self.write(':')
+ self.write(":")
if node.stop is not None:
self.visit(node.stop, frame)
if node.step is not None:
- self.write(':')
+ self.write(":")
self.visit(node.step, frame)
@optimizeconst
def visit_Filter(self, node, frame):
if self.environment.is_async:
- self.write('await auto_await(')
- self.write(self.filters[node.name] + '(')
+ self.write("await auto_await(")
+ self.write(self.filters[node.name] + "(")
func = self.environment.filters.get(node.name)
if func is None:
- self.fail('no filter named %r' % node.name, node.lineno)
- if getattr(func, 'contextfilter', False):
- self.write('context, ')
- elif getattr(func, 'evalcontextfilter', False):
- self.write('context.eval_ctx, ')
- elif getattr(func, 'environmentfilter', False):
- self.write('environment, ')
+ self.fail("no filter named %r" % node.name, node.lineno)
+ if getattr(func, "contextfilter", False):
+ self.write("context, ")
+ elif getattr(func, "evalcontextfilter", False):
+ self.write("context.eval_ctx, ")
+ elif getattr(func, "environmentfilter", False):
+ self.write("environment, ")
# if the filter node is None we are inside a filter block
# and want to write to the current buffer
if node.node is not None:
self.visit(node.node, frame)
elif frame.eval_ctx.volatile:
- self.write('(context.eval_ctx.autoescape and'
- ' Markup(concat(%s)) or concat(%s))' %
- (frame.buffer, frame.buffer))
+ self.write(
+ "(context.eval_ctx.autoescape and"
+ " Markup(concat(%s)) or concat(%s))" % (frame.buffer, frame.buffer)
+ )
elif frame.eval_ctx.autoescape:
- self.write('Markup(concat(%s))' % frame.buffer)
+ self.write("Markup(concat(%s))" % frame.buffer)
else:
- self.write('concat(%s)' % frame.buffer)
+ self.write("concat(%s)" % frame.buffer)
self.signature(node, frame)
- self.write(')')
+ self.write(")")
if self.environment.is_async:
- self.write(')')
+ self.write(")")
@optimizeconst
def visit_Test(self, node, frame):
- self.write(self.tests[node.name] + '(')
+ self.write(self.tests[node.name] + "(")
if node.name not in self.environment.tests:
- self.fail('no test named %r' % node.name, node.lineno)
+ self.fail("no test named %r" % node.name, node.lineno)
self.visit(node.node, frame)
self.signature(node, frame)
- self.write(')')
+ self.write(")")
@optimizeconst
def visit_CondExpr(self, node, frame):
def write_expr2():
if node.expr2 is not None:
return self.visit(node.expr2, frame)
- self.write('cond_expr_undefined(%r)' % ('the inline if-'
- 'expression on %s evaluated to false and '
- 'no else section was defined.' % self.position(node)))
-
- self.write('(')
+ self.write(
+ "cond_expr_undefined(%r)"
+ % (
+ "the inline if-"
+ "expression on %s evaluated to false and "
+ "no else section was defined." % self.position(node)
+ )
+ )
+
+ self.write("(")
self.visit(node.expr1, frame)
- self.write(' if ')
+ self.write(" if ")
self.visit(node.test, frame)
- self.write(' else ')
+ self.write(" else ")
write_expr2()
- self.write(')')
+ self.write(")")
@optimizeconst
def visit_Call(self, node, frame, forward_caller=False):
if self.environment.is_async:
- self.write('await auto_await(')
+ self.write("await auto_await(")
if self.environment.sandboxed:
- self.write('environment.call(context, ')
+ self.write("environment.call(context, ")
else:
- self.write('context.call(')
+ self.write("context.call(")
self.visit(node.node, frame)
- extra_kwargs = forward_caller and {'caller': 'caller'} or None
+ extra_kwargs = forward_caller and {"caller": "caller"} or None
self.signature(node, frame, extra_kwargs)
- self.write(')')
+ self.write(")")
if self.environment.is_async:
- self.write(')')
+ self.write(")")
def visit_Keyword(self, node, frame):
- self.write(node.key + '=')
+ self.write(node.key + "=")
self.visit(node.value, frame)
# -- Unused nodes for extensions
def visit_MarkSafe(self, node, frame):
- self.write('Markup(')
+ self.write("Markup(")
self.visit(node.expr, frame)
- self.write(')')
+ self.write(")")
def visit_MarkSafeIfAutoescape(self, node, frame):
- self.write('(context.eval_ctx.autoescape and Markup or identity)(')
+ self.write("(context.eval_ctx.autoescape and Markup or identity)(")
self.visit(node.expr, frame)
- self.write(')')
+ self.write(")")
def visit_EnvironmentAttribute(self, node, frame):
- self.write('environment.' + node.name)
+ self.write("environment." + node.name)
def visit_ExtensionAttribute(self, node, frame):
- self.write('environment.extensions[%r].%s' % (node.identifier, node.name))
+ self.write("environment.extensions[%r].%s" % (node.identifier, node.name))
def visit_ImportedName(self, node, frame):
self.write(self.import_aliases[node.importname])
@@ -1728,16 +1797,16 @@ class CodeGenerator(NodeVisitor):
self.write(node.name)
def visit_ContextReference(self, node, frame):
- self.write('context')
+ self.write("context")
def visit_DerivedContextReference(self, node, frame):
self.write(self.derive_context(frame))
def visit_Continue(self, node, frame):
- self.writeline('continue', node)
+ self.writeline("continue", node)
def visit_Break(self, node, frame):
- self.writeline('break', node)
+ self.writeline("break", node)
def visit_Scope(self, node, frame):
scope_frame = frame.inner()
@@ -1748,8 +1817,8 @@ class CodeGenerator(NodeVisitor):
def visit_OverlayScope(self, node, frame):
ctx = self.temporary_identifier()
- self.writeline('%s = %s' % (ctx, self.derive_context(frame)))
- self.writeline('%s.vars = ' % ctx)
+ self.writeline("%s = %s" % (ctx, self.derive_context(frame)))
+ self.writeline("%s.vars = " % ctx)
self.visit(node.context, frame)
self.push_context_reference(ctx)
@@ -1762,7 +1831,7 @@ class CodeGenerator(NodeVisitor):
def visit_EvalContextModifier(self, node, frame):
for keyword in node.options:
- self.writeline('context.eval_ctx.%s = ' % keyword.key)
+ self.writeline("context.eval_ctx.%s = " % keyword.key)
self.visit(keyword.value, frame)
try:
val = keyword.value.as_const(frame.eval_ctx)
@@ -1774,9 +1843,9 @@ class CodeGenerator(NodeVisitor):
def visit_ScopedEvalContextModifier(self, node, frame):
old_ctx_name = self.temporary_identifier()
saved_ctx = frame.eval_ctx.save()
- self.writeline('%s = context.eval_ctx.save()' % old_ctx_name)
+ self.writeline("%s = context.eval_ctx.save()" % old_ctx_name)
self.visit_EvalContextModifier(node, frame)
for child in node.body:
self.visit(child, frame)
frame.eval_ctx.revert(saved_ctx)
- self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name)
+ self.writeline("context.eval_ctx.revert(%s)" % old_ctx_name)
diff --git a/src/jinja2/constants.py b/src/jinja2/constants.py
index 8d6a6a7..36da88b 100644
--- a/src/jinja2/constants.py
+++ b/src/jinja2/constants.py
@@ -9,7 +9,7 @@
:license: BSD, see LICENSE for more details.
"""
#: list of lorem ipsum words used by the lipsum() helper function
-LOREM_IPSUM_WORDS = u'''\
+LOREM_IPSUM_WORDS = u"""\
a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
auctor augue bibendum blandit class commodo condimentum congue consectetuer
consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
@@ -27,4 +27,4 @@ ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit
sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
-viverra volutpat vulputate'''
+viverra volutpat vulputate"""
diff --git a/src/jinja2/debug.py b/src/jinja2/debug.py
index 23e891d..bb36609 100644
--- a/src/jinja2/debug.py
+++ b/src/jinja2/debug.py
@@ -100,7 +100,7 @@ def fake_traceback(exc_value, tb, filename, lineno):
"__jinja_exception__": exc_value,
}
# Raise an exception at the correct line number.
- code = compile('\n' * (lineno - 1) + "raise __jinja_exception__", filename, "exec")
+ code = compile("\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec")
# Build a new code object that points to the template file and
# replaces the location with a block name.
@@ -213,6 +213,8 @@ if sys.version_info >= (3, 7):
def tb_set_next(tb, tb_next):
tb.tb_next = tb_next
return tb
+
+
elif PYPY:
# PyPy might have special support, and won't work with ctypes.
try:
@@ -221,6 +223,7 @@ elif PYPY:
# Without tproxy support, use the original traceback.
def tb_set_next(tb, tb_next):
return tb
+
else:
# With tproxy support, create a proxy around the traceback that
# returns the new tb_next.
@@ -232,6 +235,8 @@ elif PYPY:
return op.delegate()
return tputil.make_proxy(controller, obj=tb)
+
+
else:
# Use ctypes to assign tb_next at the C level since it's read-only
# from Python.
diff --git a/src/jinja2/defaults.py b/src/jinja2/defaults.py
index 135ef81..8ee52bb 100644
--- a/src/jinja2/defaults.py
+++ b/src/jinja2/defaults.py
@@ -15,40 +15,41 @@ from .utils import Joiner
from .utils import Namespace
# defaults for the parser / lexer
-BLOCK_START_STRING = '{%'
-BLOCK_END_STRING = '%}'
-VARIABLE_START_STRING = '{{'
-VARIABLE_END_STRING = '}}'
-COMMENT_START_STRING = '{#'
-COMMENT_END_STRING = '#}'
+BLOCK_START_STRING = "{%"
+BLOCK_END_STRING = "%}"
+VARIABLE_START_STRING = "{{"
+VARIABLE_END_STRING = "}}"
+COMMENT_START_STRING = "{#"
+COMMENT_END_STRING = "#}"
LINE_STATEMENT_PREFIX = None
LINE_COMMENT_PREFIX = None
TRIM_BLOCKS = False
LSTRIP_BLOCKS = False
-NEWLINE_SEQUENCE = '\n'
+NEWLINE_SEQUENCE = "\n"
KEEP_TRAILING_NEWLINE = False
# default filters, tests and namespace
from jinja2.filters import FILTERS as DEFAULT_FILTERS
from jinja2.tests import TESTS as DEFAULT_TESTS
+
DEFAULT_NAMESPACE = {
- 'range': range_type,
- 'dict': dict,
- 'lipsum': generate_lorem_ipsum,
- 'cycler': Cycler,
- 'joiner': Joiner,
- 'namespace': Namespace
+ "range": range_type,
+ "dict": dict,
+ "lipsum": generate_lorem_ipsum,
+ "cycler": Cycler,
+ "joiner": Joiner,
+ "namespace": Namespace,
}
# default policies
DEFAULT_POLICIES = {
- 'compiler.ascii_str': True,
- 'urlize.rel': 'noopener',
- 'urlize.target': None,
- 'truncate.leeway': 5,
- 'json.dumps_function': None,
- 'json.dumps_kwargs': {'sort_keys': True},
- 'ext.i18n.trimmed': False,
+ "compiler.ascii_str": True,
+ "urlize.rel": "noopener",
+ "urlize.target": None,
+ "truncate.leeway": 5,
+ "json.dumps_function": None,
+ "json.dumps_kwargs": {"sort_keys": True},
+ "ext.i18n.trimmed": False,
}
# export all constants
diff --git a/src/jinja2/environment.py b/src/jinja2/environment.py
index 252d7eb..7c4d26a 100644
--- a/src/jinja2/environment.py
+++ b/src/jinja2/environment.py
@@ -123,20 +123,25 @@ def fail_for_missing_callable(string, name):
try:
name._fail_with_undefined_error()
except Exception as e:
- msg = '%s (%s; did you forget to quote the callable name?)' % (msg, e)
+ msg = "%s (%s; did you forget to quote the callable name?)" % (msg, e)
raise TemplateRuntimeError(msg)
def _environment_sanity_check(environment):
"""Perform a sanity check on the environment."""
- assert issubclass(environment.undefined, Undefined), 'undefined must ' \
- 'be a subclass of undefined because filters depend on it.'
- assert environment.block_start_string != \
- environment.variable_start_string != \
- environment.comment_start_string, 'block, variable and comment ' \
- 'start strings must be different'
- assert environment.newline_sequence in ('\r', '\r\n', '\n'), \
- 'newline_sequence set to unknown line ending string.'
+ assert issubclass(
+ environment.undefined, Undefined
+ ), "undefined must be a subclass of undefined because filters depend on it."
+ assert (
+ environment.block_start_string
+ != environment.variable_start_string
+ != environment.comment_start_string
+ ), "block, variable and comment start strings must be different"
+ assert environment.newline_sequence in (
+ "\r",
+ "\r\n",
+ "\n",
+ ), "newline_sequence set to unknown line ending string."
return environment
@@ -287,29 +292,31 @@ class Environment(object):
#: :class:`~jinja2.runtime.Context` for more information.
context_class = Context
- def __init__(self,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- lstrip_blocks=LSTRIP_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- keep_trailing_newline=KEEP_TRAILING_NEWLINE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False,
- loader=None,
- cache_size=400,
- auto_reload=True,
- bytecode_cache=None,
- enable_async=False):
+ def __init__(
+ self,
+ block_start_string=BLOCK_START_STRING,
+ block_end_string=BLOCK_END_STRING,
+ variable_start_string=VARIABLE_START_STRING,
+ variable_end_string=VARIABLE_END_STRING,
+ comment_start_string=COMMENT_START_STRING,
+ comment_end_string=COMMENT_END_STRING,
+ line_statement_prefix=LINE_STATEMENT_PREFIX,
+ line_comment_prefix=LINE_COMMENT_PREFIX,
+ trim_blocks=TRIM_BLOCKS,
+ lstrip_blocks=LSTRIP_BLOCKS,
+ newline_sequence=NEWLINE_SEQUENCE,
+ keep_trailing_newline=KEEP_TRAILING_NEWLINE,
+ extensions=(),
+ optimized=True,
+ undefined=Undefined,
+ finalize=None,
+ autoescape=False,
+ loader=None,
+ cache_size=400,
+ auto_reload=True,
+ bytecode_cache=None,
+ enable_async=False,
+ ):
# !!Important notice!!
# The constructor accepts quite a few arguments that should be
# passed by keyword rather than position. However it's important to
@@ -381,15 +388,28 @@ class Environment(object):
if not hasattr(self, key):
setattr(self, key, value)
- def overlay(self, block_start_string=missing, block_end_string=missing,
- variable_start_string=missing, variable_end_string=missing,
- comment_start_string=missing, comment_end_string=missing,
- line_statement_prefix=missing, line_comment_prefix=missing,
- trim_blocks=missing, lstrip_blocks=missing,
- extensions=missing, optimized=missing,
- undefined=missing, finalize=missing, autoescape=missing,
- loader=missing, cache_size=missing, auto_reload=missing,
- bytecode_cache=missing):
+ def overlay(
+ self,
+ block_start_string=missing,
+ block_end_string=missing,
+ variable_start_string=missing,
+ variable_end_string=missing,
+ comment_start_string=missing,
+ comment_end_string=missing,
+ line_statement_prefix=missing,
+ line_comment_prefix=missing,
+ trim_blocks=missing,
+ lstrip_blocks=missing,
+ extensions=missing,
+ optimized=missing,
+ undefined=missing,
+ finalize=missing,
+ autoescape=missing,
+ loader=missing,
+ cache_size=missing,
+ auto_reload=missing,
+ bytecode_cache=missing,
+ ):
"""Create a new overlay environment that shares all the data with the
current environment except for cache and the overridden attributes.
Extensions cannot be removed for an overlayed environment. An overlayed
@@ -402,7 +422,7 @@ class Environment(object):
through.
"""
args = dict(locals())
- del args['self'], args['cache_size'], args['extensions']
+ del args["self"], args["cache_size"], args["extensions"]
rv = object.__new__(self.__class__)
rv.__dict__.update(self.__dict__)
@@ -430,8 +450,7 @@ class Environment(object):
def iter_extensions(self):
"""Iterates over the extensions by priority."""
- return iter(sorted(self.extensions.values(),
- key=lambda x: x.priority))
+ return iter(sorted(self.extensions.values(), key=lambda x: x.priority))
def getitem(self, obj, argument):
"""Get an item or attribute of an object but prefer the item."""
@@ -463,8 +482,9 @@ class Environment(object):
except (TypeError, LookupError, AttributeError):
return self.undefined(obj=obj, name=attribute)
- def call_filter(self, name, value, args=None, kwargs=None,
- context=None, eval_ctx=None):
+ def call_filter(
+ self, name, value, args=None, kwargs=None, context=None, eval_ctx=None
+ ):
"""Invokes a filter on a value the same way the compiler does it.
Note that on Python 3 this might return a coroutine in case the
@@ -476,21 +496,22 @@ class Environment(object):
"""
func = self.filters.get(name)
if func is None:
- fail_for_missing_callable('no filter named %r', name)
+ fail_for_missing_callable("no filter named %r", name)
args = [value] + list(args or ())
- if getattr(func, 'contextfilter', False):
+ if getattr(func, "contextfilter", False):
if context is None:
- raise TemplateRuntimeError('Attempted to invoke context '
- 'filter without context')
+ raise TemplateRuntimeError(
+ "Attempted to invoke context filter without context"
+ )
args.insert(0, context)
- elif getattr(func, 'evalcontextfilter', False):
+ elif getattr(func, "evalcontextfilter", False):
if eval_ctx is None:
if context is not None:
eval_ctx = context.eval_ctx
else:
eval_ctx = EvalContext(self)
args.insert(0, eval_ctx)
- elif getattr(func, 'environmentfilter', False):
+ elif getattr(func, "environmentfilter", False):
args.insert(0, self)
return func(*args, **(kwargs or {}))
@@ -501,7 +522,7 @@ class Environment(object):
"""
func = self.tests.get(name)
if func is None:
- fail_for_missing_callable('no test named %r', name)
+ fail_for_missing_callable("no test named %r", name)
return func(value, *(args or ()), **(kwargs or {}))
@internalcode
@@ -544,8 +565,11 @@ class Environment(object):
called for all parsing and compiling methods but *not* for :meth:`lex`
because there you usually only want the actual source tokenized.
"""
- return reduce(lambda s, e: e.preprocess(s, name, filename),
- self.iter_extensions(), text_type(source))
+ return reduce(
+ lambda s, e: e.preprocess(s, name, filename),
+ self.iter_extensions(),
+ text_type(source),
+ )
def _tokenize(self, source, name, filename=None, state=None):
"""Called by the parser to do the preprocessing and filtering
@@ -565,8 +589,14 @@ class Environment(object):
.. versionadded:: 2.5
"""
- return generate(source, self, name, filename, defer_init=defer_init,
- optimized=self.optimized)
+ return generate(
+ source,
+ self,
+ name,
+ filename,
+ defer_init=defer_init,
+ optimized=self.optimized,
+ )
def _compile(self, source, filename):
"""Internal hook that can be overridden to hook a different compile
@@ -574,11 +604,10 @@ class Environment(object):
.. versionadded:: 2.5
"""
- return compile(source, filename, 'exec')
+ return compile(source, filename, "exec")
@internalcode
- def compile(self, source, name=None, filename=None, raw=False,
- defer_init=False):
+ def compile(self, source, name=None, filename=None, raw=False, defer_init=False):
"""Compile a node or template source code. The `name` parameter is
the load name of the template after it was joined using
:meth:`join_path` if necessary, not the filename on the file system.
@@ -603,12 +632,11 @@ class Environment(object):
if isinstance(source, string_types):
source_hint = source
source = self._parse(source, name, filename)
- source = self._generate(source, name, filename,
- defer_init=defer_init)
+ source = self._generate(source, name, filename, defer_init=defer_init)
if raw:
return source
if filename is None:
- filename = '<template>'
+ filename = "<template>"
else:
filename = encode_filename(filename)
return self._compile(source, filename)
@@ -643,25 +671,32 @@ class Environment(object):
.. versionadded:: 2.1
"""
- parser = Parser(self, source, state='variable')
+ parser = Parser(self, source, state="variable")
try:
expr = parser.parse_expression()
if not parser.stream.eos:
- raise TemplateSyntaxError('chunk after expression',
- parser.stream.current.lineno,
- None, None)
+ raise TemplateSyntaxError(
+ "chunk after expression", parser.stream.current.lineno, None, None
+ )
expr.set_environment(self)
except TemplateSyntaxError:
if sys.exc_info() is not None:
self.handle_exception(source=source)
- body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
+ body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)]
template = self.from_string(nodes.Template(body, lineno=1))
return TemplateExpression(template, undefined_to_none)
- def compile_templates(self, target, extensions=None, filter_func=None,
- zip='deflated', log_function=None,
- ignore_errors=True, py_compile=False):
+ def compile_templates(
+ self,
+ target,
+ extensions=None,
+ filter_func=None,
+ zip="deflated",
+ log_function=None,
+ ignore_errors=True,
+ py_compile=False,
+ ):
"""Finds all the templates the loader can find, compiles them
and stores them in `target`. If `zip` is `None`, instead of in a
zipfile, the templates will be stored in a directory.
@@ -692,17 +727,18 @@ class Environment(object):
if py_compile:
if not PY2 or PYPY:
from warnings import warn
- warn(Warning('py_compile has no effect on pypy or Python 3'))
+
+ warn(Warning("py_compile has no effect on pypy or Python 3"))
py_compile = False
else:
import imp
import marshal
- py_header = imp.get_magic() + \
- u'\xff\xff\xff\xff'.encode('iso-8859-15')
+
+ py_header = imp.get_magic() + u"\xff\xff\xff\xff".encode("iso-8859-15")
# Python 3.3 added a source filesize to the header
if sys.version_info >= (3, 3):
- py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15')
+ py_header += u"\x00\x00\x00\x00".encode("iso-8859-15")
def write_file(filename, data):
if zip:
@@ -718,8 +754,10 @@ class Environment(object):
if zip is not None:
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
- zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
- stored=ZIP_STORED)[zip])
+
+ zip_file = ZipFile(
+ target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]
+ )
log_function('Compiling into Zip archive "%s"' % target)
else:
if not os.path.isdir(target):
@@ -741,10 +779,8 @@ class Environment(object):
if py_compile:
c = self._compile(code, encode_filename(filename))
- write_file(filename + 'c', py_header +
- marshal.dumps(c))
- log_function('Byte-compiled "%s" as %s' %
- (name, filename + 'c'))
+ write_file(filename + "c", py_header + marshal.dumps(c))
+ log_function('Byte-compiled "%s" as %s' % (name, filename + "c"))
else:
write_file(filename, code)
log_function('Compiled "%s" as %s' % (name, filename))
@@ -752,7 +788,7 @@ class Environment(object):
if zip:
zip_file.close()
- log_function('Finished compiling templates')
+ log_function("Finished compiling templates")
def list_templates(self, extensions=None, filter_func=None):
"""Returns a list of templates for this environment. This requires
@@ -773,10 +809,10 @@ class Environment(object):
x = self.loader.list_templates()
if extensions is not None:
if filter_func is not None:
- raise TypeError('either extensions or filter_func '
- 'can be passed, but not both')
- filter_func = lambda x: '.' in x and \
- x.rsplit('.', 1)[1] in extensions
+ raise TypeError(
+ "either extensions or filter_func can be passed, but not both"
+ )
+ filter_func = lambda x: "." in x and x.rsplit(".", 1)[1] in extensions
if filter_func is not None:
x = list(ifilter(filter_func, x))
return x
@@ -786,6 +822,7 @@ class Environment(object):
rewritten exceptions or return a rendered traceback for the template.
"""
from jinja2.debug import rewrite_traceback_stack
+
reraise(*rewrite_traceback_stack(source=source))
def join_path(self, template, parent):
@@ -803,12 +840,13 @@ class Environment(object):
@internalcode
def _load_template(self, name, globals):
if self.loader is None:
- raise TypeError('no loader for this environment specified')
+ raise TypeError("no loader for this environment specified")
cache_key = (weakref.ref(self.loader), name)
if self.cache is not None:
template = self.cache.get(cache_key)
- if template is not None and (not self.auto_reload or
- template.is_up_to_date):
+ if template is not None and (
+ not self.auto_reload or template.is_up_to_date
+ ):
return template
template = self.loader.load(self, name, globals)
if self.cache is not None:
@@ -859,8 +897,9 @@ class Environment(object):
names._fail_with_undefined_error()
if not names:
- raise TemplatesNotFound(message=u'Tried to select from an empty list '
- u'of templates.')
+ raise TemplatesNotFound(
+ message=u"Tried to select from an empty list " u"of templates."
+ )
globals = self.make_globals(globals)
for name in names:
if isinstance(name, Template):
@@ -874,8 +913,7 @@ class Environment(object):
raise TemplatesNotFound(names)
@internalcode
- def get_or_select_template(self, template_name_or_list,
- parent=None, globals=None):
+ def get_or_select_template(self, template_name_or_list, parent=None, globals=None):
"""Does a typecheck and dispatches to :meth:`select_template`
if an iterable of template names is given, otherwise to
:meth:`get_template`.
@@ -937,33 +975,53 @@ class Template(object):
#: rather than through an existing environment.
environment_class = Environment
- def __new__(cls, source,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- lstrip_blocks=LSTRIP_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- keep_trailing_newline=KEEP_TRAILING_NEWLINE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False,
- enable_async=False):
+ def __new__(
+ cls,
+ source,
+ block_start_string=BLOCK_START_STRING,
+ block_end_string=BLOCK_END_STRING,
+ variable_start_string=VARIABLE_START_STRING,
+ variable_end_string=VARIABLE_END_STRING,
+ comment_start_string=COMMENT_START_STRING,
+ comment_end_string=COMMENT_END_STRING,
+ line_statement_prefix=LINE_STATEMENT_PREFIX,
+ line_comment_prefix=LINE_COMMENT_PREFIX,
+ trim_blocks=TRIM_BLOCKS,
+ lstrip_blocks=LSTRIP_BLOCKS,
+ newline_sequence=NEWLINE_SEQUENCE,
+ keep_trailing_newline=KEEP_TRAILING_NEWLINE,
+ extensions=(),
+ optimized=True,
+ undefined=Undefined,
+ finalize=None,
+ autoescape=False,
+ enable_async=False,
+ ):
env = get_spontaneous_environment(
cls.environment_class,
- block_start_string, block_end_string, variable_start_string,
- variable_end_string, comment_start_string, comment_end_string,
- line_statement_prefix, line_comment_prefix, trim_blocks,
- lstrip_blocks, newline_sequence, keep_trailing_newline,
- frozenset(extensions), optimized, undefined, finalize, autoescape,
- None, 0, False, None, enable_async)
+ block_start_string,
+ block_end_string,
+ variable_start_string,
+ variable_end_string,
+ comment_start_string,
+ comment_end_string,
+ line_statement_prefix,
+ line_comment_prefix,
+ trim_blocks,
+ lstrip_blocks,
+ newline_sequence,
+ keep_trailing_newline,
+ frozenset(extensions),
+ optimized,
+ undefined,
+ finalize,
+ autoescape,
+ None,
+ 0,
+ False,
+ None,
+ enable_async,
+ )
return env.from_string(source, template_class=cls)
@classmethod
@@ -971,10 +1029,7 @@ class Template(object):
"""Creates a template object from compiled code and the globals. This
is used by the loaders and environment to create a template object.
"""
- namespace = {
- 'environment': environment,
- '__file__': code.co_filename
- }
+ namespace = {"environment": environment, "__file__": code.co_filename}
exec(code, namespace)
rv = cls._from_namespace(environment, namespace, globals)
rv._uptodate = uptodate
@@ -994,21 +1049,21 @@ class Template(object):
t = object.__new__(cls)
t.environment = environment
t.globals = globals
- t.name = namespace['name']
- t.filename = namespace['__file__']
- t.blocks = namespace['blocks']
+ t.name = namespace["name"]
+ t.filename = namespace["__file__"]
+ t.blocks = namespace["blocks"]
# render function and module
- t.root_render_func = namespace['root']
+ t.root_render_func = namespace["root"]
t._module = None
# debug and loader helpers
- t._debug_info = namespace['debug_info']
+ t._debug_info = namespace["debug_info"]
t._uptodate = None
# store the reference
- namespace['environment'] = environment
- namespace['__jinja_template__'] = t
+ namespace["environment"] = environment
+ namespace["__jinja_template__"] = t
return t
@@ -1038,8 +1093,9 @@ class Template(object):
await template.render_async(knights='that say nih; asynchronously')
"""
# see asyncsupport for the actual implementation
- raise NotImplementedError('This feature is not available for this '
- 'version of Python')
+ raise NotImplementedError(
+ "This feature is not available for this version of Python"
+ )
def stream(self, *args, **kwargs):
"""Works exactly like :meth:`generate` but returns a
@@ -1067,8 +1123,9 @@ class Template(object):
returns an async iterator instead.
"""
# see asyncsupport for the actual implementation
- raise NotImplementedError('This feature is not available for this '
- 'version of Python')
+ raise NotImplementedError(
+ "This feature is not available for this version of Python"
+ )
def new_context(self, vars=None, shared=False, locals=None):
"""Create a new :class:`Context` for this template. The vars
@@ -1078,8 +1135,9 @@ class Template(object):
`locals` can be a dict of local variables for internal usage.
"""
- return new_context(self.environment, self.name, self.blocks,
- vars, shared, self.globals, locals)
+ return new_context(
+ self.environment, self.name, self.blocks, vars, shared, self.globals, locals
+ )
def make_module(self, vars=None, shared=False, locals=None):
"""This method works like the :attr:`module` attribute when called
@@ -1097,8 +1155,9 @@ class Template(object):
becomes unavailable in async mode.
"""
# see asyncsupport for the actual implementation
- raise NotImplementedError('This feature is not available for this '
- 'version of Python')
+ raise NotImplementedError(
+ "This feature is not available for this version of Python"
+ )
@internalcode
def _get_default_module(self):
@@ -1143,16 +1202,15 @@ class Template(object):
def debug_info(self):
"""The debug info mapping."""
if self._debug_info:
- return [tuple(imap(int, x.split('='))) for x in
- self._debug_info.split('&')]
+ return [tuple(imap(int, x.split("="))) for x in self._debug_info.split("&")]
return []
def __repr__(self):
if self.name is None:
- name = 'memory:%x' % id(self)
+ name = "memory:%x" % id(self)
else:
name = repr(self.name)
- return '<%s %s>' % (self.__class__.__name__, name)
+ return "<%s %s>" % (self.__class__.__name__, name)
@implements_to_string
@@ -1165,10 +1223,12 @@ class TemplateModule(object):
def __init__(self, template, context, body_stream=None):
if body_stream is None:
if context.environment.is_async:
- raise RuntimeError('Async mode requires a body stream '
- 'to be passed to a template module. Use '
- 'the async methods of the API you are '
- 'using.')
+ raise RuntimeError(
+ "Async mode requires a body stream "
+ "to be passed to a template module. Use "
+ "the async methods of the API you are "
+ "using."
+ )
body_stream = list(template.root_render_func(context))
self._body_stream = body_stream
self.__dict__.update(context.get_exported())
@@ -1182,10 +1242,10 @@ class TemplateModule(object):
def __repr__(self):
if self.__name__ is None:
- name = 'memory:%x' % id(self)
+ name = "memory:%x" % id(self)
else:
name = repr(self.__name__)
- return '<%s %s>' % (self.__class__.__name__, name)
+ return "<%s %s>" % (self.__class__.__name__, name)
class TemplateExpression(object):
@@ -1201,7 +1261,7 @@ class TemplateExpression(object):
def __call__(self, *args, **kwargs):
context = self._template.new_context(dict(*args, **kwargs))
consume(self._template.root_render_func(context))
- rv = context.vars['result']
+ rv = context.vars["result"]
if self._undefined_to_none and isinstance(rv, Undefined):
rv = None
return rv
@@ -1223,7 +1283,7 @@ class TemplateStream(object):
self._gen = gen
self.disable_buffering()
- def dump(self, fp, encoding=None, errors='strict'):
+ def dump(self, fp, encoding=None, errors="strict"):
"""Dump the complete stream into a file or file-like object.
Per default unicode strings are written, if you want to encode
before writing specify an `encoding`.
@@ -1235,15 +1295,15 @@ class TemplateStream(object):
close = False
if isinstance(fp, string_types):
if encoding is None:
- encoding = 'utf-8'
- fp = open(fp, 'wb')
+ encoding = "utf-8"
+ fp = open(fp, "wb")
close = True
try:
if encoding is not None:
iterable = (x.encode(encoding, errors) for x in self)
else:
iterable = self
- if hasattr(fp, 'writelines'):
+ if hasattr(fp, "writelines"):
fp.writelines(iterable)
else:
for item in iterable:
@@ -1279,7 +1339,7 @@ class TemplateStream(object):
def enable_buffering(self, size=5):
"""Enable buffering. Buffer `size` items before yielding them."""
if size <= 1:
- raise ValueError('buffer size too small')
+ raise ValueError("buffer size too small")
self.buffered = True
self._next = partial(next, self._buffered_generator(size))
diff --git a/src/jinja2/exceptions.py b/src/jinja2/exceptions.py
index 6ceea30..1038370 100644
--- a/src/jinja2/exceptions.py
+++ b/src/jinja2/exceptions.py
@@ -18,9 +18,10 @@ class TemplateError(Exception):
"""Baseclass for all template errors."""
if PY2:
+
def __init__(self, message=None):
if message is not None:
- message = text_type(message).encode('utf-8')
+ message = text_type(message).encode("utf-8")
Exception.__init__(self, message)
@property
@@ -28,11 +29,13 @@ class TemplateError(Exception):
if self.args:
message = self.args[0]
if message is not None:
- return message.decode('utf-8', 'replace')
+ return message.decode("utf-8", "replace")
def __unicode__(self):
- return self.message or u''
+ return self.message or u""
+
else:
+
def __init__(self, message=None):
Exception.__init__(self, message)
@@ -100,8 +103,9 @@ class TemplatesNotFound(TemplateNotFound):
else:
parts.append(name)
- message = u'none of the templates given were found: ' + \
- u', '.join(imap(text_type, parts))
+ message = u"none of the templates given were found: " + u", ".join(
+ imap(text_type, parts)
+ )
TemplateNotFound.__init__(self, names and names[-1] or None, message)
self.templates = list(names)
@@ -127,11 +131,11 @@ class TemplateSyntaxError(TemplateError):
return self.message
# otherwise attach some stuff
- location = 'line %d' % self.lineno
+ location = "line %d" % self.lineno
name = self.filename or self.name
if name:
location = 'File "%s", %s' % (name, location)
- lines = [self.message, ' ' + location]
+ lines = [self.message, " " + location]
# if the source is set, add the line to the output
if self.source is not None:
@@ -140,9 +144,9 @@ class TemplateSyntaxError(TemplateError):
except IndexError:
line = None
if line:
- lines.append(' ' + line.strip())
+ lines.append(" " + line.strip())
- return u'\n'.join(lines)
+ return u"\n".join(lines)
def __reduce__(self):
# https://bugs.python.org/issue1692335 Exceptions that take
diff --git a/src/jinja2/ext.py b/src/jinja2/ext.py
index 433277a..4891c9c 100644
--- a/src/jinja2/ext.py
+++ b/src/jinja2/ext.py
@@ -46,7 +46,7 @@ from .utils import Markup
# the only real useful gettext functions for a Jinja template. Note
# that ugettext must be assigned to gettext as Jinja doesn't support
# non unicode strings.
-GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
+GETTEXT_FUNCTIONS = ("_", "gettext", "ngettext")
class ExtensionRegistry(type):
@@ -54,7 +54,7 @@ class ExtensionRegistry(type):
def __new__(cls, name, bases, d):
rv = type.__new__(cls, name, bases, d)
- rv.identifier = rv.__module__ + '.' + rv.__name__
+ rv.identifier = rv.__module__ + "." + rv.__name__
return rv
@@ -134,8 +134,9 @@ class Extension(with_metaclass(ExtensionRegistry, object)):
"""
return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
- def call_method(self, name, args=None, kwargs=None, dyn_args=None,
- dyn_kwargs=None, lineno=None):
+ def call_method(
+ self, name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None
+ ):
"""Call a method of the extension. This is a shortcut for
:meth:`attr` + :class:`jinja2.nodes.Call`.
"""
@@ -143,13 +144,19 @@ class Extension(with_metaclass(ExtensionRegistry, object)):
args = []
if kwargs is None:
kwargs = []
- return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
- dyn_args, dyn_kwargs, lineno=lineno)
+ return nodes.Call(
+ self.attr(name, lineno=lineno),
+ args,
+ kwargs,
+ dyn_args,
+ dyn_kwargs,
+ lineno=lineno,
+ )
@contextfunction
def _gettext_alias(__context, *args, **kwargs):
- return __context.call(__context.resolve('gettext'), *args, **kwargs)
+ return __context.call(__context.resolve("gettext"), *args, **kwargs)
def _make_new_gettext(func):
@@ -162,24 +169,27 @@ def _make_new_gettext(func):
# variables. This makes translation strings more consistent
# and predictable. This requires escaping
return rv % variables
+
return gettext
def _make_new_ngettext(func):
@contextfunction
def ngettext(__context, __singular, __plural, __num, **variables):
- variables.setdefault('num', __num)
+ variables.setdefault("num", __num)
rv = __context.call(func, __singular, __plural, __num)
if __context.eval_ctx.autoescape:
rv = Markup(rv)
# Always treat as a format string, see gettext comment above.
return rv % variables
+
return ngettext
class InternationalizationExtension(Extension):
"""This extension adds gettext support to Jinja."""
- tags = {'trans'}
+
+ tags = {"trans"}
# TODO: the i18n extension is currently reevaluating values in a few
# situations. Take this example:
@@ -190,30 +200,28 @@ class InternationalizationExtension(Extension):
def __init__(self, environment):
Extension.__init__(self, environment)
- environment.globals['_'] = _gettext_alias
+ environment.globals["_"] = _gettext_alias
environment.extend(
install_gettext_translations=self._install,
install_null_translations=self._install_null,
install_gettext_callables=self._install_callables,
uninstall_gettext_translations=self._uninstall,
extract_translations=self._extract,
- newstyle_gettext=False
+ newstyle_gettext=False,
)
def _install(self, translations, newstyle=None):
- gettext = getattr(translations, 'ugettext', None)
+ gettext = getattr(translations, "ugettext", None)
if gettext is None:
gettext = translations.gettext
- ngettext = getattr(translations, 'ungettext', None)
+ ngettext = getattr(translations, "ungettext", None)
if ngettext is None:
ngettext = translations.ngettext
self._install_callables(gettext, ngettext, newstyle)
def _install_null(self, newstyle=None):
self._install_callables(
- lambda x: x,
- lambda s, p, n: (n != 1 and (p,) or (s,))[0],
- newstyle
+ lambda x: x, lambda s, p, n: (n != 1 and (p,) or (s,))[0], newstyle
)
def _install_callables(self, gettext, ngettext, newstyle=None):
@@ -222,13 +230,10 @@ class InternationalizationExtension(Extension):
if self.environment.newstyle_gettext:
gettext = _make_new_gettext(gettext)
ngettext = _make_new_ngettext(ngettext)
- self.environment.globals.update(
- gettext=gettext,
- ngettext=ngettext
- )
+ self.environment.globals.update(gettext=gettext, ngettext=ngettext)
def _uninstall(self, translations):
- for key in 'gettext', 'ngettext':
+ for key in "gettext", "ngettext":
self.environment.globals.pop(key, None)
def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
@@ -248,41 +253,44 @@ class InternationalizationExtension(Extension):
plural_expr_assignment = None
variables = {}
trimmed = None
- while parser.stream.current.type != 'block_end':
+ while parser.stream.current.type != "block_end":
if variables:
- parser.stream.expect('comma')
+ parser.stream.expect("comma")
# skip colon for python compatibility
- if parser.stream.skip_if('colon'):
+ if parser.stream.skip_if("colon"):
break
- name = parser.stream.expect('name')
+ name = parser.stream.expect("name")
if name.value in variables:
- parser.fail('translatable variable %r defined twice.' %
- name.value, name.lineno,
- exc=TemplateAssertionError)
+ parser.fail(
+ "translatable variable %r defined twice." % name.value,
+ name.lineno,
+ exc=TemplateAssertionError,
+ )
# expressions
- if parser.stream.current.type == 'assign':
+ if parser.stream.current.type == "assign":
next(parser.stream)
variables[name.value] = var = parser.parse_expression()
- elif trimmed is None and name.value in ('trimmed', 'notrimmed'):
- trimmed = name.value == 'trimmed'
+ elif trimmed is None and name.value in ("trimmed", "notrimmed"):
+ trimmed = name.value == "trimmed"
continue
else:
- variables[name.value] = var = nodes.Name(name.value, 'load')
+ variables[name.value] = var = nodes.Name(name.value, "load")
if plural_expr is None:
if isinstance(var, nodes.Call):
- plural_expr = nodes.Name('_trans', 'load')
+ plural_expr = nodes.Name("_trans", "load")
variables[name.value] = plural_expr
plural_expr_assignment = nodes.Assign(
- nodes.Name('_trans', 'store'), var)
+ nodes.Name("_trans", "store"), var
+ )
else:
plural_expr = var
- num_called_num = name.value == 'num'
+ num_called_num = name.value == "num"
- parser.stream.expect('block_end')
+ parser.stream.expect("block_end")
plural = None
have_plural = False
@@ -293,22 +301,24 @@ class InternationalizationExtension(Extension):
if singular_names:
referenced.update(singular_names)
if plural_expr is None:
- plural_expr = nodes.Name(singular_names[0], 'load')
- num_called_num = singular_names[0] == 'num'
+ plural_expr = nodes.Name(singular_names[0], "load")
+ num_called_num = singular_names[0] == "num"
# if we have a pluralize block, we parse that too
- if parser.stream.current.test('name:pluralize'):
+ if parser.stream.current.test("name:pluralize"):
have_plural = True
next(parser.stream)
- if parser.stream.current.type != 'block_end':
- name = parser.stream.expect('name')
+ if parser.stream.current.type != "block_end":
+ name = parser.stream.expect("name")
if name.value not in variables:
- parser.fail('unknown variable %r for pluralization' %
- name.value, name.lineno,
- exc=TemplateAssertionError)
+ parser.fail(
+ "unknown variable %r for pluralization" % name.value,
+ name.lineno,
+ exc=TemplateAssertionError,
+ )
plural_expr = variables[name.value]
- num_called_num = name.value == 'num'
- parser.stream.expect('block_end')
+ num_called_num = name.value == "num"
+ parser.stream.expect("block_end")
plural_names, plural = self._parse_block(parser, False)
next(parser.stream)
referenced.update(plural_names)
@@ -318,88 +328,97 @@ class InternationalizationExtension(Extension):
# register free names as simple name expressions
for var in referenced:
if var not in variables:
- variables[var] = nodes.Name(var, 'load')
+ variables[var] = nodes.Name(var, "load")
if not have_plural:
plural_expr = None
elif plural_expr is None:
- parser.fail('pluralize without variables', lineno)
+ parser.fail("pluralize without variables", lineno)
if trimmed is None:
- trimmed = self.environment.policies['ext.i18n.trimmed']
+ trimmed = self.environment.policies["ext.i18n.trimmed"]
if trimmed:
singular = self._trim_whitespace(singular)
if plural:
plural = self._trim_whitespace(plural)
- node = self._make_node(singular, plural, variables, plural_expr,
- bool(referenced),
- num_called_num and have_plural)
+ node = self._make_node(
+ singular,
+ plural,
+ variables,
+ plural_expr,
+ bool(referenced),
+ num_called_num and have_plural,
+ )
node.set_lineno(lineno)
if plural_expr_assignment is not None:
return [plural_expr_assignment, node]
else:
return node
- def _trim_whitespace(self, string, _ws_re=re.compile(r'\s*\n\s*')):
- return _ws_re.sub(' ', string.strip())
+ def _trim_whitespace(self, string, _ws_re=re.compile(r"\s*\n\s*")):
+ return _ws_re.sub(" ", string.strip())
def _parse_block(self, parser, allow_pluralize):
"""Parse until the next block tag with a given name."""
referenced = []
buf = []
while 1:
- if parser.stream.current.type == 'data':
- buf.append(parser.stream.current.value.replace('%', '%%'))
+ if parser.stream.current.type == "data":
+ buf.append(parser.stream.current.value.replace("%", "%%"))
next(parser.stream)
- elif parser.stream.current.type == 'variable_begin':
+ elif parser.stream.current.type == "variable_begin":
next(parser.stream)
- name = parser.stream.expect('name').value
+ name = parser.stream.expect("name").value
referenced.append(name)
- buf.append('%%(%s)s' % name)
- parser.stream.expect('variable_end')
- elif parser.stream.current.type == 'block_begin':
+ buf.append("%%(%s)s" % name)
+ parser.stream.expect("variable_end")
+ elif parser.stream.current.type == "block_begin":
next(parser.stream)
- if parser.stream.current.test('name:endtrans'):
+ if parser.stream.current.test("name:endtrans"):
break
- elif parser.stream.current.test('name:pluralize'):
+ elif parser.stream.current.test("name:pluralize"):
if allow_pluralize:
break
- parser.fail('a translatable section can have only one '
- 'pluralize section')
- parser.fail('control structures in translatable sections are '
- 'not allowed')
+ parser.fail(
+ "a translatable section can have only one pluralize section"
+ )
+ parser.fail(
+ "control structures in translatable sections are not allowed"
+ )
elif parser.stream.eos:
- parser.fail('unclosed translation block')
+ parser.fail("unclosed translation block")
else:
- assert False, 'internal parser error'
+ assert False, "internal parser error"
return referenced, concat(buf)
- def _make_node(self, singular, plural, variables, plural_expr,
- vars_referenced, num_called_num):
+ def _make_node(
+ self, singular, plural, variables, plural_expr, vars_referenced, num_called_num
+ ):
"""Generates a useful node from the data provided."""
# no variables referenced? no need to escape for old style
# gettext invocations only if there are vars.
if not vars_referenced and not self.environment.newstyle_gettext:
- singular = singular.replace('%%', '%')
+ singular = singular.replace("%%", "%")
if plural:
- plural = plural.replace('%%', '%')
+ plural = plural.replace("%%", "%")
# singular only:
if plural_expr is None:
- gettext = nodes.Name('gettext', 'load')
- node = nodes.Call(gettext, [nodes.Const(singular)],
- [], None, None)
+ gettext = nodes.Name("gettext", "load")
+ node = nodes.Call(gettext, [nodes.Const(singular)], [], None, None)
# singular and plural
else:
- ngettext = nodes.Name('ngettext', 'load')
- node = nodes.Call(ngettext, [
- nodes.Const(singular),
- nodes.Const(plural),
- plural_expr
- ], [], None, None)
+ ngettext = nodes.Name("ngettext", "load")
+ node = nodes.Call(
+ ngettext,
+ [nodes.Const(singular), nodes.Const(plural), plural_expr],
+ [],
+ None,
+ None,
+ )
# in case newstyle gettext is used, the method is powerful
# enough to handle the variable expansion and autoescape
@@ -408,7 +427,7 @@ class InternationalizationExtension(Extension):
for key, value in iteritems(variables):
# the function adds that later anyways in case num was
# called num, so just skip it.
- if num_called_num and key == 'num':
+ if num_called_num and key == "num":
continue
node.kwargs.append(nodes.Keyword(key, value))
@@ -418,10 +437,15 @@ class InternationalizationExtension(Extension):
# environment with autoescaping turned on
node = nodes.MarkSafeIfAutoescape(node)
if variables:
- node = nodes.Mod(node, nodes.Dict([
- nodes.Pair(nodes.Const(key), value)
- for key, value in variables.items()
- ]))
+ node = nodes.Mod(
+ node,
+ nodes.Dict(
+ [
+ nodes.Pair(nodes.Const(key), value)
+ for key, value in variables.items()
+ ]
+ ),
+ )
return nodes.Output([node])
@@ -429,7 +453,8 @@ class ExprStmtExtension(Extension):
"""Adds a `do` tag to Jinja that works like the print statement just
that it doesn't print the return value.
"""
- tags = set(['do'])
+
+ tags = set(["do"])
def parse(self, parser):
node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
@@ -439,11 +464,12 @@ class ExprStmtExtension(Extension):
class LoopControlExtension(Extension):
"""Adds break and continue to the template engine."""
- tags = set(['break', 'continue'])
+
+ tags = set(["break", "continue"])
def parse(self, parser):
token = next(parser.stream)
- if token.value == 'break':
+ if token.value == "break":
return nodes.Break(lineno=token.lineno)
return nodes.Continue(lineno=token.lineno)
@@ -476,7 +502,8 @@ class DebugExtension(Extension):
.. versionadded:: 2.11.0
"""
- tags = {'debug'}
+
+ tags = {"debug"}
def parse(self, parser):
lineno = parser.stream.expect("name:debug").lineno
@@ -498,8 +525,7 @@ class DebugExtension(Extension):
return pprint.pformat(result, depth=3)
-def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
- babel_style=True):
+def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True):
"""Extract localizable strings from the given template node. Per
default this function returns matches in babel style that means non string
parameters as well as keyword arguments are returned as `None`. This
@@ -535,14 +561,15 @@ def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
extraction interface or extract comments yourself.
"""
for node in node.find_all(nodes.Call):
- if not isinstance(node.node, nodes.Name) or \
- node.node.name not in gettext_functions:
+ if (
+ not isinstance(node.node, nodes.Name)
+ or node.node.name not in gettext_functions
+ ):
continue
strings = []
for arg in node.args:
- if isinstance(arg, nodes.Const) and \
- isinstance(arg.value, string_types):
+ if isinstance(arg, nodes.Const) and isinstance(arg.value, string_types):
strings.append(arg.value)
else:
strings.append(None)
@@ -581,9 +608,10 @@ class _CommentFinder(object):
def find_backwards(self, offset):
try:
- for _, token_type, token_value in \
- reversed(self.tokens[self.offset:offset]):
- if token_type in ('comment', 'linecomment'):
+ for _, token_type, token_value in reversed(
+ self.tokens[self.offset : offset]
+ ):
+ if token_type in ("comment", "linecomment"):
try:
prefix, comment = token_value.split(None, 1)
except ValueError:
@@ -597,7 +625,7 @@ class _CommentFinder(object):
def find_comments(self, lineno):
if not self.comment_tags or self.last_lineno > lineno:
return []
- for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]):
+ for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
if token_lineno > lineno:
return self.find_backwards(self.offset + idx)
return self.find_backwards(len(self.tokens))
@@ -632,7 +660,7 @@ def babel_extract(fileobj, keywords, comment_tags, options):
(comments will be empty currently)
"""
extensions = set()
- for extension in options.get('extensions', '').split(','):
+ for extension in options.get("extensions", "").split(","):
extension = extension.strip()
if not extension:
continue
@@ -641,34 +669,33 @@ def babel_extract(fileobj, keywords, comment_tags, options):
extensions.add(InternationalizationExtension)
def getbool(options, key, default=False):
- return options.get(key, str(default)).lower() in \
- ('1', 'on', 'yes', 'true')
+ return options.get(key, str(default)).lower() in ("1", "on", "yes", "true")
- silent = getbool(options, 'silent', True)
+ silent = getbool(options, "silent", True)
environment = Environment(
- options.get('block_start_string', BLOCK_START_STRING),
- options.get('block_end_string', BLOCK_END_STRING),
- options.get('variable_start_string', VARIABLE_START_STRING),
- options.get('variable_end_string', VARIABLE_END_STRING),
- options.get('comment_start_string', COMMENT_START_STRING),
- options.get('comment_end_string', COMMENT_END_STRING),
- options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
- options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
- getbool(options, 'trim_blocks', TRIM_BLOCKS),
- getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS),
+ options.get("block_start_string", BLOCK_START_STRING),
+ options.get("block_end_string", BLOCK_END_STRING),
+ options.get("variable_start_string", VARIABLE_START_STRING),
+ options.get("variable_end_string", VARIABLE_END_STRING),
+ options.get("comment_start_string", COMMENT_START_STRING),
+ options.get("comment_end_string", COMMENT_END_STRING),
+ options.get("line_statement_prefix") or LINE_STATEMENT_PREFIX,
+ options.get("line_comment_prefix") or LINE_COMMENT_PREFIX,
+ getbool(options, "trim_blocks", TRIM_BLOCKS),
+ getbool(options, "lstrip_blocks", LSTRIP_BLOCKS),
NEWLINE_SEQUENCE,
- getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE),
+ getbool(options, "keep_trailing_newline", KEEP_TRAILING_NEWLINE),
frozenset(extensions),
cache_size=0,
- auto_reload=False
+ auto_reload=False,
)
- if getbool(options, 'trimmed'):
- environment.policies['ext.i18n.trimmed'] = True
- if getbool(options, 'newstyle_gettext'):
+ if getbool(options, "trimmed"):
+ environment.policies["ext.i18n.trimmed"] = True
+ if getbool(options, "newstyle_gettext"):
environment.newstyle_gettext = True
- source = fileobj.read().decode(options.get('encoding', 'utf-8'))
+ source = fileobj.read().decode(options.get("encoding", "utf-8"))
try:
node = environment.parse(source)
tokens = list(environment.lex(environment.preprocess(source)))
diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py
index 8bd389c..c7a2f39 100644
--- a/src/jinja2/filters.py
+++ b/src/jinja2/filters.py
@@ -31,8 +31,8 @@ from .utils import soft_unicode
from .utils import unicode_urlencode
from .utils import urlize
-_word_re = re.compile(r'\w+', re.UNICODE)
-_word_beginning_split_re = re.compile(r'([-\s\(\{\[\<]+)', re.UNICODE)
+_word_re = re.compile(r"\w+", re.UNICODE)
+_word_beginning_split_re = re.compile(r"([-\s\(\{\[\<]+)", re.UNICODE)
def contextfilter(f):
@@ -102,8 +102,12 @@ def make_multi_attrgetter(environment, attribute, postprocess=None):
Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc.
"""
- attribute_parts = attribute.split(',') if isinstance(attribute, string_types) else [attribute]
- attribute = [_prepare_attribute_parts(attribute_part) for attribute_part in attribute_parts]
+ attribute_parts = (
+ attribute.split(",") if isinstance(attribute, string_types) else [attribute]
+ )
+ attribute = [
+ _prepare_attribute_parts(attribute_part) for attribute_part in attribute_parts
+ ]
def attrgetter(item):
items = [None] * len(attribute)
@@ -125,14 +129,14 @@ def _prepare_attribute_parts(attr):
if attr is None:
return []
elif isinstance(attr, string_types):
- return [int(x) if x.isdigit() else x for x in attr.split('.')]
+ return [int(x) if x.isdigit() else x for x in attr.split(".")]
else:
return [attr]
def do_forceescape(value):
"""Enforce HTML escaping. This will probably double escape variables."""
- if hasattr(value, '__html__'):
+ if hasattr(value, "__html__"):
value = value.__html__()
return escape(text_type(value))
@@ -187,8 +191,11 @@ def do_replace(eval_ctx, s, old, new, count=None):
count = -1
if not eval_ctx.autoescape:
return text_type(s).replace(text_type(old), text_type(new), count)
- if hasattr(old, '__html__') or hasattr(new, '__html__') and \
- not hasattr(s, '__html__'):
+ if (
+ hasattr(old, "__html__")
+ or hasattr(new, "__html__")
+ and not hasattr(s, "__html__")
+ ):
s = escape(s)
else:
s = soft_unicode(s)
@@ -229,13 +236,13 @@ def do_xmlattr(_eval_ctx, d, autospace=True):
As you can see it automatically prepends a space in front of the item
if the filter returned something unless the second parameter is false.
"""
- rv = u' '.join(
+ rv = u" ".join(
u'%s="%s"' % (escape(key), escape(value))
for key, value in iteritems(d)
if value is not None and not isinstance(value, Undefined)
)
if autospace and rv:
- rv = u' ' + rv
+ rv = u" " + rv
if _eval_ctx.autoescape:
rv = Markup(rv)
return rv
@@ -252,13 +259,16 @@ def do_title(s):
"""Return a titlecased version of the value. I.e. words will start with
uppercase letters, all remaining characters are lowercase.
"""
- return ''.join(
- [item[0].upper() + item[1:].lower()
- for item in _word_beginning_split_re.split(soft_unicode(s))
- if item])
+ return "".join(
+ [
+ item[0].upper() + item[1:].lower()
+ for item in _word_beginning_split_re.split(soft_unicode(s))
+ if item
+ ]
+ )
-def do_dictsort(value, case_sensitive=False, by='key', reverse=False):
+def do_dictsort(value, case_sensitive=False, by="key", reverse=False):
"""Sort a dict and yield (key, value) pairs. Because python dicts are
unsorted you may want to use this function to order them by either
key or value:
@@ -277,14 +287,12 @@ def do_dictsort(value, case_sensitive=False, by='key', reverse=False):
{% for item in mydict|dictsort(false, 'value') %}
sort the dict by value, case insensitive
"""
- if by == 'key':
+ if by == "key":
pos = 0
- elif by == 'value':
+ elif by == "value":
pos = 1
else:
- raise FilterArgumentError(
- 'You can only sort by either "key" or "value"'
- )
+ raise FilterArgumentError('You can only sort by either "key" or "value"')
def sort_func(item):
value = item[pos]
@@ -341,8 +349,7 @@ def do_sort(environment, value, reverse=False, case_sensitive=False, attribute=N
The ``attribute`` parameter was added.
"""
key_func = make_multi_attrgetter(
- environment, attribute,
- postprocess=ignore_case if not case_sensitive else None
+ environment, attribute, postprocess=ignore_case if not case_sensitive else None
)
return sorted(value, key=key_func, reverse=reverse)
@@ -363,8 +370,7 @@ def do_unique(environment, value, case_sensitive=False, attribute=None):
:param attribute: Filter objects with unique values for this attribute.
"""
getter = make_attrgetter(
- environment, attribute,
- postprocess=ignore_case if not case_sensitive else None
+ environment, attribute, postprocess=ignore_case if not case_sensitive else None
)
seen = set()
@@ -382,12 +388,10 @@ def _min_or_max(environment, value, func, case_sensitive, attribute):
try:
first = next(it)
except StopIteration:
- return environment.undefined('No aggregated item, sequence was empty.')
+ return environment.undefined("No aggregated item, sequence was empty.")
key_func = make_attrgetter(
- environment,
- attribute,
- postprocess=ignore_case if not case_sensitive else None
+ environment, attribute, postprocess=ignore_case if not case_sensitive else None
)
return func(chain([first], it), key=key_func)
@@ -422,7 +426,7 @@ def do_max(environment, value, case_sensitive=False, attribute=None):
return _min_or_max(environment, value, max, case_sensitive, attribute)
-def do_default(value, default_value=u'', boolean=False):
+def do_default(value, default_value=u"", boolean=False):
"""If the value is undefined it will return the passed default value,
otherwise the value of the variable:
@@ -451,7 +455,7 @@ def do_default(value, default_value=u'', boolean=False):
@evalcontextfilter
-def do_join(eval_ctx, value, d=u'', attribute=None):
+def do_join(eval_ctx, value, d=u"", attribute=None):
"""Return a string which is the concatenation of the strings in the
sequence. The separator between elements is an empty string per
default, you can define it with the optional parameter:
@@ -482,11 +486,11 @@ def do_join(eval_ctx, value, d=u'', attribute=None):
# if the delimiter doesn't have an html representation we check
# if any of the items has. If yes we do a coercion to Markup
- if not hasattr(d, '__html__'):
+ if not hasattr(d, "__html__"):
value = list(value)
do_escape = False
for idx, item in enumerate(value):
- if hasattr(item, '__html__'):
+ if hasattr(item, "__html__"):
do_escape = True
else:
value[idx] = text_type(item)
@@ -511,7 +515,7 @@ def do_first(environment, seq):
try:
return next(iter(seq))
except StopIteration:
- return environment.undefined('No first item, sequence was empty.')
+ return environment.undefined("No first item, sequence was empty.")
@environmentfilter
@@ -528,7 +532,7 @@ def do_last(environment, seq):
try:
return next(iter(reversed(seq)))
except StopIteration:
- return environment.undefined('No last item, sequence was empty.')
+ return environment.undefined("No last item, sequence was empty.")
@contextfilter
@@ -537,7 +541,7 @@ def do_random(context, seq):
try:
return random.choice(seq)
except IndexError:
- return context.environment.undefined('No random item, sequence was empty.')
+ return context.environment.undefined("No random item, sequence was empty.")
def do_filesizeformat(value, binary=False):
@@ -549,25 +553,25 @@ def do_filesizeformat(value, binary=False):
bytes = float(value)
base = binary and 1024 or 1000
prefixes = [
- (binary and 'KiB' or 'kB'),
- (binary and 'MiB' or 'MB'),
- (binary and 'GiB' or 'GB'),
- (binary and 'TiB' or 'TB'),
- (binary and 'PiB' or 'PB'),
- (binary and 'EiB' or 'EB'),
- (binary and 'ZiB' or 'ZB'),
- (binary and 'YiB' or 'YB')
+ (binary and "KiB" or "kB"),
+ (binary and "MiB" or "MB"),
+ (binary and "GiB" or "GB"),
+ (binary and "TiB" or "TB"),
+ (binary and "PiB" or "PB"),
+ (binary and "EiB" or "EB"),
+ (binary and "ZiB" or "ZB"),
+ (binary and "YiB" or "YB"),
]
if bytes == 1:
- return '1 Byte'
+ return "1 Byte"
elif bytes < base:
- return '%d Bytes' % bytes
+ return "%d Bytes" % bytes
else:
for i, prefix in enumerate(prefixes):
unit = base ** (i + 2)
if bytes < unit:
- return '%.1f %s' % ((base * bytes / unit), prefix)
- return '%.1f %s' % ((base * bytes / unit), prefix)
+ return "%.1f %s" % ((base * bytes / unit), prefix)
+ return "%.1f %s" % ((base * bytes / unit), prefix)
def do_pprint(value, verbose=False):
@@ -580,8 +584,9 @@ def do_pprint(value, verbose=False):
@evalcontextfilter
-def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False,
- target=None, rel=None):
+def do_urlize(
+ eval_ctx, value, trim_url_limit=None, nofollow=False, target=None, rel=None
+):
"""Converts URLs in plain text into clickable links.
If you pass the filter an additional integer it will shorten the urls
@@ -604,22 +609,20 @@ def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False,
The *target* parameter was added.
"""
policies = eval_ctx.environment.policies
- rel = set((rel or '').split() or [])
+ rel = set((rel or "").split() or [])
if nofollow:
- rel.add('nofollow')
- rel.update((policies['urlize.rel'] or '').split())
+ rel.add("nofollow")
+ rel.update((policies["urlize.rel"] or "").split())
if target is None:
- target = policies['urlize.target']
- rel = ' '.join(sorted(rel)) or None
+ target = policies["urlize.target"]
+ rel = " ".join(sorted(rel)) or None
rv = urlize(value, trim_url_limit, rel=rel, target=target)
if eval_ctx.autoescape:
rv = Markup(rv)
return rv
-def do_indent(
- s, width=4, first=False, blank=False, indentfirst=None
-):
+def do_indent(s, width=4, first=False, blank=False, indentfirst=None):
"""Return a copy of the string with each line indented by 4 spaces. The
first line and blank lines are not indented by default.
@@ -633,13 +636,14 @@ def do_indent(
Rename the ``indentfirst`` argument to ``first``.
"""
if indentfirst is not None:
- warnings.warn(DeprecationWarning(
- 'The "indentfirst" argument is renamed to "first".'
- ), stacklevel=2)
+ warnings.warn(
+ DeprecationWarning('The "indentfirst" argument is renamed to "first".'),
+ stacklevel=2,
+ )
first = indentfirst
- indention = u' ' * width
- newline = u'\n'
+ indention = u" " * width
+ newline = u"\n"
if isinstance(s, Markup):
indention = Markup(indention)
@@ -665,7 +669,7 @@ def do_indent(
@environmentfilter
-def do_truncate(env, s, length=255, killwords=False, end='...', leeway=None):
+def do_truncate(env, s, length=255, killwords=False, end="...", leeway=None):
"""Return a truncated copy of the string. The length is specified
with the first parameter which defaults to ``255``. If the second
parameter is ``true`` the filter will cut the text at length. Otherwise
@@ -690,14 +694,14 @@ def do_truncate(env, s, length=255, killwords=False, end='...', leeway=None):
can be reconfigured globally.
"""
if leeway is None:
- leeway = env.policies['truncate.leeway']
- assert length >= len(end), 'expected length >= %s, got %s' % (len(end), length)
- assert leeway >= 0, 'expected leeway >= 0, got %s' % leeway
+ leeway = env.policies["truncate.leeway"]
+ assert length >= len(end), "expected length >= %s, got %s" % (len(end), length)
+ assert leeway >= 0, "expected leeway >= 0, got %s" % leeway
if len(s) <= length + leeway:
return s
if killwords:
- return s[:length - len(end)] + end
- result = s[:length - len(end)].rsplit(' ', 1)[0]
+ return s[: length - len(end)] + end
+ result = s[: length - len(end)].rsplit(" ", 1)[0]
return result + end
@@ -816,8 +820,9 @@ def do_format(value, *args, **kwargs):
#printf-style-string-formatting
"""
if args and kwargs:
- raise FilterArgumentError('can\'t handle positional and keyword '
- 'arguments at the same time')
+ raise FilterArgumentError(
+ "can't handle positional and keyword arguments at the same time"
+ )
return soft_unicode(value) % (kwargs or args)
@@ -827,9 +832,8 @@ def do_trim(value, chars=None):
def do_striptags(value):
- """Strip SGML/XML tags and replace adjacent whitespace by one space.
- """
- if hasattr(value, '__html__'):
+ """Strip SGML/XML tags and replace adjacent whitespace by one space."""
+ if hasattr(value, "__html__"):
value = value.__html__()
return Markup(text_type(value)).striptags()
@@ -901,7 +905,7 @@ def do_batch(value, linecount, fill_with=None):
yield tmp
-def do_round(value, precision=0, method='common'):
+def do_round(value, precision=0, method="common"):
"""Round the number to a given precision. The first
parameter specifies the precision (default is ``0``), the
second the rounding method:
@@ -927,9 +931,9 @@ def do_round(value, precision=0, method='common'):
{{ 42.55|round|int }}
-> 43
"""
- if not method in ('common', 'ceil', 'floor'):
- raise FilterArgumentError('method must be common, ceil or floor')
- if method == 'common':
+ if not method in ("common", "ceil", "floor"):
+ raise FilterArgumentError("method must be common, ceil or floor")
+ if method == "common":
return round(value, precision)
func = getattr(math, method)
return func(value * (10 ** precision)) / (10 ** precision)
@@ -940,7 +944,7 @@ def do_round(value, precision=0, method='common'):
# we do not want to accidentally expose an auto generated repr in case
# people start to print this out in comments or something similar for
# debugging.
-_GroupTuple = namedtuple('_GroupTuple', ['grouper', 'list'])
+_GroupTuple = namedtuple("_GroupTuple", ["grouper", "list"])
_GroupTuple.__repr__ = tuple.__repr__
_GroupTuple.__str__ = tuple.__str__
@@ -981,8 +985,10 @@ def do_groupby(environment, value, attribute):
The attribute supports dot notation for nested access.
"""
expr = make_attrgetter(environment, attribute)
- return [_GroupTuple(key, list(values)) for key, values
- in groupby(sorted(value, key=expr), expr)]
+ return [
+ _GroupTuple(key, list(values))
+ for key, values in groupby(sorted(value, key=expr), expr)
+ ]
@environmentfilter
@@ -1039,7 +1045,7 @@ def do_reverse(value):
rv.reverse()
return rv
except TypeError:
- raise FilterArgumentError('argument must be iterable')
+ raise FilterArgumentError("argument must be iterable")
@environmentfilter
@@ -1060,8 +1066,9 @@ def do_attr(environment, obj, name):
except AttributeError:
pass
else:
- if environment.sandboxed and not \
- environment.is_safe_attribute(obj, name, value):
+ if environment.sandboxed and not environment.is_safe_attribute(
+ obj, name, value
+ ):
return environment.unsafe_undefined(obj, name)
return value
return environment.undefined(obj=obj, name=name)
@@ -1248,11 +1255,11 @@ def do_tojson(eval_ctx, value, indent=None):
.. versionadded:: 2.9
"""
policies = eval_ctx.environment.policies
- dumper = policies['json.dumps_function']
- options = policies['json.dumps_kwargs']
+ dumper = policies["json.dumps_function"]
+ options = policies["json.dumps_kwargs"]
if indent is not None:
options = dict(options)
- options['indent'] = indent
+ options["indent"] = indent
return htmlsafe_json_dumps(value, dumper=dumper, **options)
@@ -1261,21 +1268,23 @@ def prepare_map(args, kwargs):
seq = args[1]
default = None
- if len(args) == 2 and 'attribute' in kwargs:
- attribute = kwargs.pop('attribute')
- default = kwargs.pop('default', None)
+ if len(args) == 2 and "attribute" in kwargs:
+ attribute = kwargs.pop("attribute")
+ default = kwargs.pop("default", None)
if kwargs:
- raise FilterArgumentError('Unexpected keyword argument %r' %
- next(iter(kwargs)))
+ raise FilterArgumentError(
+ "Unexpected keyword argument %r" % next(iter(kwargs))
+ )
func = make_attrgetter(context.environment, attribute, default=default)
else:
try:
name = args[2]
args = args[3:]
except LookupError:
- raise FilterArgumentError('map requires a filter argument')
+ raise FilterArgumentError("map requires a filter argument")
func = lambda item: context.environment.call_filter(
- name, item, args, kwargs, context=context)
+ name, item, args, kwargs, context=context
+ )
return seq, func
@@ -1287,7 +1296,7 @@ def prepare_select_or_reject(args, kwargs, modfunc, lookup_attr):
try:
attr = args[2]
except LookupError:
- raise FilterArgumentError('Missing parameter for attribute name')
+ raise FilterArgumentError("Missing parameter for attribute name")
transfunc = make_attrgetter(context.environment, attr)
off = 1
else:
@@ -1296,9 +1305,8 @@ def prepare_select_or_reject(args, kwargs, modfunc, lookup_attr):
try:
name = args[2 + off]
- args = args[3 + off:]
- func = lambda item: context.environment.call_test(
- name, item, args, kwargs)
+ args = args[3 + off :]
+ func = lambda item: context.environment.call_test(name, item, args, kwargs)
except LookupError:
func = bool
@@ -1314,57 +1322,57 @@ def select_or_reject(args, kwargs, modfunc, lookup_attr):
FILTERS = {
- 'abs': abs,
- 'attr': do_attr,
- 'batch': do_batch,
- 'capitalize': do_capitalize,
- 'center': do_center,
- 'count': len,
- 'd': do_default,
- 'default': do_default,
- 'dictsort': do_dictsort,
- 'e': escape,
- 'escape': escape,
- 'filesizeformat': do_filesizeformat,
- 'first': do_first,
- 'float': do_float,
- 'forceescape': do_forceescape,
- 'format': do_format,
- 'groupby': do_groupby,
- 'indent': do_indent,
- 'int': do_int,
- 'join': do_join,
- 'last': do_last,
- 'length': len,
- 'list': do_list,
- 'lower': do_lower,
- 'map': do_map,
- 'min': do_min,
- 'max': do_max,
- 'pprint': do_pprint,
- 'random': do_random,
- 'reject': do_reject,
- 'rejectattr': do_rejectattr,
- 'replace': do_replace,
- 'reverse': do_reverse,
- 'round': do_round,
- 'safe': do_mark_safe,
- 'select': do_select,
- 'selectattr': do_selectattr,
- 'slice': do_slice,
- 'sort': do_sort,
- 'string': soft_unicode,
- 'striptags': do_striptags,
- 'sum': do_sum,
- 'title': do_title,
- 'trim': do_trim,
- 'truncate': do_truncate,
- 'unique': do_unique,
- 'upper': do_upper,
- 'urlencode': do_urlencode,
- 'urlize': do_urlize,
- 'wordcount': do_wordcount,
- 'wordwrap': do_wordwrap,
- 'xmlattr': do_xmlattr,
- 'tojson': do_tojson,
+ "abs": abs,
+ "attr": do_attr,
+ "batch": do_batch,
+ "capitalize": do_capitalize,
+ "center": do_center,
+ "count": len,
+ "d": do_default,
+ "default": do_default,
+ "dictsort": do_dictsort,
+ "e": escape,
+ "escape": escape,
+ "filesizeformat": do_filesizeformat,
+ "first": do_first,
+ "float": do_float,
+ "forceescape": do_forceescape,
+ "format": do_format,
+ "groupby": do_groupby,
+ "indent": do_indent,
+ "int": do_int,
+ "join": do_join,
+ "last": do_last,
+ "length": len,
+ "list": do_list,
+ "lower": do_lower,
+ "map": do_map,
+ "min": do_min,
+ "max": do_max,
+ "pprint": do_pprint,
+ "random": do_random,
+ "reject": do_reject,
+ "rejectattr": do_rejectattr,
+ "replace": do_replace,
+ "reverse": do_reverse,
+ "round": do_round,
+ "safe": do_mark_safe,
+ "select": do_select,
+ "selectattr": do_selectattr,
+ "slice": do_slice,
+ "sort": do_sort,
+ "string": soft_unicode,
+ "striptags": do_striptags,
+ "sum": do_sum,
+ "title": do_title,
+ "trim": do_trim,
+ "truncate": do_truncate,
+ "unique": do_unique,
+ "upper": do_upper,
+ "urlencode": do_urlencode,
+ "urlize": do_urlize,
+ "wordcount": do_wordcount,
+ "wordwrap": do_wordwrap,
+ "xmlattr": do_xmlattr,
+ "tojson": do_tojson,
}
diff --git a/src/jinja2/idtracking.py b/src/jinja2/idtracking.py
index 069844c..9a0d838 100644
--- a/src/jinja2/idtracking.py
+++ b/src/jinja2/idtracking.py
@@ -1,10 +1,10 @@
from ._compat import iteritems
from .visitor import NodeVisitor
-VAR_LOAD_PARAMETER = 'param'
-VAR_LOAD_RESOLVE = 'resolve'
-VAR_LOAD_ALIAS = 'alias'
-VAR_LOAD_UNDEFINED = 'undefined'
+VAR_LOAD_PARAMETER = "param"
+VAR_LOAD_RESOLVE = "resolve"
+VAR_LOAD_ALIAS = "alias"
+VAR_LOAD_UNDEFINED = "undefined"
def find_symbols(nodes, parent_symbols=None):
@@ -22,7 +22,6 @@ def symbols_for_node(node, parent_symbols=None):
class Symbols(object):
-
def __init__(self, parent=None, level=None):
if level is None:
if parent is None:
@@ -40,7 +39,7 @@ class Symbols(object):
visitor.visit(node, **kwargs)
def _define_ref(self, name, load=None):
- ident = 'l_%d_%s' % (self.level, name)
+ ident = "l_%d_%s" % (self.level, name)
self.refs[name] = ident
if load is not None:
self.loads[ident] = load
@@ -61,8 +60,10 @@ class Symbols(object):
def ref(self, name):
rv = self.find_ref(name)
if rv is None:
- raise AssertionError('Tried to resolve a name to a reference that '
- 'was unknown to the frame (%r)' % name)
+ raise AssertionError(
+ "Tried to resolve a name to a reference that "
+ "was unknown to the frame (%r)" % name
+ )
return rv
def copy(self):
@@ -117,7 +118,7 @@ class Symbols(object):
if branch_count == len(branch_symbols):
continue
target = self.find_ref(name)
- assert target is not None, 'should not happen'
+ assert target is not None, "should not happen"
if self.parent is not None:
outer_target = self.parent.find_ref(name)
@@ -148,7 +149,6 @@ class Symbols(object):
class RootVisitor(NodeVisitor):
-
def __init__(self, symbols):
self.sym_visitor = FrameSymbolVisitor(symbols)
@@ -156,35 +156,39 @@ class RootVisitor(NodeVisitor):
for child in node.iter_child_nodes():
self.sym_visitor.visit(child)
- visit_Template = visit_Block = visit_Macro = visit_FilterBlock = \
- visit_Scope = visit_If = visit_ScopedEvalContextModifier = \
- _simple_visit
+ visit_Template = (
+ visit_Block
+ ) = (
+ visit_Macro
+ ) = (
+ visit_FilterBlock
+ ) = visit_Scope = visit_If = visit_ScopedEvalContextModifier = _simple_visit
def visit_AssignBlock(self, node, **kwargs):
for child in node.body:
self.sym_visitor.visit(child)
def visit_CallBlock(self, node, **kwargs):
- for child in node.iter_child_nodes(exclude=('call',)):
+ for child in node.iter_child_nodes(exclude=("call",)):
self.sym_visitor.visit(child)
def visit_OverlayScope(self, node, **kwargs):
for child in node.body:
self.sym_visitor.visit(child)
- def visit_For(self, node, for_branch='body', **kwargs):
- if for_branch == 'body':
+ def visit_For(self, node, for_branch="body", **kwargs):
+ if for_branch == "body":
self.sym_visitor.visit(node.target, store_as_param=True)
branch = node.body
- elif for_branch == 'else':
+ elif for_branch == "else":
branch = node.else_
- elif for_branch == 'test':
+ elif for_branch == "test":
self.sym_visitor.visit(node.target, store_as_param=True)
if node.test is not None:
self.sym_visitor.visit(node.test)
return
else:
- raise RuntimeError('Unknown for branch')
+ raise RuntimeError("Unknown for branch")
for item in branch or ():
self.sym_visitor.visit(item)
@@ -195,8 +199,9 @@ class RootVisitor(NodeVisitor):
self.sym_visitor.visit(child)
def generic_visit(self, node, *args, **kwargs):
- raise NotImplementedError('Cannot find symbols for %r' %
- node.__class__.__name__)
+ raise NotImplementedError(
+ "Cannot find symbols for %r" % node.__class__.__name__
+ )
class FrameSymbolVisitor(NodeVisitor):
@@ -207,11 +212,11 @@ class FrameSymbolVisitor(NodeVisitor):
def visit_Name(self, node, store_as_param=False, **kwargs):
"""All assignments to names go through this function."""
- if store_as_param or node.ctx == 'param':
+ if store_as_param or node.ctx == "param":
self.symbols.declare_parameter(node.name)
- elif node.ctx == 'store':
+ elif node.ctx == "store":
self.symbols.store(node.name)
- elif node.ctx == 'load':
+ elif node.ctx == "load":
self.symbols.load(node.name)
def visit_NSRef(self, node, **kwargs):
diff --git a/src/jinja2/lexer.py b/src/jinja2/lexer.py
index cdd8427..1bac2d6 100644
--- a/src/jinja2/lexer.py
+++ b/src/jinja2/lexer.py
@@ -31,11 +31,12 @@ from .utils import LRUCache
_lexer_cache = LRUCache(50)
# static regular expressions
-whitespace_re = re.compile(r'\s+', re.U)
-newline_re = re.compile(r'(\r\n|\r|\n)')
-string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
- r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
-integer_re = re.compile(r'(\d+_)*\d+')
+whitespace_re = re.compile(r"\s+", re.U)
+newline_re = re.compile(r"(\r\n|\r|\n)")
+string_re = re.compile(
+ r"('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S
+)
+integer_re = re.compile(r"(\d+_)*\d+")
float_re = re.compile(
r"""
(?<!\.) # doesn't start with a .
@@ -52,134 +53,146 @@ float_re = re.compile(
try:
# check if this Python supports Unicode identifiers
- compile('föö', '<unknown>', 'eval')
+ compile("föö", "<unknown>", "eval")
except SyntaxError:
# no Unicode support, use ASCII identifiers
- name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
+ name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")
check_ident = False
else:
# Unicode support, build a pattern to match valid characters, and set flag
# to use str.isidentifier to validate during lexing
from jinja2 import _identifier
- name_re = re.compile(r'[\w{0}]+'.format(_identifier.pattern))
+
+ name_re = re.compile(r"[\w{0}]+".format(_identifier.pattern))
check_ident = True
# remove the pattern from memory after building the regex
import sys
- del sys.modules['jinja2._identifier']
+
+ del sys.modules["jinja2._identifier"]
import jinja2
+
del jinja2._identifier
del _identifier
# internal the tokens and keep references to them
-TOKEN_ADD = intern('add')
-TOKEN_ASSIGN = intern('assign')
-TOKEN_COLON = intern('colon')
-TOKEN_COMMA = intern('comma')
-TOKEN_DIV = intern('div')
-TOKEN_DOT = intern('dot')
-TOKEN_EQ = intern('eq')
-TOKEN_FLOORDIV = intern('floordiv')
-TOKEN_GT = intern('gt')
-TOKEN_GTEQ = intern('gteq')
-TOKEN_LBRACE = intern('lbrace')
-TOKEN_LBRACKET = intern('lbracket')
-TOKEN_LPAREN = intern('lparen')
-TOKEN_LT = intern('lt')
-TOKEN_LTEQ = intern('lteq')
-TOKEN_MOD = intern('mod')
-TOKEN_MUL = intern('mul')
-TOKEN_NE = intern('ne')
-TOKEN_PIPE = intern('pipe')
-TOKEN_POW = intern('pow')
-TOKEN_RBRACE = intern('rbrace')
-TOKEN_RBRACKET = intern('rbracket')
-TOKEN_RPAREN = intern('rparen')
-TOKEN_SEMICOLON = intern('semicolon')
-TOKEN_SUB = intern('sub')
-TOKEN_TILDE = intern('tilde')
-TOKEN_WHITESPACE = intern('whitespace')
-TOKEN_FLOAT = intern('float')
-TOKEN_INTEGER = intern('integer')
-TOKEN_NAME = intern('name')
-TOKEN_STRING = intern('string')
-TOKEN_OPERATOR = intern('operator')
-TOKEN_BLOCK_BEGIN = intern('block_begin')
-TOKEN_BLOCK_END = intern('block_end')
-TOKEN_VARIABLE_BEGIN = intern('variable_begin')
-TOKEN_VARIABLE_END = intern('variable_end')
-TOKEN_RAW_BEGIN = intern('raw_begin')
-TOKEN_RAW_END = intern('raw_end')
-TOKEN_COMMENT_BEGIN = intern('comment_begin')
-TOKEN_COMMENT_END = intern('comment_end')
-TOKEN_COMMENT = intern('comment')
-TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin')
-TOKEN_LINESTATEMENT_END = intern('linestatement_end')
-TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin')
-TOKEN_LINECOMMENT_END = intern('linecomment_end')
-TOKEN_LINECOMMENT = intern('linecomment')
-TOKEN_DATA = intern('data')
-TOKEN_INITIAL = intern('initial')
-TOKEN_EOF = intern('eof')
+TOKEN_ADD = intern("add")
+TOKEN_ASSIGN = intern("assign")
+TOKEN_COLON = intern("colon")
+TOKEN_COMMA = intern("comma")
+TOKEN_DIV = intern("div")
+TOKEN_DOT = intern("dot")
+TOKEN_EQ = intern("eq")
+TOKEN_FLOORDIV = intern("floordiv")
+TOKEN_GT = intern("gt")
+TOKEN_GTEQ = intern("gteq")
+TOKEN_LBRACE = intern("lbrace")
+TOKEN_LBRACKET = intern("lbracket")
+TOKEN_LPAREN = intern("lparen")
+TOKEN_LT = intern("lt")
+TOKEN_LTEQ = intern("lteq")
+TOKEN_MOD = intern("mod")
+TOKEN_MUL = intern("mul")
+TOKEN_NE = intern("ne")
+TOKEN_PIPE = intern("pipe")
+TOKEN_POW = intern("pow")
+TOKEN_RBRACE = intern("rbrace")
+TOKEN_RBRACKET = intern("rbracket")
+TOKEN_RPAREN = intern("rparen")
+TOKEN_SEMICOLON = intern("semicolon")
+TOKEN_SUB = intern("sub")
+TOKEN_TILDE = intern("tilde")
+TOKEN_WHITESPACE = intern("whitespace")
+TOKEN_FLOAT = intern("float")
+TOKEN_INTEGER = intern("integer")
+TOKEN_NAME = intern("name")
+TOKEN_STRING = intern("string")
+TOKEN_OPERATOR = intern("operator")
+TOKEN_BLOCK_BEGIN = intern("block_begin")
+TOKEN_BLOCK_END = intern("block_end")
+TOKEN_VARIABLE_BEGIN = intern("variable_begin")
+TOKEN_VARIABLE_END = intern("variable_end")
+TOKEN_RAW_BEGIN = intern("raw_begin")
+TOKEN_RAW_END = intern("raw_end")
+TOKEN_COMMENT_BEGIN = intern("comment_begin")
+TOKEN_COMMENT_END = intern("comment_end")
+TOKEN_COMMENT = intern("comment")
+TOKEN_LINESTATEMENT_BEGIN = intern("linestatement_begin")
+TOKEN_LINESTATEMENT_END = intern("linestatement_end")
+TOKEN_LINECOMMENT_BEGIN = intern("linecomment_begin")
+TOKEN_LINECOMMENT_END = intern("linecomment_end")
+TOKEN_LINECOMMENT = intern("linecomment")
+TOKEN_DATA = intern("data")
+TOKEN_INITIAL = intern("initial")
+TOKEN_EOF = intern("eof")
# bind operators to token types
operators = {
- '+': TOKEN_ADD,
- '-': TOKEN_SUB,
- '/': TOKEN_DIV,
- '//': TOKEN_FLOORDIV,
- '*': TOKEN_MUL,
- '%': TOKEN_MOD,
- '**': TOKEN_POW,
- '~': TOKEN_TILDE,
- '[': TOKEN_LBRACKET,
- ']': TOKEN_RBRACKET,
- '(': TOKEN_LPAREN,
- ')': TOKEN_RPAREN,
- '{': TOKEN_LBRACE,
- '}': TOKEN_RBRACE,
- '==': TOKEN_EQ,
- '!=': TOKEN_NE,
- '>': TOKEN_GT,
- '>=': TOKEN_GTEQ,
- '<': TOKEN_LT,
- '<=': TOKEN_LTEQ,
- '=': TOKEN_ASSIGN,
- '.': TOKEN_DOT,
- ':': TOKEN_COLON,
- '|': TOKEN_PIPE,
- ',': TOKEN_COMMA,
- ';': TOKEN_SEMICOLON
+ "+": TOKEN_ADD,
+ "-": TOKEN_SUB,
+ "/": TOKEN_DIV,
+ "//": TOKEN_FLOORDIV,
+ "*": TOKEN_MUL,
+ "%": TOKEN_MOD,
+ "**": TOKEN_POW,
+ "~": TOKEN_TILDE,
+ "[": TOKEN_LBRACKET,
+ "]": TOKEN_RBRACKET,
+ "(": TOKEN_LPAREN,
+ ")": TOKEN_RPAREN,
+ "{": TOKEN_LBRACE,
+ "}": TOKEN_RBRACE,
+ "==": TOKEN_EQ,
+ "!=": TOKEN_NE,
+ ">": TOKEN_GT,
+ ">=": TOKEN_GTEQ,
+ "<": TOKEN_LT,
+ "<=": TOKEN_LTEQ,
+ "=": TOKEN_ASSIGN,
+ ".": TOKEN_DOT,
+ ":": TOKEN_COLON,
+ "|": TOKEN_PIPE,
+ ",": TOKEN_COMMA,
+ ";": TOKEN_SEMICOLON,
}
reverse_operators = dict([(v, k) for k, v in iteritems(operators)])
-assert len(operators) == len(reverse_operators), 'operators dropped'
-operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
- sorted(operators, key=lambda x: -len(x))))
+assert len(operators) == len(reverse_operators), "operators dropped"
+operator_re = re.compile(
+ "(%s)" % "|".join(re.escape(x) for x in sorted(operators, key=lambda x: -len(x)))
+)
-ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT,
- TOKEN_COMMENT_END, TOKEN_WHITESPACE,
- TOKEN_LINECOMMENT_BEGIN, TOKEN_LINECOMMENT_END,
- TOKEN_LINECOMMENT])
-ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA,
- TOKEN_COMMENT, TOKEN_LINECOMMENT])
+ignored_tokens = frozenset(
+ [
+ TOKEN_COMMENT_BEGIN,
+ TOKEN_COMMENT,
+ TOKEN_COMMENT_END,
+ TOKEN_WHITESPACE,
+ TOKEN_LINECOMMENT_BEGIN,
+ TOKEN_LINECOMMENT_END,
+ TOKEN_LINECOMMENT,
+ ]
+)
+ignore_if_empty = frozenset(
+ [TOKEN_WHITESPACE, TOKEN_DATA, TOKEN_COMMENT, TOKEN_LINECOMMENT]
+)
def _describe_token_type(token_type):
if token_type in reverse_operators:
return reverse_operators[token_type]
return {
- TOKEN_COMMENT_BEGIN: 'begin of comment',
- TOKEN_COMMENT_END: 'end of comment',
- TOKEN_COMMENT: 'comment',
- TOKEN_LINECOMMENT: 'comment',
- TOKEN_BLOCK_BEGIN: 'begin of statement block',
- TOKEN_BLOCK_END: 'end of statement block',
- TOKEN_VARIABLE_BEGIN: 'begin of print statement',
- TOKEN_VARIABLE_END: 'end of print statement',
- TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement',
- TOKEN_LINESTATEMENT_END: 'end of line statement',
- TOKEN_DATA: 'template data / text',
- TOKEN_EOF: 'end of template'
+ TOKEN_COMMENT_BEGIN: "begin of comment",
+ TOKEN_COMMENT_END: "end of comment",
+ TOKEN_COMMENT: "comment",
+ TOKEN_LINECOMMENT: "comment",
+ TOKEN_BLOCK_BEGIN: "begin of statement block",
+ TOKEN_BLOCK_END: "end of statement block",
+ TOKEN_VARIABLE_BEGIN: "begin of print statement",
+ TOKEN_VARIABLE_END: "end of print statement",
+ TOKEN_LINESTATEMENT_BEGIN: "begin of line statement",
+ TOKEN_LINESTATEMENT_END: "end of line statement",
+ TOKEN_DATA: "template data / text",
+ TOKEN_EOF: "end of template",
}.get(token_type, token_type)
@@ -192,8 +205,8 @@ def describe_token(token):
def describe_token_expr(expr):
"""Like `describe_token` but for token expressions."""
- if ':' in expr:
- type, value = expr.split(':', 1)
+ if ":" in expr:
+ type, value = expr.split(":", 1)
if type == TOKEN_NAME:
return value
else:
@@ -212,21 +225,39 @@ def compile_rules(environment):
"""Compiles all the rules from the environment into a list of rules."""
e = re.escape
rules = [
- (len(environment.comment_start_string), TOKEN_COMMENT_BEGIN,
- e(environment.comment_start_string)),
- (len(environment.block_start_string), TOKEN_BLOCK_BEGIN,
- e(environment.block_start_string)),
- (len(environment.variable_start_string), TOKEN_VARIABLE_BEGIN,
- e(environment.variable_start_string))
+ (
+ len(environment.comment_start_string),
+ TOKEN_COMMENT_BEGIN,
+ e(environment.comment_start_string),
+ ),
+ (
+ len(environment.block_start_string),
+ TOKEN_BLOCK_BEGIN,
+ e(environment.block_start_string),
+ ),
+ (
+ len(environment.variable_start_string),
+ TOKEN_VARIABLE_BEGIN,
+ e(environment.variable_start_string),
+ ),
]
if environment.line_statement_prefix is not None:
- rules.append((len(environment.line_statement_prefix), TOKEN_LINESTATEMENT_BEGIN,
- r'^[ \t\v]*' + e(environment.line_statement_prefix)))
+ rules.append(
+ (
+ len(environment.line_statement_prefix),
+ TOKEN_LINESTATEMENT_BEGIN,
+ r"^[ \t\v]*" + e(environment.line_statement_prefix),
+ )
+ )
if environment.line_comment_prefix is not None:
- rules.append((len(environment.line_comment_prefix), TOKEN_LINECOMMENT_BEGIN,
- r'(?:^|(?<=\S))[^\S\r\n]*' +
- e(environment.line_comment_prefix)))
+ rules.append(
+ (
+ len(environment.line_comment_prefix),
+ TOKEN_LINECOMMENT_BEGIN,
+ r"(?:^|(?<=\S))[^\S\r\n]*" + e(environment.line_comment_prefix),
+ )
+ )
return [x[1:] for x in sorted(rules, reverse=True)]
@@ -246,6 +277,7 @@ class Failure(object):
class Token(tuple):
"""Token class."""
+
__slots__ = ()
lineno, type, value = (property(itemgetter(x)) for x in range(3))
@@ -255,7 +287,7 @@ class Token(tuple):
def __str__(self):
if self.type in reverse_operators:
return reverse_operators[self.type]
- elif self.type == 'name':
+ elif self.type == "name":
return self.value
return self.type
@@ -268,8 +300,8 @@ class Token(tuple):
# passed an iterable of not interned strings.
if self.type == expr:
return True
- elif ':' in expr:
- return expr.split(':', 1) == [self.type, self.value]
+ elif ":" in expr:
+ return expr.split(":", 1) == [self.type, self.value]
return False
def test_any(self, *iterable):
@@ -280,11 +312,7 @@ class Token(tuple):
return False
def __repr__(self):
- return 'Token(%r, %r, %r)' % (
- self.lineno,
- self.type,
- self.value
- )
+ return "Token(%r, %r, %r)" % (self.lineno, self.type, self.value)
@implements_iterator
@@ -321,7 +349,7 @@ class TokenStream(object):
self.name = name
self.filename = filename
self.closed = False
- self.current = Token(1, TOKEN_INITIAL, '')
+ self.current = Token(1, TOKEN_INITIAL, "")
next(self)
def __iter__(self):
@@ -329,6 +357,7 @@ class TokenStream(object):
def __bool__(self):
return bool(self._pushed) or self.current.type is not TOKEN_EOF
+
__nonzero__ = __bool__ # py2
@property
@@ -381,7 +410,7 @@ class TokenStream(object):
def close(self):
"""Close the stream."""
- self.current = Token(self.current.lineno, TOKEN_EOF, '')
+ self.current = Token(self.current.lineno, TOKEN_EOF, "")
self._iter = None
self.closed = True
@@ -392,14 +421,18 @@ class TokenStream(object):
if not self.current.test(expr):
expr = describe_token_expr(expr)
if self.current.type is TOKEN_EOF:
- raise TemplateSyntaxError('unexpected end of template, '
- 'expected %r.' % expr,
- self.current.lineno,
- self.name, self.filename)
- raise TemplateSyntaxError("expected token %r, got %r" %
- (expr, describe_token(self.current)),
- self.current.lineno,
- self.name, self.filename)
+ raise TemplateSyntaxError(
+ "unexpected end of template, expected %r." % expr,
+ self.current.lineno,
+ self.name,
+ self.filename,
+ )
+ raise TemplateSyntaxError(
+ "expected token %r, got %r" % (expr, describe_token(self.current)),
+ self.current.lineno,
+ self.name,
+ self.filename,
+ )
try:
return self.current
finally:
@@ -408,18 +441,20 @@ class TokenStream(object):
def get_lexer(environment):
"""Return a lexer which is probably cached."""
- key = (environment.block_start_string,
- environment.block_end_string,
- environment.variable_start_string,
- environment.variable_end_string,
- environment.comment_start_string,
- environment.comment_end_string,
- environment.line_statement_prefix,
- environment.line_comment_prefix,
- environment.trim_blocks,
- environment.lstrip_blocks,
- environment.newline_sequence,
- environment.keep_trailing_newline)
+ key = (
+ environment.block_start_string,
+ environment.block_end_string,
+ environment.variable_start_string,
+ environment.variable_end_string,
+ environment.comment_start_string,
+ environment.comment_end_string,
+ environment.line_statement_prefix,
+ environment.line_comment_prefix,
+ environment.trim_blocks,
+ environment.lstrip_blocks,
+ environment.newline_sequence,
+ environment.keep_trailing_newline,
+ )
lexer = _lexer_cache.get(key)
if lexer is None:
lexer = Lexer(environment)
@@ -460,7 +495,7 @@ class Lexer(object):
(integer_re, TOKEN_INTEGER, None),
(name_re, TOKEN_NAME, None),
(string_re, TOKEN_STRING, None),
- (operator_re, TOKEN_OPERATOR, None)
+ (operator_re, TOKEN_OPERATOR, None),
]
# assemble the root lexing rule. because "|" is ungreedy
@@ -472,7 +507,7 @@ class Lexer(object):
root_tag_rules = compile_rules(environment)
# block suffix if trimming is enabled
- block_suffix_re = environment.trim_blocks and '\\n?' or ''
+ block_suffix_re = environment.trim_blocks and "\\n?" or ""
# If lstrip is enabled, it should not be applied if there is any
# non-whitespace between the newline and block.
@@ -483,63 +518,109 @@ class Lexer(object):
# global lexing rules
self.rules = {
- 'root': [
+ "root": [
# directives
- (c('(.*?)(?:%s)' % '|'.join(
- [r'(?P<raw_begin>%s(\-|\+|)\s*raw\s*(?:\-%s\s*|%s))' % (
- e(environment.block_start_string),
- e(environment.block_end_string),
- e(environment.block_end_string)
- )] + [
- r'(?P<%s>%s(\-|\+|))' % (n, r)
- for n, r in root_tag_rules
- ])), OptionalLStrip(TOKEN_DATA, '#bygroup'), '#bygroup'),
+ (
+ c(
+ "(.*?)(?:%s)"
+ % "|".join(
+ [
+ r"(?P<raw_begin>%s(\-|\+|)\s*raw\s*(?:\-%s\s*|%s))"
+ % (
+ e(environment.block_start_string),
+ e(environment.block_end_string),
+ e(environment.block_end_string),
+ )
+ ]
+ + [
+ r"(?P<%s>%s(\-|\+|))" % (n, r)
+ for n, r in root_tag_rules
+ ]
+ )
+ ),
+ OptionalLStrip(TOKEN_DATA, "#bygroup"),
+ "#bygroup",
+ ),
# data
- (c('.+'), TOKEN_DATA, None)
+ (c(".+"), TOKEN_DATA, None),
],
# comments
TOKEN_COMMENT_BEGIN: [
- (c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
- e(environment.comment_end_string),
- e(environment.comment_end_string),
- block_suffix_re
- )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'),
- (c('(.)'), (Failure('Missing end of comment tag'),), None)
+ (
+ c(
+ r"(.*?)((?:\-%s\s*|%s)%s)"
+ % (
+ e(environment.comment_end_string),
+ e(environment.comment_end_string),
+ block_suffix_re,
+ )
+ ),
+ (TOKEN_COMMENT, TOKEN_COMMENT_END),
+ "#pop",
+ ),
+ (c("(.)"), (Failure("Missing end of comment tag"),), None),
],
# blocks
TOKEN_BLOCK_BEGIN: [
- (c(r'(?:\-%s\s*|%s)%s' % (
- e(environment.block_end_string),
- e(environment.block_end_string),
- block_suffix_re
- )), TOKEN_BLOCK_END, '#pop'),
- ] + tag_rules,
+ (
+ c(
+ r"(?:\-%s\s*|%s)%s"
+ % (
+ e(environment.block_end_string),
+ e(environment.block_end_string),
+ block_suffix_re,
+ )
+ ),
+ TOKEN_BLOCK_END,
+ "#pop",
+ ),
+ ]
+ + tag_rules,
# variables
TOKEN_VARIABLE_BEGIN: [
- (c(r'\-%s\s*|%s' % (
- e(environment.variable_end_string),
- e(environment.variable_end_string)
- )), TOKEN_VARIABLE_END, '#pop')
- ] + tag_rules,
+ (
+ c(
+ r"\-%s\s*|%s"
+ % (
+ e(environment.variable_end_string),
+ e(environment.variable_end_string),
+ )
+ ),
+ TOKEN_VARIABLE_END,
+ "#pop",
+ )
+ ]
+ + tag_rules,
# raw block
TOKEN_RAW_BEGIN: [
- (c(r'(.*?)((?:%s(\-|\+|))\s*endraw\s*(?:\-%s\s*|%s%s))' % (
- e(environment.block_start_string),
- e(environment.block_end_string),
- e(environment.block_end_string),
- block_suffix_re
- )), OptionalLStrip(TOKEN_DATA, TOKEN_RAW_END), '#pop'),
- (c('(.)'), (Failure('Missing end of raw directive'),), None)
+ (
+ c(
+ r"(.*?)((?:%s(\-|\+|))\s*endraw\s*(?:\-%s\s*|%s%s))"
+ % (
+ e(environment.block_start_string),
+ e(environment.block_end_string),
+ e(environment.block_end_string),
+ block_suffix_re,
+ )
+ ),
+ OptionalLStrip(TOKEN_DATA, TOKEN_RAW_END),
+ "#pop",
+ ),
+ (c("(.)"), (Failure("Missing end of raw directive"),), None),
],
# line statements
TOKEN_LINESTATEMENT_BEGIN: [
- (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop')
- ] + tag_rules,
+ (c(r"\s*(\n|$)"), TOKEN_LINESTATEMENT_END, "#pop")
+ ]
+ + tag_rules,
# line comments
TOKEN_LINECOMMENT_BEGIN: [
- (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT,
- TOKEN_LINECOMMENT_END), '#pop')
- ]
+ (
+ c(r"(.*?)()(?=\n|$)"),
+ (TOKEN_LINECOMMENT, TOKEN_LINECOMMENT_END),
+ "#pop",
+ )
+ ],
}
def _normalize_newlines(self, value):
@@ -547,8 +628,7 @@ class Lexer(object):
return newline_re.sub(self.newline_sequence, value)
def tokenize(self, source, name=None, filename=None, state=None):
- """Calls tokeniter + tokenize and wraps it in a token stream.
- """
+ """Calls tokeniter + tokenize and wraps it in a token stream."""
stream = self.tokeniter(source, name, filename, state)
return TokenStream(self.wrap(stream, name, filename), name, filename)
@@ -568,22 +648,24 @@ class Lexer(object):
continue
elif token == TOKEN_DATA:
value = self._normalize_newlines(value)
- elif token == 'keyword':
+ elif token == "keyword":
token = value
elif token == TOKEN_NAME:
value = str(value)
if check_ident and not value.isidentifier():
raise TemplateSyntaxError(
- 'Invalid character in identifier',
- lineno, name, filename)
+ "Invalid character in identifier", lineno, name, filename
+ )
elif token == TOKEN_STRING:
# try to unescape string
try:
- value = self._normalize_newlines(value[1:-1]) \
- .encode('ascii', 'backslashreplace') \
- .decode('unicode-escape')
+ value = (
+ self._normalize_newlines(value[1:-1])
+ .encode("ascii", "backslashreplace")
+ .decode("unicode-escape")
+ )
except Exception as e:
- msg = str(e).split(':')[-1].strip()
+ msg = str(e).split(":")[-1].strip()
raise TemplateSyntaxError(msg, lineno, name, filename)
elif token == TOKEN_INTEGER:
value = int(value.replace("_", ""))
@@ -601,17 +683,17 @@ class Lexer(object):
source = text_type(source)
lines = source.splitlines()
if self.keep_trailing_newline and source:
- for newline in ('\r\n', '\r', '\n'):
+ for newline in ("\r\n", "\r", "\n"):
if source.endswith(newline):
- lines.append('')
+ lines.append("")
break
- source = '\n'.join(lines)
+ source = "\n".join(lines)
pos = 0
lineno = 1
- stack = ['root']
- if state is not None and state != 'root':
- assert state in ('variable', 'block'), 'invalid state'
- stack.append(state + '_begin')
+ stack = ["root"]
+ if state is not None and state != "root":
+ assert state in ("variable", "block"), "invalid state"
+ stack.append(state + "_begin")
statetokens = self.rules[stack[-1]]
source_length = len(source)
balancing_stack = []
@@ -629,11 +711,10 @@ class Lexer(object):
# are balanced. continue parsing with the lower rule which
# is the operator rule. do this only if the end tags look
# like operators
- if (
- balancing_stack
- and tokens in (
- TOKEN_VARIABLE_END, TOKEN_BLOCK_END, TOKEN_LINESTATEMENT_END
- )
+ if balancing_stack and tokens in (
+ TOKEN_VARIABLE_END,
+ TOKEN_BLOCK_END,
+ TOKEN_LINESTATEMENT_END,
):
continue
@@ -663,7 +744,7 @@ class Lexer(object):
and not m.groupdict().get(TOKEN_VARIABLE_BEGIN)
):
# The start of text between the last newline and the tag.
- l_pos = text.rfind('\n') + 1
+ l_pos = text.rfind("\n") + 1
# If there's only whitespace between the newline and the
# tag, strip it.
@@ -677,51 +758,54 @@ class Lexer(object):
# bygroup is a bit more complex, in that case we
# yield for the current token the first named
# group that matched
- elif token == '#bygroup':
+ elif token == "#bygroup":
for key, value in iteritems(m.groupdict()):
if value is not None:
yield lineno, key, value
- lineno += value.count('\n')
+ lineno += value.count("\n")
break
else:
- raise RuntimeError('%r wanted to resolve '
- 'the token dynamically'
- ' but no group matched'
- % regex)
+ raise RuntimeError(
+ "%r wanted to resolve "
+ "the token dynamically"
+ " but no group matched" % regex
+ )
# normal group
else:
data = groups[idx]
if data or token not in ignore_if_empty:
yield lineno, token, data
- lineno += data.count('\n')
+ lineno += data.count("\n")
# strings as token just are yielded as it.
else:
data = m.group()
# update brace/parentheses balance
if tokens == TOKEN_OPERATOR:
- if data == '{':
- balancing_stack.append('}')
- elif data == '(':
- balancing_stack.append(')')
- elif data == '[':
- balancing_stack.append(']')
- elif data in ('}', ')', ']'):
+ if data == "{":
+ balancing_stack.append("}")
+ elif data == "(":
+ balancing_stack.append(")")
+ elif data == "[":
+ balancing_stack.append("]")
+ elif data in ("}", ")", "]"):
if not balancing_stack:
- raise TemplateSyntaxError('unexpected \'%s\'' %
- data, lineno, name,
- filename)
+ raise TemplateSyntaxError(
+ "unexpected '%s'" % data, lineno, name, filename
+ )
expected_op = balancing_stack.pop()
if expected_op != data:
- raise TemplateSyntaxError('unexpected \'%s\', '
- 'expected \'%s\'' %
- (data, expected_op),
- lineno, name,
- filename)
+ raise TemplateSyntaxError(
+ "unexpected '%s', "
+ "expected '%s'" % (data, expected_op),
+ lineno,
+ name,
+ filename,
+ )
# yield items
if data or tokens not in ignore_if_empty:
yield lineno, tokens, data
- lineno += data.count('\n')
+ lineno += data.count("\n")
# fetch new position into new variable so that we can check
# if there is a internal parsing error which would result
@@ -731,19 +815,20 @@ class Lexer(object):
# handle state changes
if new_state is not None:
# remove the uppermost state
- if new_state == '#pop':
+ if new_state == "#pop":
stack.pop()
# resolve the new state by group checking
- elif new_state == '#bygroup':
+ elif new_state == "#bygroup":
for key, value in iteritems(m.groupdict()):
if value is not None:
stack.append(key)
break
else:
- raise RuntimeError('%r wanted to resolve the '
- 'new state dynamically but'
- ' no group matched' %
- regex)
+ raise RuntimeError(
+ "%r wanted to resolve the "
+ "new state dynamically but"
+ " no group matched" % regex
+ )
# direct state name given
else:
stack.append(new_state)
@@ -752,8 +837,9 @@ class Lexer(object):
# this means a loop without break condition, avoid that and
# raise error
elif pos2 == pos:
- raise RuntimeError('%r yielded empty string without '
- 'stack change' % regex)
+ raise RuntimeError(
+ "%r yielded empty string without stack change" % regex
+ )
# publish new function and start again
pos = pos2
break
@@ -764,6 +850,9 @@ class Lexer(object):
if pos >= source_length:
return
# something went wrong
- raise TemplateSyntaxError('unexpected char %r at %d' %
- (source[pos], pos), lineno,
- name, filename)
+ raise TemplateSyntaxError(
+ "unexpected char %r at %d" % (source[pos], pos),
+ lineno,
+ name,
+ filename,
+ )
diff --git a/src/jinja2/loaders.py b/src/jinja2/loaders.py
index 86049f7..fa91a73 100644
--- a/src/jinja2/loaders.py
+++ b/src/jinja2/loaders.py
@@ -31,12 +31,14 @@ def split_template_path(template):
'..' in the path it will raise a `TemplateNotFound` error.
"""
pieces = []
- for piece in template.split('/'):
- if path.sep in piece \
- or (path.altsep and path.altsep in piece) or \
- piece == path.pardir:
+ for piece in template.split("/"):
+ if (
+ path.sep in piece
+ or (path.altsep and path.altsep in piece)
+ or piece == path.pardir
+ ):
raise TemplateNotFound(template)
- elif piece and piece != '.':
+ elif piece and piece != ".":
pieces.append(piece)
return pieces
@@ -93,15 +95,16 @@ class BaseLoader(object):
the template will be reloaded.
"""
if not self.has_source_access:
- raise RuntimeError('%s cannot provide access to the source' %
- self.__class__.__name__)
+ raise RuntimeError(
+ "%s cannot provide access to the source" % self.__class__.__name__
+ )
raise TemplateNotFound(template)
def list_templates(self):
"""Iterates over all templates. If the loader does not support that
it should raise a :exc:`TypeError` which is the default behavior.
"""
- raise TypeError('this loader cannot iterate over all templates')
+ raise TypeError("this loader cannot iterate over all templates")
@internalcode
def load(self, environment, name, globals=None):
@@ -138,8 +141,9 @@ class BaseLoader(object):
bucket.code = code
bcc.set_bucket(bucket)
- return environment.template_class.from_code(environment, code,
- globals, uptodate)
+ return environment.template_class.from_code(
+ environment, code, globals, uptodate
+ )
class FileSystemLoader(BaseLoader):
@@ -164,10 +168,9 @@ class FileSystemLoader(BaseLoader):
The ``followlinks`` parameter was added.
"""
- def __init__(self, searchpath, encoding='utf-8', followlinks=False):
- if (
- not isinstance(searchpath, abc.Iterable)
- or isinstance(searchpath, string_types)
+ def __init__(self, searchpath, encoding="utf-8", followlinks=False):
+ if not isinstance(searchpath, abc.Iterable) or isinstance(
+ searchpath, string_types
):
searchpath = [searchpath]
@@ -197,6 +200,7 @@ class FileSystemLoader(BaseLoader):
return path.getmtime(filename) == mtime
except OSError:
return False
+
return contents, filename, uptodate
raise TemplateNotFound(template)
@@ -206,10 +210,12 @@ class FileSystemLoader(BaseLoader):
walk_dir = os.walk(searchpath, followlinks=self.followlinks)
for dirpath, dirnames, filenames in walk_dir:
for filename in filenames:
- template = os.path.join(dirpath, filename) \
- [len(searchpath):].strip(os.path.sep) \
- .replace(os.path.sep, '/')
- if template[:2] == './':
+ template = (
+ os.path.join(dirpath, filename)[len(searchpath) :]
+ .strip(os.path.sep)
+ .replace(os.path.sep, "/")
+ )
+ if template[:2] == "./":
template = template[2:]
if template not in found:
found.add(template)
@@ -305,6 +311,7 @@ class PackageLoader(BaseLoader):
def up_to_date():
return os.path.isfile(p) and os.path.getmtime(p) == mtime
+
else:
# Package is a zip file.
try:
@@ -341,7 +348,7 @@ class PackageLoader(BaseLoader):
# Package is a zip file.
prefix = (
- self._template_root[len(self._archive):].lstrip(os.path.sep)
+ self._template_root[len(self._archive) :].lstrip(os.path.sep)
+ os.path.sep
)
offset = len(prefix)
@@ -422,7 +429,7 @@ class PrefixLoader(BaseLoader):
by loading ``'app2/index.html'`` the file from the second.
"""
- def __init__(self, mapping, delimiter='/'):
+ def __init__(self, mapping, delimiter="/"):
self.mapping = mapping
self.delimiter = delimiter
@@ -522,22 +529,20 @@ class ModuleLoader(BaseLoader):
has_source_access = False
def __init__(self, path):
- package_name = '_jinja2_module_templates_%x' % id(self)
+ package_name = "_jinja2_module_templates_%x" % id(self)
# create a fake module that looks for the templates in the
# path given.
mod = _TemplateModule(package_name)
- if (
- not isinstance(path, abc.Iterable)
- or isinstance(path, string_types)
- ):
+ if not isinstance(path, abc.Iterable) or isinstance(path, string_types):
path = [path]
mod.__path__ = [fspath(p) for p in path]
- sys.modules[package_name] = weakref.proxy(mod,
- lambda x: sys.modules.pop(package_name, None))
+ sys.modules[package_name] = weakref.proxy(
+ mod, lambda x: sys.modules.pop(package_name, None)
+ )
# the only strong reference, the sys.modules entry is weak
# so that the garbage collector can remove it once the
@@ -547,20 +552,20 @@ class ModuleLoader(BaseLoader):
@staticmethod
def get_template_key(name):
- return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest()
+ return "tmpl_" + sha1(name.encode("utf-8")).hexdigest()
@staticmethod
def get_module_filename(name):
- return ModuleLoader.get_template_key(name) + '.py'
+ return ModuleLoader.get_template_key(name) + ".py"
@internalcode
def load(self, environment, name, globals=None):
key = self.get_template_key(name)
- module = '%s.%s' % (self.package_name, key)
+ module = "%s.%s" % (self.package_name, key)
mod = getattr(self.module, module, None)
if mod is None:
try:
- mod = __import__(module, None, None, ['root'])
+ mod = __import__(module, None, None, ["root"])
except ImportError:
raise TemplateNotFound(name)
@@ -569,4 +574,5 @@ class ModuleLoader(BaseLoader):
sys.modules.pop(module, None)
return environment.template_class.from_module_dict(
- environment, mod.__dict__, globals)
+ environment, mod.__dict__, globals
+ )
diff --git a/src/jinja2/meta.py b/src/jinja2/meta.py
index 6a4208e..3c7917e 100644
--- a/src/jinja2/meta.py
+++ b/src/jinja2/meta.py
@@ -19,8 +19,7 @@ class TrackingCodeGenerator(CodeGenerator):
"""We abuse the code generator for introspection."""
def __init__(self, environment):
- CodeGenerator.__init__(self, environment, '<introspection>',
- '<introspection>')
+ CodeGenerator.__init__(self, environment, "<introspection>", "<introspection>")
self.undeclared_identifiers = set()
def write(self, x):
@@ -30,7 +29,7 @@ class TrackingCodeGenerator(CodeGenerator):
"""Remember all undeclared identifiers."""
CodeGenerator.enter_frame(self, frame)
for _, (action, param) in iteritems(frame.symbols.loads):
- if action == 'resolve' and param not in self.environment.globals:
+ if action == "resolve" and param not in self.environment.globals:
self.undeclared_identifiers.add(param)
@@ -73,8 +72,9 @@ def find_referenced_templates(ast):
This function is useful for dependency tracking. For example if you want
to rebuild parts of the website after a layout template has changed.
"""
- for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import,
- nodes.Include)):
+ for node in ast.find_all(
+ (nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include)
+ ):
if not isinstance(node.template, nodes.Const):
# a tuple with some non consts in there
if isinstance(node.template, (nodes.Tuple, nodes.List)):
@@ -97,8 +97,9 @@ def find_referenced_templates(ast):
# a tuple or list (latter *should* not happen) made of consts,
# yield the consts that are strings. We could warn here for
# non string values
- elif isinstance(node, nodes.Include) and \
- isinstance(node.template.value, (tuple, list)):
+ elif isinstance(node, nodes.Include) and isinstance(
+ node.template.value, (tuple, list)
+ ):
for template_name in node.template.value:
if isinstance(template_name, string_types):
yield template_name
diff --git a/src/jinja2/nativetypes.py b/src/jinja2/nativetypes.py
index 6b3e5f5..9866c96 100644
--- a/src/jinja2/nativetypes.py
+++ b/src/jinja2/nativetypes.py
@@ -33,7 +33,7 @@ def native_concat(nodes, preserve_quotes=True):
else:
if isinstance(nodes, types.GeneratorType):
nodes = chain(head, nodes)
- raw = u''.join([text_type(v) for v in nodes])
+ raw = u"".join([text_type(v) for v in nodes])
try:
literal = literal_eval(raw)
diff --git a/src/jinja2/nodes.py b/src/jinja2/nodes.py
index 36d921d..65b2599 100644
--- a/src/jinja2/nodes.py
+++ b/src/jinja2/nodes.py
@@ -22,30 +22,26 @@ from ._compat import with_metaclass
from .utils import Markup
_binop_to_func = {
- '*': operator.mul,
- '/': operator.truediv,
- '//': operator.floordiv,
- '**': operator.pow,
- '%': operator.mod,
- '+': operator.add,
- '-': operator.sub
+ "*": operator.mul,
+ "/": operator.truediv,
+ "//": operator.floordiv,
+ "**": operator.pow,
+ "%": operator.mod,
+ "+": operator.add,
+ "-": operator.sub,
}
-_uaop_to_func = {
- 'not': operator.not_,
- '+': operator.pos,
- '-': operator.neg
-}
+_uaop_to_func = {"not": operator.not_, "+": operator.pos, "-": operator.neg}
_cmpop_to_func = {
- 'eq': operator.eq,
- 'ne': operator.ne,
- 'gt': operator.gt,
- 'gteq': operator.ge,
- 'lt': operator.lt,
- 'lteq': operator.le,
- 'in': lambda a, b: a in b,
- 'notin': lambda a, b: a not in b
+ "eq": operator.eq,
+ "ne": operator.ne,
+ "gt": operator.gt,
+ "gteq": operator.ge,
+ "lt": operator.lt,
+ "lteq": operator.le,
+ "in": lambda a, b: a in b,
+ "notin": lambda a, b: a not in b,
}
@@ -59,14 +55,14 @@ class NodeType(type):
automatically forwarded to the child."""
def __new__(cls, name, bases, d):
- for attr in 'fields', 'attributes':
+ for attr in "fields", "attributes":
storage = []
storage.extend(getattr(bases[0], attr, ()))
storage.extend(d.get(attr, ()))
- assert len(bases) == 1, 'multiple inheritance not allowed'
- assert len(storage) == len(set(storage)), 'layout conflict'
+ assert len(bases) == 1, "multiple inheritance not allowed"
+ assert len(storage) == len(set(storage)), "layout conflict"
d[attr] = tuple(storage)
- d.setdefault('abstract', False)
+ d.setdefault("abstract", False)
return type.__new__(cls, name, bases, d)
@@ -94,9 +90,11 @@ class EvalContext(object):
def get_eval_context(node, ctx):
if ctx is None:
if node.environment is None:
- raise RuntimeError('if no eval context is passed, the '
- 'node must have an attached '
- 'environment.')
+ raise RuntimeError(
+ "if no eval context is passed, the "
+ "node must have an attached "
+ "environment."
+ )
return EvalContext(node.environment)
return ctx
@@ -117,30 +115,32 @@ class Node(with_metaclass(NodeType, object)):
The `environment` attribute is set at the end of the parsing process for
all nodes automatically.
"""
+
fields = ()
- attributes = ('lineno', 'environment')
+ attributes = ("lineno", "environment")
abstract = True
def __init__(self, *fields, **attributes):
if self.abstract:
- raise TypeError('abstract nodes are not instantiable')
+ raise TypeError("abstract nodes are not instantiable")
if fields:
if len(fields) != len(self.fields):
if not self.fields:
- raise TypeError('%r takes 0 arguments' %
- self.__class__.__name__)
- raise TypeError('%r takes 0 or %d argument%s' % (
- self.__class__.__name__,
- len(self.fields),
- len(self.fields) != 1 and 's' or ''
- ))
+ raise TypeError("%r takes 0 arguments" % self.__class__.__name__)
+ raise TypeError(
+ "%r takes 0 or %d argument%s"
+ % (
+ self.__class__.__name__,
+ len(self.fields),
+ len(self.fields) != 1 and "s" or "",
+ )
+ )
for name, arg in izip(self.fields, fields):
setattr(self, name, arg)
for attr in self.attributes:
setattr(self, attr, attributes.pop(attr, None))
if attributes:
- raise TypeError('unknown attribute %r' %
- next(iter(attributes)))
+ raise TypeError("unknown attribute %r" % next(iter(attributes)))
def iter_fields(self, exclude=None, only=None):
"""This method iterates over all fields that are defined and yields
@@ -150,9 +150,11 @@ class Node(with_metaclass(NodeType, object)):
should be sets or tuples of field names.
"""
for name in self.fields:
- if (exclude is only is None) or \
- (exclude is not None and name not in exclude) or \
- (only is not None and name in only):
+ if (
+ (exclude is only is None)
+ or (exclude is not None and name not in exclude)
+ or (only is not None and name in only)
+ ):
try:
yield name, getattr(self, name)
except AttributeError:
@@ -197,7 +199,7 @@ class Node(with_metaclass(NodeType, object)):
todo = deque([self])
while todo:
node = todo.popleft()
- if 'ctx' in node.fields:
+ if "ctx" in node.fields:
node.ctx = ctx
todo.extend(node.iter_child_nodes())
return self
@@ -207,7 +209,7 @@ class Node(with_metaclass(NodeType, object)):
todo = deque([self])
while todo:
node = todo.popleft()
- if 'lineno' in node.attributes:
+ if "lineno" in node.attributes:
if node.lineno is None or override:
node.lineno = lineno
todo.extend(node.iter_child_nodes())
@@ -223,8 +225,9 @@ class Node(with_metaclass(NodeType, object)):
return self
def __eq__(self, other):
- return type(self) is type(other) and \
- tuple(self.iter_fields()) == tuple(other.iter_fields())
+ return type(self) is type(other) and tuple(self.iter_fields()) == tuple(
+ other.iter_fields()
+ )
def __ne__(self, other):
return not self.__eq__(other)
@@ -233,10 +236,9 @@ class Node(with_metaclass(NodeType, object)):
__hash__ = object.__hash__
def __repr__(self):
- return '%s(%s)' % (
+ return "%s(%s)" % (
self.__class__.__name__,
- ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
- arg in self.fields)
+ ", ".join("%s=%r" % (arg, getattr(self, arg, None)) for arg in self.fields),
)
def dump(self):
@@ -245,37 +247,39 @@ class Node(with_metaclass(NodeType, object)):
buf.append(repr(node))
return
- buf.append('nodes.%s(' % node.__class__.__name__)
+ buf.append("nodes.%s(" % node.__class__.__name__)
if not node.fields:
- buf.append(')')
+ buf.append(")")
return
for idx, field in enumerate(node.fields):
if idx:
- buf.append(', ')
+ buf.append(", ")
value = getattr(node, field)
if isinstance(value, list):
- buf.append('[')
+ buf.append("[")
for idx, item in enumerate(value):
if idx:
- buf.append(', ')
+ buf.append(", ")
_dump(item)
- buf.append(']')
+ buf.append("]")
else:
_dump(value)
- buf.append(')')
+ buf.append(")")
+
buf = []
_dump(self)
- return ''.join(buf)
-
+ return "".join(buf)
class Stmt(Node):
"""Base node for all statements."""
+
abstract = True
class Helper(Node):
"""Nodes that exist in a specific context only."""
+
abstract = True
@@ -283,19 +287,22 @@ class Template(Node):
"""Node that represents a template. This must be the outermost node that
is passed to the compiler.
"""
- fields = ('body',)
+
+ fields = ("body",)
class Output(Stmt):
"""A node that holds multiple expressions which are then printed out.
This is used both for the `print` statement and the regular template data.
"""
- fields = ('nodes',)
+
+ fields = ("nodes",)
class Extends(Stmt):
"""Represents an extends statement."""
- fields = ('template',)
+
+ fields = ("template",)
class For(Stmt):
@@ -306,12 +313,14 @@ class For(Stmt):
For filtered nodes an expression can be stored as `test`, otherwise `None`.
"""
- fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive')
+
+ fields = ("target", "iter", "body", "else_", "test", "recursive")
class If(Stmt):
"""If `test` is true, `body` is rendered, else `else_`."""
- fields = ('test', 'body', 'elif_', 'else_')
+
+ fields = ("test", "body", "elif_", "else_")
class Macro(Stmt):
@@ -319,19 +328,22 @@ class Macro(Stmt):
arguments and `defaults` a list of defaults if there are any. `body` is
a list of nodes for the macro body.
"""
- fields = ('name', 'args', 'defaults', 'body')
+
+ fields = ("name", "args", "defaults", "body")
class CallBlock(Stmt):
"""Like a macro without a name but a call instead. `call` is called with
the unnamed macro as `caller` argument this node holds.
"""
- fields = ('call', 'args', 'defaults', 'body')
+
+ fields = ("call", "args", "defaults", "body")
class FilterBlock(Stmt):
"""Node for filter sections."""
- fields = ('body', 'filter')
+
+ fields = ("body", "filter")
class With(Stmt):
@@ -340,22 +352,26 @@ class With(Stmt):
.. versionadded:: 2.9.3
"""
- fields = ('targets', 'values', 'body')
+
+ fields = ("targets", "values", "body")
class Block(Stmt):
"""A node that represents a block."""
- fields = ('name', 'body', 'scoped')
+
+ fields = ("name", "body", "scoped")
class Include(Stmt):
"""A node that represents the include tag."""
- fields = ('template', 'with_context', 'ignore_missing')
+
+ fields = ("template", "with_context", "ignore_missing")
class Import(Stmt):
"""A node that represents the import tag."""
- fields = ('template', 'target', 'with_context')
+
+ fields = ("template", "target", "with_context")
class FromImport(Stmt):
@@ -369,26 +385,31 @@ class FromImport(Stmt):
The list of names may contain tuples if aliases are wanted.
"""
- fields = ('template', 'names', 'with_context')
+
+ fields = ("template", "names", "with_context")
class ExprStmt(Stmt):
"""A statement that evaluates an expression and discards the result."""
- fields = ('node',)
+
+ fields = ("node",)
class Assign(Stmt):
"""Assigns an expression to a target."""
- fields = ('target', 'node')
+
+ fields = ("target", "node")
class AssignBlock(Stmt):
"""Assigns a block to a target."""
- fields = ('target', 'filter', 'body')
+
+ fields = ("target", "filter", "body")
class Expr(Node):
"""Baseclass for all expressions."""
+
abstract = True
def as_const(self, eval_ctx=None):
@@ -411,15 +432,18 @@ class Expr(Node):
class BinExpr(Expr):
"""Baseclass for all binary expressions."""
- fields = ('left', 'right')
+
+ fields = ("left", "right")
operator = None
abstract = True
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
# intercepted operators cannot be folded at compile time
- if self.environment.sandboxed and \
- self.operator in self.environment.intercepted_binops:
+ if (
+ self.environment.sandboxed
+ and self.operator in self.environment.intercepted_binops
+ ):
raise Impossible()
f = _binop_to_func[self.operator]
try:
@@ -430,15 +454,18 @@ class BinExpr(Expr):
class UnaryExpr(Expr):
"""Baseclass for all unary expressions."""
- fields = ('node',)
+
+ fields = ("node",)
operator = None
abstract = True
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
# intercepted operators cannot be folded at compile time
- if self.environment.sandboxed and \
- self.operator in self.environment.intercepted_unops:
+ if (
+ self.environment.sandboxed
+ and self.operator in self.environment.intercepted_unops
+ ):
raise Impossible()
f = _uaop_to_func[self.operator]
try:
@@ -455,16 +482,17 @@ class Name(Expr):
- `load`: load that name
- `param`: like `store` but if the name was defined as function parameter.
"""
- fields = ('name', 'ctx')
+
+ fields = ("name", "ctx")
def can_assign(self):
- return self.name not in ('true', 'false', 'none',
- 'True', 'False', 'None')
+ return self.name not in ("true", "false", "none", "True", "False", "None")
class NSRef(Expr):
"""Reference to a namespace value assignment"""
- fields = ('name', 'attr')
+
+ fields = ("name", "attr")
def can_assign(self):
# We don't need any special checks here; NSRef assignments have a
@@ -476,6 +504,7 @@ class NSRef(Expr):
class Literal(Expr):
"""Baseclass for literals."""
+
abstract = True
@@ -485,14 +514,18 @@ class Const(Literal):
complex values such as lists too. Only constants with a safe
representation (objects where ``eval(repr(x)) == x`` is true).
"""
- fields = ('value',)
+
+ fields = ("value",)
def as_const(self, eval_ctx=None):
rv = self.value
- if PY2 and type(rv) is text_type and \
- self.environment.policies['compiler.ascii_str']:
+ if (
+ PY2
+ and type(rv) is text_type
+ and self.environment.policies["compiler.ascii_str"]
+ ):
try:
- rv = rv.encode('ascii')
+ rv = rv.encode("ascii")
except UnicodeError:
pass
return rv
@@ -504,6 +537,7 @@ class Const(Literal):
an `Impossible` exception.
"""
from .compiler import has_safe_repr
+
if not has_safe_repr(value):
raise Impossible()
return cls(value, lineno=lineno, environment=environment)
@@ -511,7 +545,8 @@ class Const(Literal):
class TemplateData(Literal):
"""A constant template string."""
- fields = ('data',)
+
+ fields = ("data",)
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -527,7 +562,8 @@ class Tuple(Literal):
for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
is used for loading the names or storing.
"""
- fields = ('items', 'ctx')
+
+ fields = ("items", "ctx")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -542,7 +578,8 @@ class Tuple(Literal):
class List(Literal):
"""Any list literal such as ``[1, 2, 3]``"""
- fields = ('items',)
+
+ fields = ("items",)
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -553,7 +590,8 @@ class Dict(Literal):
"""Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
:class:`Pair` nodes.
"""
- fields = ('items',)
+
+ fields = ("items",)
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -562,7 +600,8 @@ class Dict(Literal):
class Pair(Helper):
"""A key, value pair for dicts."""
- fields = ('key', 'value')
+
+ fields = ("key", "value")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -571,7 +610,8 @@ class Pair(Helper):
class Keyword(Helper):
"""A key, value pair for keyword arguments where key is a string."""
- fields = ('key', 'value')
+
+ fields = ("key", "value")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -582,7 +622,8 @@ class CondExpr(Expr):
"""A conditional expression (inline if expression). (``{{
foo if bar else baz }}``)
"""
- fields = ('test', 'expr1', 'expr2')
+
+ fields = ("test", "expr1", "expr2")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -623,7 +664,7 @@ class Filter(Expr):
filtered. Buffers are created by macros and filter blocks.
"""
- fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+ fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -638,23 +679,22 @@ class Filter(Expr):
# python 3. because of that, do not rename filter_ to filter!
filter_ = self.environment.filters.get(self.name)
- if filter_ is None or getattr(filter_, 'contextfilter', False):
+ if filter_ is None or getattr(filter_, "contextfilter", False):
raise Impossible()
# We cannot constant handle async filters, so we need to make sure
# to not go down this path.
- if (
- eval_ctx.environment.is_async
- and getattr(filter_, 'asyncfiltervariant', False)
+ if eval_ctx.environment.is_async and getattr(
+ filter_, "asyncfiltervariant", False
):
raise Impossible()
args, kwargs = args_as_const(self, eval_ctx)
args.insert(0, self.node.as_const(eval_ctx))
- if getattr(filter_, 'evalcontextfilter', False):
+ if getattr(filter_, "evalcontextfilter", False):
args.insert(0, eval_ctx)
- elif getattr(filter_, 'environmentfilter', False):
+ elif getattr(filter_, "environmentfilter", False):
args.insert(0, self.environment)
try:
@@ -668,7 +708,7 @@ class Test(Expr):
rest of the fields are the same as for :class:`Call`.
"""
- fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+ fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
def as_const(self, eval_ctx=None):
test = self.environment.tests.get(self.name)
@@ -693,20 +733,23 @@ class Call(Expr):
node for dynamic positional (``*args``) or keyword (``**kwargs``)
arguments.
"""
- fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+
+ fields = ("node", "args", "kwargs", "dyn_args", "dyn_kwargs")
class Getitem(Expr):
"""Get an attribute or item from an expression and prefer the item."""
- fields = ('node', 'arg', 'ctx')
+
+ fields = ("node", "arg", "ctx")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
- if self.ctx != 'load':
+ if self.ctx != "load":
raise Impossible()
try:
- return self.environment.getitem(self.node.as_const(eval_ctx),
- self.arg.as_const(eval_ctx))
+ return self.environment.getitem(
+ self.node.as_const(eval_ctx), self.arg.as_const(eval_ctx)
+ )
except Exception:
raise Impossible()
@@ -718,15 +761,15 @@ class Getattr(Expr):
"""Get an attribute or item from an expression that is a ascii-only
bytestring and prefer the attribute.
"""
- fields = ('node', 'attr', 'ctx')
+
+ fields = ("node", "attr", "ctx")
def as_const(self, eval_ctx=None):
- if self.ctx != 'load':
+ if self.ctx != "load":
raise Impossible()
try:
eval_ctx = get_eval_context(self, eval_ctx)
- return self.environment.getattr(self.node.as_const(eval_ctx),
- self.attr)
+ return self.environment.getattr(self.node.as_const(eval_ctx), self.attr)
except Exception:
raise Impossible()
@@ -738,14 +781,17 @@ class Slice(Expr):
"""Represents a slice object. This must only be used as argument for
:class:`Subscript`.
"""
- fields = ('start', 'stop', 'step')
+
+ fields = ("start", "stop", "step")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
+
def const(obj):
if obj is None:
return None
return obj.as_const(eval_ctx)
+
return slice(const(self.start), const(self.stop), const(self.step))
@@ -753,18 +799,20 @@ class Concat(Expr):
"""Concatenates the list of expressions provided after converting them to
unicode.
"""
- fields = ('nodes',)
+
+ fields = ("nodes",)
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
- return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes)
+ return "".join(text_type(x.as_const(eval_ctx)) for x in self.nodes)
class Compare(Expr):
"""Compares an expression with some other expressions. `ops` must be a
list of :class:`Operand`\\s.
"""
- fields = ('expr', 'ops')
+
+ fields = ("expr", "ops")
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -787,54 +835,67 @@ class Compare(Expr):
class Operand(Helper):
"""Holds an operator and an expression."""
- fields = ('op', 'expr')
+
+ fields = ("op", "expr")
+
if __debug__:
- Operand.__doc__ += '\nThe following operators are available: ' + \
- ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) |
- set(_uaop_to_func) | set(_cmpop_to_func)))
+ Operand.__doc__ += "\nThe following operators are available: " + ", ".join(
+ sorted(
+ "``%s``" % x
+ for x in set(_binop_to_func) | set(_uaop_to_func) | set(_cmpop_to_func)
+ )
+ )
class Mul(BinExpr):
"""Multiplies the left with the right node."""
- operator = '*'
+
+ operator = "*"
class Div(BinExpr):
"""Divides the left by the right node."""
- operator = '/'
+
+ operator = "/"
class FloorDiv(BinExpr):
"""Divides the left by the right node and truncates conver the
result into an integer by truncating.
"""
- operator = '//'
+
+ operator = "//"
class Add(BinExpr):
"""Add the left to the right node."""
- operator = '+'
+
+ operator = "+"
class Sub(BinExpr):
"""Subtract the right from the left node."""
- operator = '-'
+
+ operator = "-"
class Mod(BinExpr):
"""Left modulo right."""
- operator = '%'
+
+ operator = "%"
class Pow(BinExpr):
"""Left to the power of right."""
- operator = '**'
+
+ operator = "**"
class And(BinExpr):
"""Short circuited AND."""
- operator = 'and'
+
+ operator = "and"
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -843,7 +904,8 @@ class And(BinExpr):
class Or(BinExpr):
"""Short circuited OR."""
- operator = 'or'
+
+ operator = "or"
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -852,17 +914,20 @@ class Or(BinExpr):
class Not(UnaryExpr):
"""Negate the expression."""
- operator = 'not'
+
+ operator = "not"
class Neg(UnaryExpr):
"""Make the expression negative."""
- operator = '-'
+
+ operator = "-"
class Pos(UnaryExpr):
"""Make the expression positive (noop for most expressions)"""
- operator = '+'
+
+ operator = "+"
# Helpers for extensions
@@ -872,7 +937,8 @@ class EnvironmentAttribute(Expr):
"""Loads an attribute from the environment object. This is useful for
extensions that want to call a callback stored on the environment.
"""
- fields = ('name',)
+
+ fields = ("name",)
class ExtensionAttribute(Expr):
@@ -882,7 +948,8 @@ class ExtensionAttribute(Expr):
This node is usually constructed by calling the
:meth:`~jinja2.ext.Extension.attr` method on an extension.
"""
- fields = ('identifier', 'name')
+
+ fields = ("identifier", "name")
class ImportedName(Expr):
@@ -891,7 +958,8 @@ class ImportedName(Expr):
function from the cgi module on evaluation. Imports are optimized by the
compiler so there is no need to assign them to local variables.
"""
- fields = ('importname',)
+
+ fields = ("importname",)
class InternalName(Expr):
@@ -901,16 +969,20 @@ class InternalName(Expr):
a new identifier for you. This identifier is not available from the
template and is not threated specially by the compiler.
"""
- fields = ('name',)
+
+ fields = ("name",)
def __init__(self):
- raise TypeError('Can\'t create internal names. Use the '
- '`free_identifier` method on a parser.')
+ raise TypeError(
+ "Can't create internal names. Use the "
+ "`free_identifier` method on a parser."
+ )
class MarkSafe(Expr):
"""Mark the wrapped expression as safe (wrap it as `Markup`)."""
- fields = ('expr',)
+
+ fields = ("expr",)
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -923,7 +995,8 @@ class MarkSafeIfAutoescape(Expr):
.. versionadded:: 2.5
"""
- fields = ('expr',)
+
+ fields = ("expr",)
def as_const(self, eval_ctx=None):
eval_ctx = get_eval_context(self, eval_ctx)
@@ -972,7 +1045,8 @@ class Break(Stmt):
class Scope(Stmt):
"""An artificial scope."""
- fields = ('body',)
+
+ fields = ("body",)
class OverlayScope(Stmt):
@@ -988,7 +1062,8 @@ class OverlayScope(Stmt):
.. versionadded:: 2.10
"""
- fields = ('context', 'body')
+
+ fields = ("context", "body")
class EvalContextModifier(Stmt):
@@ -999,7 +1074,8 @@ class EvalContextModifier(Stmt):
EvalContextModifier(options=[Keyword('autoescape', Const(True))])
"""
- fields = ('options',)
+
+ fields = ("options",)
class ScopedEvalContextModifier(EvalContextModifier):
@@ -1007,10 +1083,14 @@ class ScopedEvalContextModifier(EvalContextModifier):
:class:`EvalContextModifier` but will only modify the
:class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
"""
- fields = ('body',)
+
+ fields = ("body",)
# make sure nobody creates custom nodes
def _failing_new(*args, **kwargs):
- raise TypeError('can\'t create custom node types')
-NodeType.__new__ = staticmethod(_failing_new); del _failing_new
+ raise TypeError("can't create custom node types")
+
+
+NodeType.__new__ = staticmethod(_failing_new)
+del _failing_new
diff --git a/src/jinja2/parser.py b/src/jinja2/parser.py
index 3759761..648c3fd 100644
--- a/src/jinja2/parser.py
+++ b/src/jinja2/parser.py
@@ -15,18 +15,31 @@ from .exceptions import TemplateSyntaxError
from .lexer import describe_token
from .lexer import describe_token_expr
-_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
- 'macro', 'include', 'from', 'import',
- 'set', 'with', 'autoescape'])
-_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
+_statement_keywords = frozenset(
+ [
+ "for",
+ "if",
+ "block",
+ "extends",
+ "print",
+ "macro",
+ "include",
+ "from",
+ "import",
+ "set",
+ "with",
+ "autoescape",
+ ]
+)
+_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
_math_nodes = {
- 'add': nodes.Add,
- 'sub': nodes.Sub,
- 'mul': nodes.Mul,
- 'div': nodes.Div,
- 'floordiv': nodes.FloorDiv,
- 'mod': nodes.Mod,
+ "add": nodes.Add,
+ "sub": nodes.Sub,
+ "mul": nodes.Mul,
+ "div": nodes.Div,
+ "floordiv": nodes.FloorDiv,
+ "mod": nodes.Mod,
}
@@ -35,8 +48,7 @@ class Parser(object):
extensions and can be used to parse expressions or statements.
"""
- def __init__(self, environment, source, name=None, filename=None,
- state=None):
+ def __init__(self, environment, source, name=None, filename=None, state=None):
self.environment = environment
self.stream = environment._tokenize(source, name, filename, state)
self.name = name
@@ -64,31 +76,37 @@ class Parser(object):
for exprs in end_token_stack:
expected.extend(imap(describe_token_expr, exprs))
if end_token_stack:
- currently_looking = ' or '.join(
- "'%s'" % describe_token_expr(expr)
- for expr in end_token_stack[-1])
+ currently_looking = " or ".join(
+ "'%s'" % describe_token_expr(expr) for expr in end_token_stack[-1]
+ )
else:
currently_looking = None
if name is None:
- message = ['Unexpected end of template.']
+ message = ["Unexpected end of template."]
else:
- message = ['Encountered unknown tag \'%s\'.' % name]
+ message = ["Encountered unknown tag '%s'." % name]
if currently_looking:
if name is not None and name in expected:
- message.append('You probably made a nesting mistake. Jinja '
- 'is expecting this tag, but currently looking '
- 'for %s.' % currently_looking)
+ message.append(
+ "You probably made a nesting mistake. Jinja "
+ "is expecting this tag, but currently looking "
+ "for %s." % currently_looking
+ )
else:
- message.append('Jinja was looking for the following tags: '
- '%s.' % currently_looking)
+ message.append(
+ "Jinja was looking for the following tags: "
+ "%s." % currently_looking
+ )
if self._tag_stack:
- message.append('The innermost block that needs to be '
- 'closed is \'%s\'.' % self._tag_stack[-1])
+ message.append(
+ "The innermost block that needs to be "
+ "closed is '%s'." % self._tag_stack[-1]
+ )
- self.fail(' '.join(message), lineno)
+ self.fail(" ".join(message), lineno)
def fail_unknown_tag(self, name, lineno=None):
"""Called if the parser encounters an unknown tag. Tries to fail
@@ -106,7 +124,7 @@ class Parser(object):
def is_tuple_end(self, extra_end_rules=None):
"""Are we at the end of a tuple?"""
- if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
+ if self.stream.current.type in ("variable_end", "block_end", "rparen"):
return True
elif extra_end_rules is not None:
return self.stream.current.test_any(extra_end_rules)
@@ -116,22 +134,22 @@ class Parser(object):
"""Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
self._last_identifier += 1
rv = object.__new__(nodes.InternalName)
- nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
+ nodes.Node.__init__(rv, "fi%d" % self._last_identifier, lineno=lineno)
return rv
def parse_statement(self):
"""Parse a single statement."""
token = self.stream.current
- if token.type != 'name':
- self.fail('tag name expected', token.lineno)
+ if token.type != "name":
+ self.fail("tag name expected", token.lineno)
self._tag_stack.append(token.value)
pop_tag = True
try:
if token.value in _statement_keywords:
- return getattr(self, 'parse_' + self.stream.current.value)()
- if token.value == 'call':
+ return getattr(self, "parse_" + self.stream.current.value)()
+ if token.value == "call":
return self.parse_call_block()
- if token.value == 'filter':
+ if token.value == "filter":
return self.parse_filter_block()
ext = self.extensions.get(token.value)
if ext is not None:
@@ -158,16 +176,16 @@ class Parser(object):
can be set to `True` and the end token is removed.
"""
# the first token may be a colon for python compatibility
- self.stream.skip_if('colon')
+ self.stream.skip_if("colon")
# in the future it would be possible to add whole code sections
# by adding some sort of end of statement token and parsing those here.
- self.stream.expect('block_end')
+ self.stream.expect("block_end")
result = self.subparse(end_tokens)
# we reached the end of the template too early, the subparser
# does not check for this, so we do that now
- if self.stream.current.type == 'eof':
+ if self.stream.current.type == "eof":
self.fail_eof(end_tokens)
if drop_needle:
@@ -178,50 +196,47 @@ class Parser(object):
"""Parse an assign statement."""
lineno = next(self.stream).lineno
target = self.parse_assign_target(with_namespace=True)
- if self.stream.skip_if('assign'):
+ if self.stream.skip_if("assign"):
expr = self.parse_tuple()
return nodes.Assign(target, expr, lineno=lineno)
filter_node = self.parse_filter(None)
- body = self.parse_statements(('name:endset',),
- drop_needle=True)
+ body = self.parse_statements(("name:endset",), drop_needle=True)
return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
def parse_for(self):
"""Parse a for loop."""
- lineno = self.stream.expect('name:for').lineno
- target = self.parse_assign_target(extra_end_rules=('name:in',))
- self.stream.expect('name:in')
- iter = self.parse_tuple(with_condexpr=False,
- extra_end_rules=('name:recursive',))
+ lineno = self.stream.expect("name:for").lineno
+ target = self.parse_assign_target(extra_end_rules=("name:in",))
+ self.stream.expect("name:in")
+ iter = self.parse_tuple(
+ with_condexpr=False, extra_end_rules=("name:recursive",)
+ )
test = None
- if self.stream.skip_if('name:if'):
+ if self.stream.skip_if("name:if"):
test = self.parse_expression()
- recursive = self.stream.skip_if('name:recursive')
- body = self.parse_statements(('name:endfor', 'name:else'))
- if next(self.stream).value == 'endfor':
+ recursive = self.stream.skip_if("name:recursive")
+ body = self.parse_statements(("name:endfor", "name:else"))
+ if next(self.stream).value == "endfor":
else_ = []
else:
- else_ = self.parse_statements(('name:endfor',), drop_needle=True)
- return nodes.For(target, iter, body, else_, test,
- recursive, lineno=lineno)
+ else_ = self.parse_statements(("name:endfor",), drop_needle=True)
+ return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno)
def parse_if(self):
"""Parse an if construct."""
- node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
+ node = result = nodes.If(lineno=self.stream.expect("name:if").lineno)
while 1:
node.test = self.parse_tuple(with_condexpr=False)
- node.body = self.parse_statements(('name:elif', 'name:else',
- 'name:endif'))
+ node.body = self.parse_statements(("name:elif", "name:else", "name:endif"))
node.elif_ = []
node.else_ = []
token = next(self.stream)
- if token.test('name:elif'):
+ if token.test("name:elif"):
node = nodes.If(lineno=self.stream.current.lineno)
result.elif_.append(node)
continue
- elif token.test('name:else'):
- result.else_ = self.parse_statements(('name:endif',),
- drop_needle=True)
+ elif token.test("name:else"):
+ result.else_ = self.parse_statements(("name:endif",), drop_needle=True)
break
return result
@@ -229,45 +244,43 @@ class Parser(object):
node = nodes.With(lineno=next(self.stream).lineno)
targets = []
values = []
- while self.stream.current.type != 'block_end':
+ while self.stream.current.type != "block_end":
lineno = self.stream.current.lineno
if targets:
- self.stream.expect('comma')
+ self.stream.expect("comma")
target = self.parse_assign_target()
- target.set_ctx('param')
+ target.set_ctx("param")
targets.append(target)
- self.stream.expect('assign')
+ self.stream.expect("assign")
values.append(self.parse_expression())
node.targets = targets
node.values = values
- node.body = self.parse_statements(('name:endwith',),
- drop_needle=True)
+ node.body = self.parse_statements(("name:endwith",), drop_needle=True)
return node
def parse_autoescape(self):
node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno)
- node.options = [
- nodes.Keyword('autoescape', self.parse_expression())
- ]
- node.body = self.parse_statements(('name:endautoescape',),
- drop_needle=True)
+ node.options = [nodes.Keyword("autoescape", self.parse_expression())]
+ node.body = self.parse_statements(("name:endautoescape",), drop_needle=True)
return nodes.Scope([node])
def parse_block(self):
node = nodes.Block(lineno=next(self.stream).lineno)
- node.name = self.stream.expect('name').value
- node.scoped = self.stream.skip_if('name:scoped')
+ node.name = self.stream.expect("name").value
+ node.scoped = self.stream.skip_if("name:scoped")
# common problem people encounter when switching from django
# to jinja. we do not support hyphens in block names, so let's
# raise a nicer error message in that case.
- if self.stream.current.type == 'sub':
- self.fail('Block names in Jinja have to be valid Python '
- 'identifiers and may not contain hyphens, use an '
- 'underscore instead.')
-
- node.body = self.parse_statements(('name:endblock',), drop_needle=True)
- self.stream.skip_if('name:' + node.name)
+ if self.stream.current.type == "sub":
+ self.fail(
+ "Block names in Jinja have to be valid Python "
+ "identifiers and may not contain hyphens, use an "
+ "underscore instead."
+ )
+
+ node.body = self.parse_statements(("name:endblock",), drop_needle=True)
+ self.stream.skip_if("name:" + node.name)
return node
def parse_extends(self):
@@ -276,9 +289,10 @@ class Parser(object):
return node
def parse_import_context(self, node, default):
- if self.stream.current.test_any('name:with', 'name:without') and \
- self.stream.look().test('name:context'):
- node.with_context = next(self.stream).value == 'with'
+ if self.stream.current.test_any(
+ "name:with", "name:without"
+ ) and self.stream.look().test("name:context"):
+ node.with_context = next(self.stream).value == "with"
self.stream.skip()
else:
node.with_context = default
@@ -287,8 +301,9 @@ class Parser(object):
def parse_include(self):
node = nodes.Include(lineno=next(self.stream).lineno)
node.template = self.parse_expression()
- if self.stream.current.test('name:ignore') and \
- self.stream.look().test('name:missing'):
+ if self.stream.current.test("name:ignore") and self.stream.look().test(
+ "name:missing"
+ ):
node.ignore_missing = True
self.stream.skip(2)
else:
@@ -298,67 +313,71 @@ class Parser(object):
def parse_import(self):
node = nodes.Import(lineno=next(self.stream).lineno)
node.template = self.parse_expression()
- self.stream.expect('name:as')
+ self.stream.expect("name:as")
node.target = self.parse_assign_target(name_only=True).name
return self.parse_import_context(node, False)
def parse_from(self):
node = nodes.FromImport(lineno=next(self.stream).lineno)
node.template = self.parse_expression()
- self.stream.expect('name:import')
+ self.stream.expect("name:import")
node.names = []
def parse_context():
- if self.stream.current.value in ('with', 'without') and \
- self.stream.look().test('name:context'):
- node.with_context = next(self.stream).value == 'with'
+ if self.stream.current.value in (
+ "with",
+ "without",
+ ) and self.stream.look().test("name:context"):
+ node.with_context = next(self.stream).value == "with"
self.stream.skip()
return True
return False
while 1:
if node.names:
- self.stream.expect('comma')
- if self.stream.current.type == 'name':
+ self.stream.expect("comma")
+ if self.stream.current.type == "name":
if parse_context():
break
target = self.parse_assign_target(name_only=True)
- if target.name.startswith('_'):
- self.fail('names starting with an underline can not '
- 'be imported', target.lineno,
- exc=TemplateAssertionError)
- if self.stream.skip_if('name:as'):
+ if target.name.startswith("_"):
+ self.fail(
+ "names starting with an underline can not be imported",
+ target.lineno,
+ exc=TemplateAssertionError,
+ )
+ if self.stream.skip_if("name:as"):
alias = self.parse_assign_target(name_only=True)
node.names.append((target.name, alias.name))
else:
node.names.append(target.name)
- if parse_context() or self.stream.current.type != 'comma':
+ if parse_context() or self.stream.current.type != "comma":
break
else:
- self.stream.expect('name')
- if not hasattr(node, 'with_context'):
+ self.stream.expect("name")
+ if not hasattr(node, "with_context"):
node.with_context = False
return node
def parse_signature(self, node):
node.args = args = []
node.defaults = defaults = []
- self.stream.expect('lparen')
- while self.stream.current.type != 'rparen':
+ self.stream.expect("lparen")
+ while self.stream.current.type != "rparen":
if args:
- self.stream.expect('comma')
+ self.stream.expect("comma")
arg = self.parse_assign_target(name_only=True)
- arg.set_ctx('param')
- if self.stream.skip_if('assign'):
+ arg.set_ctx("param")
+ if self.stream.skip_if("assign"):
defaults.append(self.parse_expression())
elif defaults:
- self.fail('non-default argument follows default argument')
+ self.fail("non-default argument follows default argument")
args.append(arg)
- self.stream.expect('rparen')
+ self.stream.expect("rparen")
def parse_call_block(self):
node = nodes.CallBlock(lineno=next(self.stream).lineno)
- if self.stream.current.type == 'lparen':
+ if self.stream.current.type == "lparen":
self.parse_signature(node)
else:
node.args = []
@@ -366,36 +385,39 @@ class Parser(object):
node.call = self.parse_expression()
if not isinstance(node.call, nodes.Call):
- self.fail('expected call', node.lineno)
- node.body = self.parse_statements(('name:endcall',), drop_needle=True)
+ self.fail("expected call", node.lineno)
+ node.body = self.parse_statements(("name:endcall",), drop_needle=True)
return node
def parse_filter_block(self):
node = nodes.FilterBlock(lineno=next(self.stream).lineno)
node.filter = self.parse_filter(None, start_inline=True)
- node.body = self.parse_statements(('name:endfilter',),
- drop_needle=True)
+ node.body = self.parse_statements(("name:endfilter",), drop_needle=True)
return node
def parse_macro(self):
node = nodes.Macro(lineno=next(self.stream).lineno)
node.name = self.parse_assign_target(name_only=True).name
self.parse_signature(node)
- node.body = self.parse_statements(('name:endmacro',),
- drop_needle=True)
+ node.body = self.parse_statements(("name:endmacro",), drop_needle=True)
return node
def parse_print(self):
node = nodes.Output(lineno=next(self.stream).lineno)
node.nodes = []
- while self.stream.current.type != 'block_end':
+ while self.stream.current.type != "block_end":
if node.nodes:
- self.stream.expect('comma')
+ self.stream.expect("comma")
node.nodes.append(self.parse_expression())
return node
- def parse_assign_target(self, with_tuple=True, name_only=False,
- extra_end_rules=None, with_namespace=False):
+ def parse_assign_target(
+ self,
+ with_tuple=True,
+ name_only=False,
+ extra_end_rules=None,
+ with_namespace=False,
+ ):
"""Parse an assignment target. As Jinja allows assignments to
tuples, this function can parse all allowed assignment targets. Per
default assignments to tuples are parsed, that can be disable however
@@ -404,24 +426,26 @@ class Parser(object):
parameter is forwarded to the tuple parsing function. If
`with_namespace` is enabled, a namespace assignment may be parsed.
"""
- if with_namespace and self.stream.look().type == 'dot':
- token = self.stream.expect('name')
+ if with_namespace and self.stream.look().type == "dot":
+ token = self.stream.expect("name")
next(self.stream) # dot
- attr = self.stream.expect('name')
+ attr = self.stream.expect("name")
target = nodes.NSRef(token.value, attr.value, lineno=token.lineno)
elif name_only:
- token = self.stream.expect('name')
- target = nodes.Name(token.value, 'store', lineno=token.lineno)
+ token = self.stream.expect("name")
+ target = nodes.Name(token.value, "store", lineno=token.lineno)
else:
if with_tuple:
- target = self.parse_tuple(simplified=True,
- extra_end_rules=extra_end_rules)
+ target = self.parse_tuple(
+ simplified=True, extra_end_rules=extra_end_rules
+ )
else:
target = self.parse_primary()
- target.set_ctx('store')
+ target.set_ctx("store")
if not target.can_assign():
- self.fail('can\'t assign to %r' % target.__class__.
- __name__.lower(), target.lineno)
+ self.fail(
+ "can't assign to %r" % target.__class__.__name__.lower(), target.lineno
+ )
return target
def parse_expression(self, with_condexpr=True):
@@ -436,9 +460,9 @@ class Parser(object):
def parse_condexpr(self):
lineno = self.stream.current.lineno
expr1 = self.parse_or()
- while self.stream.skip_if('name:if'):
+ while self.stream.skip_if("name:if"):
expr2 = self.parse_or()
- if self.stream.skip_if('name:else'):
+ if self.stream.skip_if("name:else"):
expr3 = self.parse_condexpr()
else:
expr3 = None
@@ -449,7 +473,7 @@ class Parser(object):
def parse_or(self):
lineno = self.stream.current.lineno
left = self.parse_and()
- while self.stream.skip_if('name:or'):
+ while self.stream.skip_if("name:or"):
right = self.parse_and()
left = nodes.Or(left, right, lineno=lineno)
lineno = self.stream.current.lineno
@@ -458,14 +482,14 @@ class Parser(object):
def parse_and(self):
lineno = self.stream.current.lineno
left = self.parse_not()
- while self.stream.skip_if('name:and'):
+ while self.stream.skip_if("name:and"):
right = self.parse_not()
left = nodes.And(left, right, lineno=lineno)
lineno = self.stream.current.lineno
return left
def parse_not(self):
- if self.stream.current.test('name:not'):
+ if self.stream.current.test("name:not"):
lineno = next(self.stream).lineno
return nodes.Not(self.parse_not(), lineno=lineno)
return self.parse_compare()
@@ -479,12 +503,13 @@ class Parser(object):
if token_type in _compare_operators:
next(self.stream)
ops.append(nodes.Operand(token_type, self.parse_math1()))
- elif self.stream.skip_if('name:in'):
- ops.append(nodes.Operand('in', self.parse_math1()))
- elif (self.stream.current.test('name:not') and
- self.stream.look().test('name:in')):
+ elif self.stream.skip_if("name:in"):
+ ops.append(nodes.Operand("in", self.parse_math1()))
+ elif self.stream.current.test("name:not") and self.stream.look().test(
+ "name:in"
+ ):
self.stream.skip(2)
- ops.append(nodes.Operand('notin', self.parse_math1()))
+ ops.append(nodes.Operand("notin", self.parse_math1()))
else:
break
lineno = self.stream.current.lineno
@@ -495,7 +520,7 @@ class Parser(object):
def parse_math1(self):
lineno = self.stream.current.lineno
left = self.parse_concat()
- while self.stream.current.type in ('add', 'sub'):
+ while self.stream.current.type in ("add", "sub"):
cls = _math_nodes[self.stream.current.type]
next(self.stream)
right = self.parse_concat()
@@ -506,7 +531,7 @@ class Parser(object):
def parse_concat(self):
lineno = self.stream.current.lineno
args = [self.parse_math2()]
- while self.stream.current.type == 'tilde':
+ while self.stream.current.type == "tilde":
next(self.stream)
args.append(self.parse_math2())
if len(args) == 1:
@@ -516,7 +541,7 @@ class Parser(object):
def parse_math2(self):
lineno = self.stream.current.lineno
left = self.parse_pow()
- while self.stream.current.type in ('mul', 'div', 'floordiv', 'mod'):
+ while self.stream.current.type in ("mul", "div", "floordiv", "mod"):
cls = _math_nodes[self.stream.current.type]
next(self.stream)
right = self.parse_pow()
@@ -527,7 +552,7 @@ class Parser(object):
def parse_pow(self):
lineno = self.stream.current.lineno
left = self.parse_unary()
- while self.stream.current.type == 'pow':
+ while self.stream.current.type == "pow":
next(self.stream)
right = self.parse_unary()
left = nodes.Pow(left, right, lineno=lineno)
@@ -537,10 +562,10 @@ class Parser(object):
def parse_unary(self, with_filter=True):
token_type = self.stream.current.type
lineno = self.stream.current.lineno
- if token_type == 'sub':
+ if token_type == "sub":
next(self.stream)
node = nodes.Neg(self.parse_unary(False), lineno=lineno)
- elif token_type == 'add':
+ elif token_type == "add":
next(self.stream)
node = nodes.Pos(self.parse_unary(False), lineno=lineno)
else:
@@ -552,40 +577,44 @@ class Parser(object):
def parse_primary(self):
token = self.stream.current
- if token.type == 'name':
- if token.value in ('true', 'false', 'True', 'False'):
- node = nodes.Const(token.value in ('true', 'True'),
- lineno=token.lineno)
- elif token.value in ('none', 'None'):
+ if token.type == "name":
+ if token.value in ("true", "false", "True", "False"):
+ node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno)
+ elif token.value in ("none", "None"):
node = nodes.Const(None, lineno=token.lineno)
else:
- node = nodes.Name(token.value, 'load', lineno=token.lineno)
+ node = nodes.Name(token.value, "load", lineno=token.lineno)
next(self.stream)
- elif token.type == 'string':
+ elif token.type == "string":
next(self.stream)
buf = [token.value]
lineno = token.lineno
- while self.stream.current.type == 'string':
+ while self.stream.current.type == "string":
buf.append(self.stream.current.value)
next(self.stream)
- node = nodes.Const(''.join(buf), lineno=lineno)
- elif token.type in ('integer', 'float'):
+ node = nodes.Const("".join(buf), lineno=lineno)
+ elif token.type in ("integer", "float"):
next(self.stream)
node = nodes.Const(token.value, lineno=token.lineno)
- elif token.type == 'lparen':
+ elif token.type == "lparen":
next(self.stream)
node = self.parse_tuple(explicit_parentheses=True)
- self.stream.expect('rparen')
- elif token.type == 'lbracket':
+ self.stream.expect("rparen")
+ elif token.type == "lbracket":
node = self.parse_list()
- elif token.type == 'lbrace':
+ elif token.type == "lbrace":
node = self.parse_dict()
else:
self.fail("unexpected '%s'" % describe_token(token), token.lineno)
return node
- def parse_tuple(self, simplified=False, with_condexpr=True,
- extra_end_rules=None, explicit_parentheses=False):
+ def parse_tuple(
+ self,
+ simplified=False,
+ with_condexpr=True,
+ extra_end_rules=None,
+ explicit_parentheses=False,
+ ):
"""Works like `parse_expression` but if multiple expressions are
delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
This method could also return a regular expression instead of a tuple
@@ -615,11 +644,11 @@ class Parser(object):
is_tuple = False
while 1:
if args:
- self.stream.expect('comma')
+ self.stream.expect("comma")
if self.is_tuple_end(extra_end_rules):
break
args.append(parse())
- if self.stream.current.type == 'comma':
+ if self.stream.current.type == "comma":
is_tuple = True
else:
break
@@ -634,46 +663,48 @@ class Parser(object):
# nothing) in the spot of an expression would be an empty
# tuple.
if not explicit_parentheses:
- self.fail('Expected an expression, got \'%s\'' %
- describe_token(self.stream.current))
+ self.fail(
+ "Expected an expression, got '%s'"
+ % describe_token(self.stream.current)
+ )
- return nodes.Tuple(args, 'load', lineno=lineno)
+ return nodes.Tuple(args, "load", lineno=lineno)
def parse_list(self):
- token = self.stream.expect('lbracket')
+ token = self.stream.expect("lbracket")
items = []
- while self.stream.current.type != 'rbracket':
+ while self.stream.current.type != "rbracket":
if items:
- self.stream.expect('comma')
- if self.stream.current.type == 'rbracket':
+ self.stream.expect("comma")
+ if self.stream.current.type == "rbracket":
break
items.append(self.parse_expression())
- self.stream.expect('rbracket')
+ self.stream.expect("rbracket")
return nodes.List(items, lineno=token.lineno)
def parse_dict(self):
- token = self.stream.expect('lbrace')
+ token = self.stream.expect("lbrace")
items = []
- while self.stream.current.type != 'rbrace':
+ while self.stream.current.type != "rbrace":
if items:
- self.stream.expect('comma')
- if self.stream.current.type == 'rbrace':
+ self.stream.expect("comma")
+ if self.stream.current.type == "rbrace":
break
key = self.parse_expression()
- self.stream.expect('colon')
+ self.stream.expect("colon")
value = self.parse_expression()
items.append(nodes.Pair(key, value, lineno=key.lineno))
- self.stream.expect('rbrace')
+ self.stream.expect("rbrace")
return nodes.Dict(items, lineno=token.lineno)
def parse_postfix(self, node):
while 1:
token_type = self.stream.current.type
- if token_type == 'dot' or token_type == 'lbracket':
+ if token_type == "dot" or token_type == "lbracket":
node = self.parse_subscript(node)
# calls are valid both after postfix expressions (getattr
# and getitem) as well as filters and tests
- elif token_type == 'lparen':
+ elif token_type == "lparen":
node = self.parse_call(node)
else:
break
@@ -682,13 +713,13 @@ class Parser(object):
def parse_filter_expr(self, node):
while 1:
token_type = self.stream.current.type
- if token_type == 'pipe':
+ if token_type == "pipe":
node = self.parse_filter(node)
- elif token_type == 'name' and self.stream.current.value == 'is':
+ elif token_type == "name" and self.stream.current.value == "is":
node = self.parse_test(node)
# calls are valid both after postfix expressions (getattr
# and getitem) as well as filters and tests
- elif token_type == 'lparen':
+ elif token_type == "lparen":
node = self.parse_call(node)
else:
break
@@ -696,53 +727,54 @@ class Parser(object):
def parse_subscript(self, node):
token = next(self.stream)
- if token.type == 'dot':
+ if token.type == "dot":
attr_token = self.stream.current
next(self.stream)
- if attr_token.type == 'name':
- return nodes.Getattr(node, attr_token.value, 'load',
- lineno=token.lineno)
- elif attr_token.type != 'integer':
- self.fail('expected name or number', attr_token.lineno)
+ if attr_token.type == "name":
+ return nodes.Getattr(
+ node, attr_token.value, "load", lineno=token.lineno
+ )
+ elif attr_token.type != "integer":
+ self.fail("expected name or number", attr_token.lineno)
arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
- return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
- if token.type == 'lbracket':
+ return nodes.Getitem(node, arg, "load", lineno=token.lineno)
+ if token.type == "lbracket":
args = []
- while self.stream.current.type != 'rbracket':
+ while self.stream.current.type != "rbracket":
if args:
- self.stream.expect('comma')
+ self.stream.expect("comma")
args.append(self.parse_subscribed())
- self.stream.expect('rbracket')
+ self.stream.expect("rbracket")
if len(args) == 1:
arg = args[0]
else:
- arg = nodes.Tuple(args, 'load', lineno=token.lineno)
- return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
- self.fail('expected subscript expression', token.lineno)
+ arg = nodes.Tuple(args, "load", lineno=token.lineno)
+ return nodes.Getitem(node, arg, "load", lineno=token.lineno)
+ self.fail("expected subscript expression", token.lineno)
def parse_subscribed(self):
lineno = self.stream.current.lineno
- if self.stream.current.type == 'colon':
+ if self.stream.current.type == "colon":
next(self.stream)
args = [None]
else:
node = self.parse_expression()
- if self.stream.current.type != 'colon':
+ if self.stream.current.type != "colon":
return node
next(self.stream)
args = [node]
- if self.stream.current.type == 'colon':
+ if self.stream.current.type == "colon":
args.append(None)
- elif self.stream.current.type not in ('rbracket', 'comma'):
+ elif self.stream.current.type not in ("rbracket", "comma"):
args.append(self.parse_expression())
else:
args.append(None)
- if self.stream.current.type == 'colon':
+ if self.stream.current.type == "colon":
next(self.stream)
- if self.stream.current.type not in ('rbracket', 'comma'):
+ if self.stream.current.type not in ("rbracket", "comma"):
args.append(self.parse_expression())
else:
args.append(None)
@@ -752,7 +784,7 @@ class Parser(object):
return nodes.Slice(lineno=lineno, *args)
def parse_call(self, node):
- token = self.stream.expect('lparen')
+ token = self.stream.expect("lparen")
args = []
kwargs = []
dyn_args = dyn_kwargs = None
@@ -760,95 +792,100 @@ class Parser(object):
def ensure(expr):
if not expr:
- self.fail('invalid syntax for function call expression',
- token.lineno)
+ self.fail("invalid syntax for function call expression", token.lineno)
- while self.stream.current.type != 'rparen':
+ while self.stream.current.type != "rparen":
if require_comma:
- self.stream.expect('comma')
+ self.stream.expect("comma")
# support for trailing comma
- if self.stream.current.type == 'rparen':
+ if self.stream.current.type == "rparen":
break
- if self.stream.current.type == 'mul':
+ if self.stream.current.type == "mul":
ensure(dyn_args is None and dyn_kwargs is None)
next(self.stream)
dyn_args = self.parse_expression()
- elif self.stream.current.type == 'pow':
+ elif self.stream.current.type == "pow":
ensure(dyn_kwargs is None)
next(self.stream)
dyn_kwargs = self.parse_expression()
else:
- if self.stream.current.type == 'name' and \
- self.stream.look().type == 'assign':
+ if (
+ self.stream.current.type == "name"
+ and self.stream.look().type == "assign"
+ ):
# Parsing a kwarg
ensure(dyn_kwargs is None)
key = self.stream.current.value
self.stream.skip(2)
value = self.parse_expression()
- kwargs.append(nodes.Keyword(key, value,
- lineno=value.lineno))
+ kwargs.append(nodes.Keyword(key, value, lineno=value.lineno))
else:
# Parsing an arg
ensure(dyn_args is None and dyn_kwargs is None and not kwargs)
args.append(self.parse_expression())
require_comma = True
- self.stream.expect('rparen')
+ self.stream.expect("rparen")
if node is None:
return args, kwargs, dyn_args, dyn_kwargs
- return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
- lineno=token.lineno)
+ return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
def parse_filter(self, node, start_inline=False):
- while self.stream.current.type == 'pipe' or start_inline:
+ while self.stream.current.type == "pipe" or start_inline:
if not start_inline:
next(self.stream)
- token = self.stream.expect('name')
+ token = self.stream.expect("name")
name = token.value
- while self.stream.current.type == 'dot':
+ while self.stream.current.type == "dot":
next(self.stream)
- name += '.' + self.stream.expect('name').value
- if self.stream.current.type == 'lparen':
+ name += "." + self.stream.expect("name").value
+ if self.stream.current.type == "lparen":
args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
else:
args = []
kwargs = []
dyn_args = dyn_kwargs = None
- node = nodes.Filter(node, name, args, kwargs, dyn_args,
- dyn_kwargs, lineno=token.lineno)
+ node = nodes.Filter(
+ node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
+ )
start_inline = False
return node
def parse_test(self, node):
token = next(self.stream)
- if self.stream.current.test('name:not'):
+ if self.stream.current.test("name:not"):
next(self.stream)
negated = True
else:
negated = False
- name = self.stream.expect('name').value
- while self.stream.current.type == 'dot':
+ name = self.stream.expect("name").value
+ while self.stream.current.type == "dot":
next(self.stream)
- name += '.' + self.stream.expect('name').value
+ name += "." + self.stream.expect("name").value
dyn_args = dyn_kwargs = None
kwargs = []
- if self.stream.current.type == 'lparen':
+ if self.stream.current.type == "lparen":
args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
- elif (self.stream.current.type in ('name', 'string', 'integer',
- 'float', 'lparen', 'lbracket',
- 'lbrace') and not
- self.stream.current.test_any('name:else', 'name:or',
- 'name:and')):
- if self.stream.current.test('name:is'):
- self.fail('You cannot chain multiple tests with is')
+ elif self.stream.current.type in (
+ "name",
+ "string",
+ "integer",
+ "float",
+ "lparen",
+ "lbracket",
+ "lbrace",
+ ) and not self.stream.current.test_any("name:else", "name:or", "name:and"):
+ if self.stream.current.test("name:is"):
+ self.fail("You cannot chain multiple tests with is")
arg_node = self.parse_primary()
arg_node = self.parse_postfix(arg_node)
args = [arg_node]
else:
args = []
- node = nodes.Test(node, name, args, kwargs, dyn_args,
- dyn_kwargs, lineno=token.lineno)
+ node = nodes.Test(
+ node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
+ )
if negated:
node = nodes.Not(node, lineno=token.lineno)
return node
@@ -870,29 +907,29 @@ class Parser(object):
try:
while self.stream:
token = self.stream.current
- if token.type == 'data':
+ if token.type == "data":
if token.value:
- add_data(nodes.TemplateData(token.value,
- lineno=token.lineno))
+ add_data(nodes.TemplateData(token.value, lineno=token.lineno))
next(self.stream)
- elif token.type == 'variable_begin':
+ elif token.type == "variable_begin":
next(self.stream)
add_data(self.parse_tuple(with_condexpr=True))
- self.stream.expect('variable_end')
- elif token.type == 'block_begin':
+ self.stream.expect("variable_end")
+ elif token.type == "block_begin":
flush_data()
next(self.stream)
- if end_tokens is not None and \
- self.stream.current.test_any(*end_tokens):
+ if end_tokens is not None and self.stream.current.test_any(
+ *end_tokens
+ ):
return body
rv = self.parse_statement()
if isinstance(rv, list):
body.extend(rv)
else:
body.append(rv)
- self.stream.expect('block_end')
+ self.stream.expect("block_end")
else:
- raise AssertionError('internal parsing error')
+ raise AssertionError("internal parsing error")
flush_data()
finally:
diff --git a/src/jinja2/runtime.py b/src/jinja2/runtime.py
index 7ee9927..9f4f224 100644
--- a/src/jinja2/runtime.py
+++ b/src/jinja2/runtime.py
@@ -36,10 +36,23 @@ from .utils import object_type_repr
from .utils import soft_unicode
# these variables are exported to the template runtime
-__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
- 'TemplateRuntimeError', 'missing', 'concat', 'escape',
- 'markup_join', 'unicode_join', 'to_string', 'identity',
- 'TemplateNotFound', 'Namespace', 'Undefined']
+__all__ = [
+ "LoopContext",
+ "TemplateReference",
+ "Macro",
+ "Markup",
+ "TemplateRuntimeError",
+ "missing",
+ "concat",
+ "escape",
+ "markup_join",
+ "unicode_join",
+ "to_string",
+ "identity",
+ "TemplateNotFound",
+ "Namespace",
+ "Undefined",
+]
#: the name of the function that is used to convert something into
#: a string. We can just use the text type here.
@@ -58,8 +71,8 @@ def markup_join(seq):
iterator = imap(soft_unicode, seq)
for arg in iterator:
buf.append(arg)
- if hasattr(arg, '__html__'):
- return Markup(u'').join(chain(buf, iterator))
+ if hasattr(arg, "__html__"):
+ return Markup(u"").join(chain(buf, iterator))
return concat(buf)
@@ -68,8 +81,15 @@ def unicode_join(seq):
return concat(imap(text_type, seq))
-def new_context(environment, template_name, blocks, vars=None,
- shared=None, globals=None, locals=None):
+def new_context(
+ environment,
+ template_name,
+ blocks,
+ vars=None,
+ shared=None,
+ globals=None,
+ locals=None,
+):
"""Internal helper for context creation."""
if vars is None:
vars = {}
@@ -85,8 +105,7 @@ def new_context(environment, template_name, blocks, vars=None,
for key, value in iteritems(locals):
if value is not missing:
parent[key] = value
- return environment.context_class(environment, parent, template_name,
- blocks)
+ return environment.context_class(environment, parent, template_name, blocks)
class TemplateReference(object):
@@ -100,18 +119,14 @@ class TemplateReference(object):
return BlockReference(name, self.__context, blocks, 0)
def __repr__(self):
- return '<%s %r>' % (
- self.__class__.__name__,
- self.__context.name
- )
+ return "<%s %r>" % (self.__class__.__name__, self.__context.name)
def _get_func(x):
- return getattr(x, '__func__', x)
+ return getattr(x, "__func__", x)
class ContextMeta(type):
-
def __new__(cls, name, bases, d):
rv = type.__new__(cls, name, bases, d)
if bases == ():
@@ -124,11 +139,15 @@ class ContextMeta(type):
# If we have a changed resolve but no changed default or missing
# resolve we invert the call logic.
- if resolve is not default_resolve and \
- resolve_or_missing is default_resolve_or_missing:
+ if (
+ resolve is not default_resolve
+ and resolve_or_missing is default_resolve_or_missing
+ ):
rv._legacy_resolve_mode = True
- elif resolve is default_resolve and \
- resolve_or_missing is default_resolve_or_missing:
+ elif (
+ resolve is default_resolve
+ and resolve_or_missing is default_resolve_or_missing
+ ):
rv._fast_resolve_mode = True
return rv
@@ -161,6 +180,7 @@ class Context(with_metaclass(ContextMeta)):
method that doesn't fail with a `KeyError` but returns an
:class:`Undefined` object for missing variables.
"""
+
# XXX: we want to eventually make this be a deprecation warning and
# remove it.
_legacy_resolve_mode = False
@@ -191,9 +211,9 @@ class Context(with_metaclass(ContextMeta)):
index = blocks.index(current) + 1
blocks[index]
except LookupError:
- return self.environment.undefined('there is no parent block '
- 'called %r.' % name,
- name='super')
+ return self.environment.undefined(
+ "there is no parent block called %r." % name, name="super"
+ )
return BlockReference(name, self, blocks, index)
def get(self, key, default=None):
@@ -254,36 +274,41 @@ class Context(with_metaclass(ContextMeta)):
__traceback_hide__ = True # noqa
# Allow callable classes to take a context
- if hasattr(__obj, '__call__'):
+ if hasattr(__obj, "__call__"):
fn = __obj.__call__
- for fn_type in ('contextfunction',
- 'evalcontextfunction',
- 'environmentfunction'):
+ for fn_type in (
+ "contextfunction",
+ "evalcontextfunction",
+ "environmentfunction",
+ ):
if hasattr(fn, fn_type):
__obj = fn
break
if callable(__obj):
- if getattr(__obj, 'contextfunction', 0):
+ if getattr(__obj, "contextfunction", 0):
args = (__self,) + args
- elif getattr(__obj, 'evalcontextfunction', 0):
+ elif getattr(__obj, "evalcontextfunction", 0):
args = (__self.eval_ctx,) + args
- elif getattr(__obj, 'environmentfunction', 0):
+ elif getattr(__obj, "environmentfunction", 0):
args = (__self.environment,) + args
try:
return __obj(*args, **kwargs)
except StopIteration:
- return __self.environment.undefined('value was undefined because '
- 'a callable raised a '
- 'StopIteration exception')
+ return __self.environment.undefined(
+ "value was undefined because "
+ "a callable raised a "
+ "StopIteration exception"
+ )
def derived(self, locals=None):
"""Internal helper function to create a derived context. This is
used in situations where the system needs a new context in the same
template that is independent.
"""
- context = new_context(self.environment, self.name, {},
- self.get_all(), True, None, locals)
+ context = new_context(
+ self.environment, self.name, {}, self.get_all(), True, None, locals
+ )
context.eval_ctx = self.eval_ctx
context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks))
return context
@@ -294,15 +319,15 @@ class Context(with_metaclass(ContextMeta)):
proxy.__name__ = meth
return proxy
- keys = _all('keys')
- values = _all('values')
- items = _all('items')
+ keys = _all("keys")
+ values = _all("values")
+ items = _all("items")
# not available on python 3
if PY2:
- iterkeys = _all('iterkeys')
- itervalues = _all('itervalues')
- iteritems = _all('iteritems')
+ iterkeys = _all("iterkeys")
+ itervalues = _all("itervalues")
+ iteritems = _all("iteritems")
del _all
def __contains__(self, name):
@@ -318,10 +343,10 @@ class Context(with_metaclass(ContextMeta)):
return item
def __repr__(self):
- return '<%s %s of %r>' % (
+ return "<%s %s of %r>" % (
self.__class__.__name__,
repr(self.get_all()),
- self.name
+ self.name,
)
@@ -341,11 +366,10 @@ class BlockReference(object):
def super(self):
"""Super the block."""
if self._depth + 1 >= len(self._stack):
- return self._context.environment. \
- undefined('there is no parent block called %r.' %
- self.name, name='super')
- return BlockReference(self.name, self._context, self._stack,
- self._depth + 1)
+ return self._context.environment.undefined(
+ "there is no parent block called %r." % self.name, name="super"
+ )
+ return BlockReference(self.name, self._context, self._stack, self._depth + 1)
@internalcode
def __call__(self):
@@ -538,8 +562,7 @@ class LoopContext:
"""
if self._recurse is None:
raise TypeError(
- "The loop must have the 'recursive' marker to be"
- " called recursively."
+ "The loop must have the 'recursive' marker to be called recursively."
)
return self._recurse(iterable, self._recurse, depth=self.depth)
@@ -551,9 +574,17 @@ class LoopContext:
class Macro(object):
"""Wraps a macro function."""
- def __init__(self, environment, func, name, arguments,
- catch_kwargs, catch_varargs, caller,
- default_autoescape=None):
+ def __init__(
+ self,
+ environment,
+ func,
+ name,
+ arguments,
+ catch_kwargs,
+ catch_varargs,
+ caller,
+ default_autoescape=None,
+ ):
self._environment = environment
self._func = func
self._argument_count = len(arguments)
@@ -562,7 +593,7 @@ class Macro(object):
self.catch_kwargs = catch_kwargs
self.catch_varargs = catch_varargs
self.caller = caller
- self.explicit_caller = 'caller' in arguments
+ self.explicit_caller = "caller" in arguments
if default_autoescape is None:
default_autoescape = environment.autoescape
self._default_autoescape = default_autoescape
@@ -593,7 +624,7 @@ class Macro(object):
autoescape = self._default_autoescape
# try to consume the positional arguments
- arguments = list(args[:self._argument_count])
+ arguments = list(args[: self._argument_count])
off = len(arguments)
# For information why this is necessary refer to the handling
@@ -604,12 +635,12 @@ class Macro(object):
# arguments expected we start filling in keyword arguments
# and defaults.
if off != self._argument_count:
- for idx, name in enumerate(self.arguments[len(arguments):]):
+ for idx, name in enumerate(self.arguments[len(arguments) :]):
try:
value = kwargs.pop(name)
except KeyError:
value = missing
- if name == 'caller':
+ if name == "caller":
found_caller = True
arguments.append(value)
else:
@@ -619,26 +650,31 @@ class Macro(object):
# if not also changed in the compiler's `function_scoping` method.
# the order is caller, keyword arguments, positional arguments!
if self.caller and not found_caller:
- caller = kwargs.pop('caller', None)
+ caller = kwargs.pop("caller", None)
if caller is None:
- caller = self._environment.undefined('No caller defined',
- name='caller')
+ caller = self._environment.undefined("No caller defined", name="caller")
arguments.append(caller)
if self.catch_kwargs:
arguments.append(kwargs)
elif kwargs:
- if 'caller' in kwargs:
- raise TypeError('macro %r was invoked with two values for '
- 'the special caller argument. This is '
- 'most likely a bug.' % self.name)
- raise TypeError('macro %r takes no keyword argument %r' %
- (self.name, next(iter(kwargs))))
+ if "caller" in kwargs:
+ raise TypeError(
+ "macro %r was invoked with two values for "
+ "the special caller argument. This is "
+ "most likely a bug." % self.name
+ )
+ raise TypeError(
+ "macro %r takes no keyword argument %r"
+ % (self.name, next(iter(kwargs)))
+ )
if self.catch_varargs:
- arguments.append(args[self._argument_count:])
+ arguments.append(args[self._argument_count :])
elif len(args) > self._argument_count:
- raise TypeError('macro %r takes not more than %d argument(s)' %
- (self.name, len(self.arguments)))
+ raise TypeError(
+ "macro %r takes not more than %d argument(s)"
+ % (self.name, len(self.arguments))
+ )
return self._invoke(arguments, autoescape)
@@ -650,9 +686,9 @@ class Macro(object):
return rv
def __repr__(self):
- return '<%s %s>' % (
+ return "<%s %s>" % (
self.__class__.__name__,
- self.name is None and 'anonymous' or repr(self.name)
+ self.name is None and "anonymous" or repr(self.name),
)
@@ -671,8 +707,13 @@ class Undefined(object):
...
jinja2.exceptions.UndefinedError: 'foo' is undefined
"""
- __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
- '_undefined_exception')
+
+ __slots__ = (
+ "_undefined_hint",
+ "_undefined_obj",
+ "_undefined_name",
+ "_undefined_exception",
+ )
def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError):
self._undefined_hint = hint
@@ -689,17 +730,17 @@ class Undefined(object):
return self._undefined_hint
if self._undefined_obj is missing:
- return '%r is undefined' % self._undefined_name
+ return "%r is undefined" % self._undefined_name
if not isinstance(self._undefined_name, string_types):
- return '%s has no element %r' % (
+ return "%s has no element %r" % (
object_type_repr(self._undefined_obj),
- self._undefined_name
+ self._undefined_name,
)
- return '%r has no attribute %r' % (
+ return "%r has no attribute %r" % (
object_type_repr(self._undefined_obj),
- self._undefined_name
+ self._undefined_name,
)
@internalcode
@@ -711,16 +752,55 @@ class Undefined(object):
@internalcode
def __getattr__(self, name):
- if name[:2] == '__':
+ if name[:2] == "__":
raise AttributeError(name)
return self._fail_with_undefined_error()
- __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
- __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
- __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
- __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \
- __float__ = __complex__ = __pow__ = __rpow__ = __sub__ = \
- __rsub__ = _fail_with_undefined_error
+ __add__ = (
+ __radd__
+ ) = (
+ __mul__
+ ) = (
+ __rmul__
+ ) = (
+ __div__
+ ) = (
+ __rdiv__
+ ) = (
+ __truediv__
+ ) = (
+ __rtruediv__
+ ) = (
+ __floordiv__
+ ) = (
+ __rfloordiv__
+ ) = (
+ __mod__
+ ) = (
+ __rmod__
+ ) = (
+ __pos__
+ ) = (
+ __neg__
+ ) = (
+ __call__
+ ) = (
+ __getitem__
+ ) = (
+ __lt__
+ ) = (
+ __le__
+ ) = (
+ __gt__
+ ) = (
+ __ge__
+ ) = (
+ __int__
+ ) = (
+ __float__
+ ) = (
+ __complex__
+ ) = __pow__ = __rpow__ = __sub__ = __rsub__ = _fail_with_undefined_error
def __eq__(self, other):
return type(self) is type(other)
@@ -732,7 +812,7 @@ class Undefined(object):
return id(type(self))
def __str__(self):
- return u''
+ return u""
def __len__(self):
return 0
@@ -743,10 +823,11 @@ class Undefined(object):
def __nonzero__(self):
return False
+
__bool__ = __nonzero__
def __repr__(self):
- return 'Undefined'
+ return "Undefined"
def make_logging_undefined(logger=None, base=None):
@@ -771,6 +852,7 @@ def make_logging_undefined(logger=None, base=None):
"""
if logger is None:
import logging
+
logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler(sys.stderr))
if base is None:
@@ -779,26 +861,27 @@ def make_logging_undefined(logger=None, base=None):
def _log_message(undef):
if undef._undefined_hint is None:
if undef._undefined_obj is missing:
- hint = '%s is undefined' % undef._undefined_name
+ hint = "%s is undefined" % undef._undefined_name
elif not isinstance(undef._undefined_name, string_types):
- hint = '%s has no element %s' % (
+ hint = "%s has no element %s" % (
object_type_repr(undef._undefined_obj),
- undef._undefined_name)
+ undef._undefined_name,
+ )
else:
- hint = '%s has no attribute %s' % (
+ hint = "%s has no attribute %s" % (
object_type_repr(undef._undefined_obj),
- undef._undefined_name)
+ undef._undefined_name,
+ )
else:
hint = undef._undefined_hint
- logger.warning('Template variable warning: %s', hint)
+ logger.warning("Template variable warning: %s", hint)
class LoggingUndefined(base):
-
def _fail_with_undefined_error(self, *args, **kwargs):
try:
return base._fail_with_undefined_error(self, *args, **kwargs)
except self._undefined_exception as e:
- logger.error('Template variable error: %s', str(e))
+ logger.error("Template variable error: %s", str(e))
raise e
def __str__(self):
@@ -812,6 +895,7 @@ def make_logging_undefined(logger=None, base=None):
return rv
if PY2:
+
def __nonzero__(self):
rv = base.__nonzero__(self)
_log_message(self)
@@ -821,7 +905,9 @@ def make_logging_undefined(logger=None, base=None):
rv = base.__unicode__(self)
_log_message(self)
return rv
+
else:
+
def __bool__(self):
rv = base.__bool__(self)
_log_message(self)
@@ -848,6 +934,7 @@ class ChainableUndefined(Undefined):
.. versionadded:: 2.11.0
"""
+
__slots__ = ()
def __html__(self):
@@ -873,17 +960,18 @@ class DebugUndefined(Undefined):
...
jinja2.exceptions.UndefinedError: 'foo' is undefined
"""
+
__slots__ = ()
def __str__(self):
if self._undefined_hint is None:
if self._undefined_obj is missing:
- return u'{{ %s }}' % self._undefined_name
- return '{{ no such element: %s[%r] }}' % (
+ return u"{{ %s }}" % self._undefined_name
+ return "{{ no such element: %s[%r] }}" % (
object_type_repr(self._undefined_obj),
- self._undefined_name
+ self._undefined_name,
)
- return u'{{ undefined value printed: %s }}' % self._undefined_hint
+ return u"{{ undefined value printed: %s }}" % self._undefined_hint
@implements_to_string
@@ -906,13 +994,22 @@ class StrictUndefined(Undefined):
...
jinja2.exceptions.UndefinedError: 'foo' is undefined
"""
+
__slots__ = ()
- __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \
- __ne__ = __bool__ = __hash__ = \
- Undefined._fail_with_undefined_error
+ __iter__ = (
+ __str__
+ ) = (
+ __len__
+ ) = (
+ __nonzero__
+ ) = __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
# remove remaining slots attributes, after the metaclass did the magic they
# are unneeded and irritating as they contain wrong data for the subclasses.
-del Undefined.__slots__, ChainableUndefined.__slots__, \
- DebugUndefined.__slots__, StrictUndefined.__slots__
+del (
+ Undefined.__slots__,
+ ChainableUndefined.__slots__,
+ DebugUndefined.__slots__,
+ StrictUndefined.__slots__,
+)
diff --git a/src/jinja2/sandbox.py b/src/jinja2/sandbox.py
index a614c27..ad87997 100644
--- a/src/jinja2/sandbox.py
+++ b/src/jinja2/sandbox.py
@@ -31,8 +31,9 @@ MAX_RANGE = 100000
#: attributes of function objects that are considered unsafe.
if PY2:
- UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
- 'func_defaults', 'func_globals'])
+ UNSAFE_FUNCTION_ATTRIBUTES = set(
+ ["func_closure", "func_code", "func_dict", "func_defaults", "func_globals"]
+ )
else:
# On versions > python 2 the special attributes on functions are gone,
# but they remain on methods and generators for whatever reason.
@@ -40,22 +41,23 @@ else:
#: unsafe method attributes. function attributes are unsafe for methods too
-UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
+UNSAFE_METHOD_ATTRIBUTES = set(["im_class", "im_func", "im_self"])
#: unsafe generator attributes.
-UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code'])
+UNSAFE_GENERATOR_ATTRIBUTES = set(["gi_frame", "gi_code"])
#: unsafe attributes on coroutines
-UNSAFE_COROUTINE_ATTRIBUTES = set(['cr_frame', 'cr_code'])
+UNSAFE_COROUTINE_ATTRIBUTES = set(["cr_frame", "cr_code"])
#: unsafe attributes on async generators
-UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(['ag_code', 'ag_frame'])
+UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(["ag_code", "ag_frame"])
import warnings
# make sure we don't warn in python 2.6 about stuff we don't care about
-warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
- module='jinja2.sandbox')
+warnings.filterwarnings(
+ "ignore", "the sets module", DeprecationWarning, module="jinja2.sandbox"
+)
from collections import deque
@@ -68,6 +70,7 @@ _mutable_sequence_types = (list,)
try:
from UserDict import UserDict, DictMixin
from UserList import UserList
+
_mutable_mapping_types += (UserDict, DictMixin)
_mutable_set_types += (UserList,)
except ImportError:
@@ -76,6 +79,7 @@ except ImportError:
# if sets is still available, register the mutable set from there as well
try:
from sets import Set
+
_mutable_set_types += (Set,)
except ImportError:
pass
@@ -87,20 +91,45 @@ _mutable_sequence_types += (abc.MutableSequence,)
_mutable_spec = (
- (_mutable_set_types, frozenset([
- 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
- 'symmetric_difference_update', 'update'
- ])),
- (_mutable_mapping_types, frozenset([
- 'clear', 'pop', 'popitem', 'setdefault', 'update'
- ])),
- (_mutable_sequence_types, frozenset([
- 'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
- ])),
- (deque, frozenset([
- 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
- 'popleft', 'remove', 'rotate'
- ]))
+ (
+ _mutable_set_types,
+ frozenset(
+ [
+ "add",
+ "clear",
+ "difference_update",
+ "discard",
+ "pop",
+ "remove",
+ "symmetric_difference_update",
+ "update",
+ ]
+ ),
+ ),
+ (
+ _mutable_mapping_types,
+ frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
+ ),
+ (
+ _mutable_sequence_types,
+ frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
+ ),
+ (
+ deque,
+ frozenset(
+ [
+ "append",
+ "appendleft",
+ "clear",
+ "extend",
+ "extendleft",
+ "pop",
+ "popleft",
+ "remove",
+ "rotate",
+ ]
+ ),
+ ),
)
@@ -118,7 +147,7 @@ class _MagicFormatMapping(abc.Mapping):
self._last_index = 0
def __getitem__(self, key):
- if key == '':
+ if key == "":
idx = self._last_index
self._last_index += 1
try:
@@ -136,9 +165,9 @@ class _MagicFormatMapping(abc.Mapping):
def inspect_format_method(callable):
- if not isinstance(callable, (types.MethodType,
- types.BuiltinMethodType)) or \
- callable.__name__ not in ('format', 'format_map'):
+ if not isinstance(
+ callable, (types.MethodType, types.BuiltinMethodType)
+ ) or callable.__name__ not in ("format", "format_map"):
return None
obj = callable.__self__
if isinstance(obj, string_types):
@@ -189,24 +218,25 @@ def is_internal_attribute(obj, attr):
if attr in UNSAFE_FUNCTION_ATTRIBUTES:
return True
elif isinstance(obj, types.MethodType):
- if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
- attr in UNSAFE_METHOD_ATTRIBUTES:
+ if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
return True
elif isinstance(obj, type):
- if attr == 'mro':
+ if attr == "mro":
return True
elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
return True
elif isinstance(obj, types.GeneratorType):
if attr in UNSAFE_GENERATOR_ATTRIBUTES:
return True
- elif hasattr(types, 'CoroutineType') and isinstance(obj, types.CoroutineType):
+ elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
if attr in UNSAFE_COROUTINE_ATTRIBUTES:
return True
- elif hasattr(types, 'AsyncGeneratorType') and isinstance(obj, types.AsyncGeneratorType):
+ elif hasattr(types, "AsyncGeneratorType") and isinstance(
+ obj, types.AsyncGeneratorType
+ ):
if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
return True
- return attr.startswith('__')
+ return attr.startswith("__")
def modifies_known_mutable(obj, attr):
@@ -247,28 +277,26 @@ class SandboxedEnvironment(Environment):
raised. However also other exceptions may occur during the rendering so
the caller has to ensure that all exceptions are caught.
"""
+
sandboxed = True
#: default callback table for the binary operators. A copy of this is
#: available on each instance of a sandboxed environment as
#: :attr:`binop_table`
default_binop_table = {
- '+': operator.add,
- '-': operator.sub,
- '*': operator.mul,
- '/': operator.truediv,
- '//': operator.floordiv,
- '**': operator.pow,
- '%': operator.mod
+ "+": operator.add,
+ "-": operator.sub,
+ "*": operator.mul,
+ "/": operator.truediv,
+ "//": operator.floordiv,
+ "**": operator.pow,
+ "%": operator.mod,
}
#: default callback table for the unary operators. A copy of this is
#: available on each instance of a sandboxed environment as
#: :attr:`unop_table`
- default_unop_table = {
- '+': operator.pos,
- '-': operator.neg
- }
+ default_unop_table = {"+": operator.pos, "-": operator.neg}
#: a set of binary operators that should be intercepted. Each operator
#: that is added to this set (empty by default) is delegated to the
@@ -318,10 +346,9 @@ class SandboxedEnvironment(Environment):
"""
return False
-
def __init__(self, *args, **kwargs):
Environment.__init__(self, *args, **kwargs)
- self.globals['range'] = safe_range
+ self.globals["range"] = safe_range
self.binop_table = self.default_binop_table.copy()
self.unop_table = self.default_unop_table.copy()
@@ -332,7 +359,7 @@ class SandboxedEnvironment(Environment):
special attributes of internal python objects as returned by the
:func:`is_internal_attribute` function.
"""
- return not (attr.startswith('_') or is_internal_attribute(obj, attr))
+ return not (attr.startswith("_") or is_internal_attribute(obj, attr))
def is_safe_callable(self, obj):
"""Check if an object is safely callable. Per default a function is
@@ -340,8 +367,9 @@ class SandboxedEnvironment(Environment):
True. Override this method to alter the behavior, but this won't
affect the `unsafe` decorator from this module.
"""
- return not (getattr(obj, 'unsafe_callable', False) or
- getattr(obj, 'alters_data', False))
+ return not (
+ getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
+ )
def call_binop(self, context, operator, left, right):
"""For intercepted binary operator calls (:meth:`intercepted_binops`)
@@ -401,11 +429,13 @@ class SandboxedEnvironment(Environment):
def unsafe_undefined(self, obj, attribute):
"""Return an undefined object for unsafe attributes."""
- return self.undefined('access to attribute %r of %r '
- 'object is unsafe.' % (
- attribute,
- obj.__class__.__name__
- ), name=attribute, obj=obj, exc=SecurityError)
+ return self.undefined(
+ "access to attribute %r of %r "
+ "object is unsafe." % (attribute, obj.__class__.__name__),
+ name=attribute,
+ obj=obj,
+ exc=SecurityError,
+ )
def format_string(self, s, args, kwargs, format_func=None):
"""If a format call is detected, then this is routed through this
@@ -416,10 +446,10 @@ class SandboxedEnvironment(Environment):
else:
formatter = SandboxedFormatter(self)
- if format_func is not None and format_func.__name__ == 'format_map':
+ if format_func is not None and format_func.__name__ == "format_map":
if len(args) != 1 or kwargs:
raise TypeError(
- 'format_map() takes exactly one argument %d given'
+ "format_map() takes exactly one argument %d given"
% (len(args) + (kwargs is not None))
)
@@ -439,7 +469,7 @@ class SandboxedEnvironment(Environment):
# the double prefixes are to avoid double keyword argument
# errors when proxying the call.
if not __self.is_safe_callable(__obj):
- raise SecurityError('%r is not safely callable' % (__obj,))
+ raise SecurityError("%r is not safely callable" % (__obj,))
return __context.call(__obj, *args, **kwargs)
@@ -459,12 +489,12 @@ class ImmutableSandboxedEnvironment(SandboxedEnvironment):
try:
from _string import formatter_field_name_split
except ImportError:
+
def formatter_field_name_split(field_name):
return field_name._formatter_field_name_split()
class SandboxedFormatterMixin(object):
-
def __init__(self, env):
self._env = env
@@ -478,14 +508,14 @@ class SandboxedFormatterMixin(object):
obj = self._env.getitem(obj, i)
return obj, first
-class SandboxedFormatter(SandboxedFormatterMixin, Formatter):
+class SandboxedFormatter(SandboxedFormatterMixin, Formatter):
def __init__(self, env):
SandboxedFormatterMixin.__init__(self, env)
Formatter.__init__(self)
-class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
+class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
def __init__(self, env, escape):
SandboxedFormatterMixin.__init__(self, env)
EscapeFormatter.__init__(self, escape)
diff --git a/src/jinja2/tests.py b/src/jinja2/tests.py
index de0f105..3eea947 100644
--- a/src/jinja2/tests.py
+++ b/src/jinja2/tests.py
@@ -18,7 +18,7 @@ from ._compat import string_types
from ._compat import text_type
from .runtime import Undefined
-number_re = re.compile(r'^-?\d+(\.\d+)?$')
+number_re = re.compile(r"^-?\d+(\.\d+)?$")
regex_type = type(number_re)
test_callable = callable
@@ -171,7 +171,7 @@ def test_iterable(value):
def test_escaped(value):
"""Check if the value is escaped."""
- return hasattr(value, '__html__')
+ return hasattr(value, "__html__")
def test_in(value, seq):
@@ -183,41 +183,41 @@ def test_in(value, seq):
TESTS = {
- 'odd': test_odd,
- 'even': test_even,
- 'divisibleby': test_divisibleby,
- 'defined': test_defined,
- 'undefined': test_undefined,
- 'none': test_none,
- 'boolean': test_boolean,
- 'false': test_false,
- 'true': test_true,
- 'integer': test_integer,
- 'float': test_float,
- 'lower': test_lower,
- 'upper': test_upper,
- 'string': test_string,
- 'mapping': test_mapping,
- 'number': test_number,
- 'sequence': test_sequence,
- 'iterable': test_iterable,
- 'callable': test_callable,
- 'sameas': test_sameas,
- 'escaped': test_escaped,
- 'in': test_in,
- '==': operator.eq,
- 'eq': operator.eq,
- 'equalto': operator.eq,
- '!=': operator.ne,
- 'ne': operator.ne,
- '>': operator.gt,
- 'gt': operator.gt,
- 'greaterthan': operator.gt,
- 'ge': operator.ge,
- '>=': operator.ge,
- '<': operator.lt,
- 'lt': operator.lt,
- 'lessthan': operator.lt,
- '<=': operator.le,
- 'le': operator.le,
+ "odd": test_odd,
+ "even": test_even,
+ "divisibleby": test_divisibleby,
+ "defined": test_defined,
+ "undefined": test_undefined,
+ "none": test_none,
+ "boolean": test_boolean,
+ "false": test_false,
+ "true": test_true,
+ "integer": test_integer,
+ "float": test_float,
+ "lower": test_lower,
+ "upper": test_upper,
+ "string": test_string,
+ "mapping": test_mapping,
+ "number": test_number,
+ "sequence": test_sequence,
+ "iterable": test_iterable,
+ "callable": test_callable,
+ "sameas": test_sameas,
+ "escaped": test_escaped,
+ "in": test_in,
+ "==": operator.eq,
+ "eq": operator.eq,
+ "equalto": operator.eq,
+ "!=": operator.ne,
+ "ne": operator.ne,
+ ">": operator.gt,
+ "gt": operator.gt,
+ "greaterthan": operator.gt,
+ "ge": operator.ge,
+ ">=": operator.ge,
+ "<": operator.lt,
+ "lt": operator.lt,
+ "lessthan": operator.lt,
+ "<=": operator.le,
+ "le": operator.le,
}
diff --git a/src/jinja2/utils.py b/src/jinja2/utils.py
index de36eef..c051f30 100644
--- a/src/jinja2/utils.py
+++ b/src/jinja2/utils.py
@@ -20,28 +20,29 @@ from ._compat import string_types
from ._compat import text_type
from ._compat import url_quote
-_word_split_re = re.compile(r'(\s+)')
+_word_split_re = re.compile(r"(\s+)")
_punctuation_re = re.compile(
- '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
- '|'.join(map(re.escape, ('(', '<', '&lt;'))),
- '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
+ "^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$"
+ % (
+ "|".join(map(re.escape, ("(", "<", "&lt;"))),
+ "|".join(map(re.escape, (".", ",", ")", ">", "\n", "&gt;"))),
)
)
-_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
-_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
-_entity_re = re.compile(r'&([^;]+);')
-_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
-_digits = '0123456789'
+_simple_email_re = re.compile(r"^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$")
+_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
+_entity_re = re.compile(r"&([^;]+);")
+_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+_digits = "0123456789"
# special singleton representing missing values for the runtime
-missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
+missing = type("MissingType", (), {"__repr__": lambda x: "missing"})()
# internal code
internal_code = set()
-concat = u''.join
+concat = u"".join
-_slash_escape = '\\/' not in json.dumps('/')
+_slash_escape = "\\/" not in json.dumps("/")
def contextfunction(f):
@@ -102,6 +103,7 @@ def is_undefined(obj):
return var
"""
from jinja2.runtime import Undefined
+
return isinstance(obj, Undefined)
@@ -119,6 +121,7 @@ def clear_caches():
"""
from jinja2.environment import _spontaneous_environments
from jinja2.lexer import _lexer_cache
+
_spontaneous_environments.clear()
_lexer_cache.clear()
@@ -135,10 +138,10 @@ def import_string(import_name, silent=False):
:return: imported object
"""
try:
- if ':' in import_name:
- module, obj = import_name.split(':', 1)
- elif '.' in import_name:
- module, _, obj = import_name.rpartition('.')
+ if ":" in import_name:
+ module, obj = import_name.split(":", 1)
+ elif "." in import_name:
+ module, _, obj = import_name.rpartition(".")
else:
return __import__(import_name)
return getattr(__import__(module, None, None, [obj]), obj)
@@ -147,7 +150,7 @@ def import_string(import_name, silent=False):
raise
-def open_if_exists(filename, mode='rb'):
+def open_if_exists(filename, mode="rb"):
"""Returns a file descriptor for the filename if that file exists,
otherwise ``None``.
"""
@@ -163,15 +166,15 @@ def object_type_repr(obj):
example for `None` and `Ellipsis`).
"""
if obj is None:
- return 'None'
+ return "None"
elif obj is Ellipsis:
- return 'Ellipsis'
+ return "Ellipsis"
# __builtin__ in 2.x, builtins in 3.x
- if obj.__class__.__module__ in ('__builtin__', 'builtins'):
+ if obj.__class__.__module__ in ("__builtin__", "builtins"):
name = obj.__class__.__name__
else:
- name = obj.__class__.__module__ + '.' + obj.__class__.__name__
- return '%s object' % name
+ name = obj.__class__.__module__ + "." + obj.__class__.__name__
+ return "%s object" % name
def pformat(obj, verbose=False):
@@ -180,9 +183,11 @@ def pformat(obj, verbose=False):
"""
try:
from pretty import pretty
+
return pretty(obj, verbose=verbose)
except ImportError:
from pprint import pformat
+
return pformat(obj)
@@ -200,45 +205,61 @@ def urlize(text, trim_url_limit=None, rel=None, target=None):
If target is not None, a target attribute will be added to the link.
"""
- trim_url = lambda x, limit=trim_url_limit: limit is not None \
- and (x[:limit] + (len(x) >=limit and '...'
- or '')) or x
+ trim_url = (
+ lambda x, limit=trim_url_limit: limit is not None
+ and (x[:limit] + (len(x) >= limit and "..." or ""))
+ or x
+ )
words = _word_split_re.split(text_type(escape(text)))
- rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or ''
- target_attr = target and ' target="%s"' % escape(target) or ''
+ rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or ""
+ target_attr = target and ' target="%s"' % escape(target) or ""
for i, word in enumerate(words):
match = _punctuation_re.match(word)
if match:
lead, middle, trail = match.groups()
- if middle.startswith('www.') or (
- '@' not in middle and
- not middle.startswith('http://') and
- not middle.startswith('https://') and
- len(middle) > 0 and
- middle[0] in _letters + _digits and (
- middle.endswith('.org') or
- middle.endswith('.net') or
- middle.endswith('.com')
- )):
- middle = '<a href="http://%s"%s%s>%s</a>' % (middle,
- rel_attr, target_attr, trim_url(middle))
- if middle.startswith('http://') or \
- middle.startswith('https://'):
- middle = '<a href="%s"%s%s>%s</a>' % (middle,
- rel_attr, target_attr, trim_url(middle))
- if '@' in middle and not middle.startswith('www.') and \
- not ':' in middle and _simple_email_re.match(middle):
+ if middle.startswith("www.") or (
+ "@" not in middle
+ and not middle.startswith("http://")
+ and not middle.startswith("https://")
+ and len(middle) > 0
+ and middle[0] in _letters + _digits
+ and (
+ middle.endswith(".org")
+ or middle.endswith(".net")
+ or middle.endswith(".com")
+ )
+ ):
+ middle = '<a href="http://%s"%s%s>%s</a>' % (
+ middle,
+ rel_attr,
+ target_attr,
+ trim_url(middle),
+ )
+ if middle.startswith("http://") or middle.startswith("https://"):
+ middle = '<a href="%s"%s%s>%s</a>' % (
+ middle,
+ rel_attr,
+ target_attr,
+ trim_url(middle),
+ )
+ if (
+ "@" in middle
+ and not middle.startswith("www.")
+ and not ":" in middle
+ and _simple_email_re.match(middle)
+ ):
middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
if lead + middle + trail != word:
words[i] = lead + middle + trail
- return u''.join(words)
+ return u"".join(words)
def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
"""Generate some lorem ipsum for the template."""
from jinja2.constants import LOREM_IPSUM_WORDS
from random import choice, randrange
+
words = LOREM_IPSUM_WORDS.split()
result = []
@@ -263,25 +284,25 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
if idx - randrange(3, 8) > last_comma:
last_comma = idx
last_fullstop += 2
- word += ','
+ word += ","
# add end of sentences
if idx - randrange(10, 20) > last_fullstop:
last_comma = last_fullstop = idx
- word += '.'
+ word += "."
next_capitalized = True
p.append(word)
# ensure that the paragraph ends with a dot.
- p = u' '.join(p)
- if p.endswith(','):
- p = p[:-1] + '.'
- elif not p.endswith('.'):
- p += '.'
+ p = u" ".join(p)
+ if p.endswith(","):
+ p = p[:-1] + "."
+ elif not p.endswith("."):
+ p += "."
result.append(p)
if not html:
- return u'\n\n'.join(result)
- return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
+ return u"\n\n".join(result)
+ return Markup(u"\n".join(u"<p>%s</p>" % escape(x) for x in result))
def unicode_urlencode(obj, charset="utf-8", for_qs=False):
@@ -336,9 +357,9 @@ class LRUCache(object):
def __getstate__(self):
return {
- 'capacity': self.capacity,
- '_mapping': self._mapping,
- '_queue': self._queue
+ "capacity": self.capacity,
+ "_mapping": self._mapping,
+ "_queue": self._queue,
}
def __setstate__(self, d):
@@ -390,10 +411,7 @@ class LRUCache(object):
return len(self._mapping)
def __repr__(self):
- return '<%s %r>' % (
- self.__class__.__name__,
- self._mapping
- )
+ return "<%s %r>" % (self.__class__.__name__, self._mapping)
def __getitem__(self, key):
"""Get an item from the cache. Moves the item up so that it has the
@@ -462,10 +480,13 @@ class LRUCache(object):
def itervalue(self):
"""Iterate over all values."""
- warnings.warn(DeprecationWarning(
- '"itervalue()" is deprecated and will be removed in version 2.12.'
- + ' Use "itervalues()" instead.'
- ), stacklevel=2)
+ warnings.warn(
+ DeprecationWarning(
+ '"itervalue()" is deprecated and will be removed in version 2.12.'
+ + ' Use "itervalues()" instead.'
+ ),
+ stacklevel=2,
+ )
return self.itervalues()
def itervalues(self):
@@ -496,10 +517,12 @@ class LRUCache(object):
abc.MutableMapping.register(LRUCache)
-def select_autoescape(enabled_extensions=('html', 'htm', 'xml'),
- disabled_extensions=(),
- default_for_string=True,
- default=False):
+def select_autoescape(
+ enabled_extensions=("html", "htm", "xml"),
+ disabled_extensions=(),
+ default_for_string=True,
+ default=False,
+):
"""Intelligently sets the initial value of autoescaping based on the
filename of the template. This is the recommended way to configure
autoescaping if you do not want to write a custom function yourself.
@@ -534,10 +557,9 @@ def select_autoescape(enabled_extensions=('html', 'htm', 'xml'),
.. versionadded:: 2.9
"""
- enabled_patterns = tuple('.' + x.lstrip('.').lower()
- for x in enabled_extensions)
- disabled_patterns = tuple('.' + x.lstrip('.').lower()
- for x in disabled_extensions)
+ enabled_patterns = tuple("." + x.lstrip(".").lower() for x in enabled_extensions)
+ disabled_patterns = tuple("." + x.lstrip(".").lower() for x in disabled_extensions)
+
def autoescape(template_name):
if template_name is None:
return default_for_string
@@ -547,6 +569,7 @@ def select_autoescape(enabled_extensions=('html', 'htm', 'xml'),
if template_name.endswith(disabled_patterns):
return False
return default
+
return autoescape
@@ -570,11 +593,13 @@ def htmlsafe_json_dumps(obj, dumper=None, **kwargs):
"""
if dumper is None:
dumper = json.dumps
- rv = dumper(obj, **kwargs) \
- .replace(u'<', u'\\u003c') \
- .replace(u'>', u'\\u003e') \
- .replace(u'&', u'\\u0026') \
- .replace(u"'", u'\\u0027')
+ rv = (
+ dumper(obj, **kwargs)
+ .replace(u"<", u"\\u003c")
+ .replace(u">", u"\\u003e")
+ .replace(u"&", u"\\u0026")
+ .replace(u"'", u"\\u0027")
+ )
return Markup(rv)
@@ -606,7 +631,7 @@ class Cycler(object):
def __init__(self, *items):
if not items:
- raise RuntimeError('at least one item has to be provided')
+ raise RuntimeError("at least one item has to be provided")
self.items = items
self.pos = 0
@@ -635,14 +660,14 @@ class Cycler(object):
class Joiner(object):
"""A joining helper for templates."""
- def __init__(self, sep=u', '):
+ def __init__(self, sep=u", "):
self.sep = sep
self.used = False
def __call__(self):
if not self.used:
self.used = True
- return u''
+ return u""
return self.sep
@@ -655,7 +680,7 @@ class Namespace(object):
self.__attrs = dict(*args, **kwargs)
def __getattribute__(self, name):
- if name == '_Namespace__attrs':
+ if name == "_Namespace__attrs":
return object.__getattribute__(self, name)
try:
return self.__attrs[name]
@@ -666,12 +691,12 @@ class Namespace(object):
self.__attrs[name] = value
def __repr__(self):
- return '<Namespace %r>' % self.__attrs
+ return "<Namespace %r>" % self.__attrs
# does this python version support async for in and async generators?
try:
- exec('async def _():\n async for _ in ():\n yield _')
+ exec("async def _():\n async for _ in ():\n yield _")
have_async_gen = True
except SyntaxError:
have_async_gen = False
diff --git a/src/jinja2/visitor.py b/src/jinja2/visitor.py
index 2e03a6b..a803ade 100644
--- a/src/jinja2/visitor.py
+++ b/src/jinja2/visitor.py
@@ -28,7 +28,7 @@ class NodeVisitor(object):
exists for this node. In that case the generic visit function is
used instead.
"""
- method = 'visit_' + node.__class__.__name__
+ method = "visit_" + node.__class__.__name__
return getattr(self, method, None)
def visit(self, node, *args, **kwargs):
diff --git a/tests/conftest.py b/tests/conftest.py
index c1c33c3..a1c11a9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -18,105 +18,93 @@ from jinja2.utils import have_async_gen
def pytest_ignore_collect(path):
- if 'async' in path.basename and not have_async_gen:
+ if "async" in path.basename and not have_async_gen:
return True
return False
def pytest_configure(config):
- '''Register custom marks for test categories.'''
+ """Register custom marks for test categories."""
custom_markers = [
- 'api',
- 'byte_code_cache',
- 'core_tags',
- 'debug',
- 'escapeUrlizeTarget',
- 'ext',
- 'extended',
- 'filesystemloader',
- 'filter',
- 'for_loop',
- 'helpers',
- 'if_condition',
- 'imports',
- 'includes',
- 'inheritance',
- 'lexer',
- 'lexnparse',
- 'loaders',
- 'loremIpsum',
- 'lowlevel',
- 'lrucache',
- 'lstripblocks',
- 'macros',
- 'meta',
- 'moduleloader',
- 'parser',
- 'regression',
- 'sandbox',
- 'set',
- 'streaming',
- 'syntax',
- 'test_tests',
- 'tokenstream',
- 'undefined',
- 'utils',
- 'with_',
+ "api",
+ "byte_code_cache",
+ "core_tags",
+ "debug",
+ "escapeUrlizeTarget",
+ "ext",
+ "extended",
+ "filesystemloader",
+ "filter",
+ "for_loop",
+ "helpers",
+ "if_condition",
+ "imports",
+ "includes",
+ "inheritance",
+ "lexer",
+ "lexnparse",
+ "loaders",
+ "loremIpsum",
+ "lowlevel",
+ "lrucache",
+ "lstripblocks",
+ "macros",
+ "meta",
+ "moduleloader",
+ "parser",
+ "regression",
+ "sandbox",
+ "set",
+ "streaming",
+ "syntax",
+ "test_tests",
+ "tokenstream",
+ "undefined",
+ "utils",
+ "with_",
]
for mark in custom_markers:
- config.addinivalue_line('markers', mark + ': test category')
+ config.addinivalue_line("markers", mark + ": test category")
@pytest.fixture
def env():
- '''returns a new environment.
- '''
+ """returns a new environment."""
return Environment()
@pytest.fixture
def dict_loader():
- '''returns DictLoader
- '''
- return loaders.DictLoader({
- 'justdict.html': 'FOO'
- })
+ """returns DictLoader"""
+ return loaders.DictLoader({"justdict.html": "FOO"})
@pytest.fixture
def package_loader():
- '''returns PackageLoader initialized from templates
- '''
- return loaders.PackageLoader('res', 'templates')
+ """returns PackageLoader initialized from templates"""
+ return loaders.PackageLoader("res", "templates")
@pytest.fixture
def filesystem_loader():
- '''returns FileSystemLoader initialized to res/templates directory
- '''
+ """returns FileSystemLoader initialized to res/templates directory"""
here = os.path.dirname(os.path.abspath(__file__))
- return loaders.FileSystemLoader(here + '/res/templates')
+ return loaders.FileSystemLoader(here + "/res/templates")
@pytest.fixture
def function_loader():
- '''returns a FunctionLoader
- '''
- return loaders.FunctionLoader({'justfunction.html': 'FOO'}.get)
+ """returns a FunctionLoader"""
+ return loaders.FunctionLoader({"justfunction.html": "FOO"}.get)
@pytest.fixture
def choice_loader(dict_loader, package_loader):
- '''returns a ChoiceLoader
- '''
+ """returns a ChoiceLoader"""
return loaders.ChoiceLoader([dict_loader, package_loader])
@pytest.fixture
def prefix_loader(filesystem_loader, dict_loader):
- '''returns a PrefixLoader
- '''
- return loaders.PrefixLoader({
- 'a': filesystem_loader,
- 'b': dict_loader
- })
+ """returns a PrefixLoader"""
+ return loaders.PrefixLoader({"a": filesystem_loader, "b": dict_loader})
diff --git a/tests/res/templates/mojibake.txt b/tests/res/templates/mojibake.txt
new file mode 100644
index 0000000..4b94aa6
--- /dev/null
+++ b/tests/res/templates/mojibake.txt
@@ -0,0 +1 @@
+文字化け
diff --git a/tests/res/templates/variable_encoding.txt b/tests/res/templates/variable_encoding.txt
deleted file mode 100644
index dadbed4..0000000
--- a/tests/res/templates/variable_encoding.txt
+++ /dev/null
@@ -1 +0,0 @@
-tech \ No newline at end of file
diff --git a/tests/test_api.py b/tests/test_api.py
index fff63ff..a0c546c 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -37,18 +37,17 @@ from jinja2.utils import evalcontextfunction
@pytest.mark.api
@pytest.mark.extended
class TestExtendedAPI(object):
-
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.items()|list }}")
+ assert tmpl.render(foo={"items": 42}) == "[('items', 42)]"
tmpl = env.from_string('{{ foo|attr("items")()|list }}')
- assert tmpl.render(foo={'items': 42}) == "[('items', 42)]"
+ assert tmpl.render(foo={"items": 42}) == "[('items', 42)]"
tmpl = env.from_string('{{ foo["items"] }}')
- assert tmpl.render(foo={'items': 42}) == '42'
+ assert tmpl.render(foo={"items": 42}) == "42"
def test_finalize(self):
e = Environment(finalize=lambda v: "" if v is None else v)
@@ -128,7 +127,7 @@ class TestExtendedAPI(object):
assert expr(foo=42) == 84
def test_template_passthrough(self, env):
- t = Template('Content')
+ t = Template("Content")
assert env.get_template(t) is t
assert env.select_template([t]) is t
assert env.get_or_select_template([t]) is t
@@ -138,7 +137,7 @@ class TestExtendedAPI(object):
"""Passing Undefined to get/select_template raises an
UndefinedError or shows the undefined message in the list.
"""
- env.loader=DictLoader({})
+ env.loader = DictLoader({})
t = Undefined(name="no_name_1")
with pytest.raises(UndefinedError):
@@ -157,23 +156,22 @@ class TestExtendedAPI(object):
assert "'no_name_1' is undefined" in exc_message
assert "no_name_2" in exc_message
-
def test_autoescape_autoselect(self, env):
def select_autoescape(name):
- if name is None or '.' not in name:
+ if name is None or "." not in name:
return False
- return name.endswith('.html')
- env = Environment(autoescape=select_autoescape,
- loader=DictLoader({
- 'test.txt': '{{ foo }}',
- 'test.html': '{{ foo }}'
- }))
- t = env.get_template('test.txt')
- assert t.render(foo='<foo>') == '<foo>'
- t = env.get_template('test.html')
- assert t.render(foo='<foo>') == '&lt;foo&gt;'
- t = env.from_string('{{ foo }}')
- assert t.render(foo='<foo>') == '<foo>'
+ 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>") == "&lt;foo&gt;"
+ t = env.from_string("{{ foo }}")
+ assert t.render(foo="<foo>") == "<foo>"
def test_sandbox_max_range(self, env):
from jinja2.sandbox import SandboxedEnvironment, MAX_RANGE
@@ -188,53 +186,56 @@ class TestExtendedAPI(object):
@pytest.mark.api
@pytest.mark.meta
class TestMeta(object):
-
def test_find_undeclared_variables(self, env):
- ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
+ ast = env.parse("{% set foo = 42 %}{{ bar + foo }}")
x = meta.find_undeclared_variables(ast)
- assert x == set(['bar'])
+ 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 %}')
+ 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'])
+ assert x == set(["bar", "seq", "muh"])
- ast = env.parse('{% for x in range(5) %}{{ x }}{% endfor %}{{ foo }}')
+ ast = env.parse("{% for x in range(5) %}{{ x }}{% endfor %}{{ foo }}")
x = meta.find_undeclared_variables(ast)
- assert x == set(['foo'])
+ assert x == set(["foo"])
def test_find_refererenced_templates(self, env):
ast = env.parse('{% extends "layout.html" %}{% include helper %}')
i = meta.find_referenced_templates(ast)
- assert next(i) == 'layout.html'
+ assert next(i) == "layout.html"
assert next(i) is None
assert list(i) == []
- ast = env.parse('{% extends "layout.html" %}'
- '{% from "test.html" import a, b as c %}'
- '{% import "meh.html" as meh %}'
- '{% include "muh.html" %}')
+ ast = env.parse(
+ '{% extends "layout.html" %}'
+ '{% from "test.html" import a, b as c %}'
+ '{% import "meh.html" as meh %}'
+ '{% include "muh.html" %}'
+ )
i = meta.find_referenced_templates(ast)
- assert list(i) == ['layout.html', 'test.html', 'meh.html', 'muh.html']
+ assert list(i) == ["layout.html", "test.html", "meh.html", "muh.html"]
def test_find_included_templates(self, env):
ast = env.parse('{% include ["foo.html", "bar.html"] %}')
i = meta.find_referenced_templates(ast)
- assert list(i) == ['foo.html', 'bar.html']
+ assert list(i) == ["foo.html", "bar.html"]
ast = env.parse('{% include ("foo.html", "bar.html") %}')
i = meta.find_referenced_templates(ast)
- assert list(i) == ['foo.html', 'bar.html']
+ assert list(i) == ["foo.html", "bar.html"]
ast = env.parse('{% include ["foo.html", "bar.html", foo] %}')
i = meta.find_referenced_templates(ast)
- assert list(i) == ['foo.html', 'bar.html', None]
+ assert list(i) == ["foo.html", "bar.html", None]
ast = env.parse('{% include ("foo.html", "bar.html", foo) %}')
i = meta.find_referenced_templates(ast)
- assert list(i) == ['foo.html', 'bar.html', None]
+ assert list(i) == ["foo.html", "bar.html", None]
@pytest.mark.api
@@ -256,8 +257,8 @@ class TestStreaming(object):
)
stream = tmpl.stream(seq=list(range(3)))
stream.enable_buffering(size=3)
- assert next(stream) == u'<ul><li>1'
- assert next(stream) == u' - 0</li>'
+ assert next(stream) == u"<ul><li>1"
+ assert next(stream) == u" - 0</li>"
def test_streaming_behavior(self, env):
tmpl = env.from_string("")
@@ -273,9 +274,9 @@ class TestStreaming(object):
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'
+ 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)
@@ -283,148 +284,151 @@ class TestStreaming(object):
@pytest.mark.api
@pytest.mark.undefined
class TestUndefined(object):
-
def test_stopiteration_is_undefined(self):
def test():
raise StopIteration()
- t = Template('A{{ test() }}B')
- assert t.render(test=test) == 'AB'
- t = Template('A{{ test().missingattribute }}B')
+
+ t = Template("A{{ test() }}B")
+ assert t.render(test=test) == "AB"
+ t = Template("A{{ test().missingattribute }}B")
pytest.raises(UndefinedError, t.render, test=test)
def test_undefined_and_special_attributes(self):
with pytest.raises(AttributeError):
- Undefined('Foo').__dict__
+ Undefined("Foo").__dict__
def test_logging_undefined(self):
_messages = []
class DebugLogger(object):
def warning(self, msg, *args):
- _messages.append('W:' + msg % args)
+ _messages.append("W:" + msg % args)
def error(self, msg, *args):
- _messages.append('E:' + msg % args)
+ _messages.append("E:" + msg % args)
logging_undefined = make_logging_undefined(DebugLogger())
env = Environment(undefined=logging_undefined)
- assert env.from_string('{{ missing }}').render() == 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 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',
+ "W:Template variable warning: missing is undefined",
"E:Template variable error: 'missing' is undefined",
- 'W:Template variable warning: missing is undefined',
- 'W:Template variable warning: int object has no attribute missing',
- 'W:Template variable warning: missing is undefined',
+ "W:Template variable warning: missing is undefined",
+ "W:Template variable warning: int object has no attribute missing",
+ "W:Template variable warning: missing is undefined",
]
def test_default_undefined(self):
env = Environment(undefined=Undefined)
- assert env.from_string('{{ missing }}').render() == 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'
- pytest.raises(UndefinedError,
- env.from_string('{{ missing - 1}}').render)
- und1 = Undefined(name='x')
- und2 = Undefined(name='y')
+ 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"
+ pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render)
+ und1 = Undefined(name="x")
+ und2 = Undefined(name="y")
assert und1 == und2
assert und1 != 42
assert hash(und1) == hash(und2) == hash(Undefined())
with pytest.raises(AttributeError):
- getattr(Undefined, '__slots__')
+ getattr(Undefined, "__slots__")
def test_chainable_undefined(self):
env = Environment(undefined=ChainableUndefined)
# The following tests are copied from test_default_undefined
- assert env.from_string('{{ missing }}').render() == u''
- assert env.from_string('{{ missing|list }}').render() == '[]'
- assert env.from_string('{{ missing is not defined }}').render() \
- == 'True'
- assert env.from_string('{{ foo.missing }}').render(foo=42) == ''
- assert env.from_string('{{ not missing }}').render() == 'True'
- pytest.raises(UndefinedError,
- env.from_string('{{ missing - 1}}').render)
+ assert env.from_string("{{ missing }}").render() == u""
+ assert env.from_string("{{ missing|list }}").render() == "[]"
+ assert env.from_string("{{ missing is not defined }}").render() == "True"
+ assert env.from_string("{{ foo.missing }}").render(foo=42) == ""
+ assert env.from_string("{{ not missing }}").render() == "True"
+ pytest.raises(UndefinedError, env.from_string("{{ missing - 1}}").render)
with pytest.raises(AttributeError):
- getattr(ChainableUndefined, '__slots__')
+ getattr(ChainableUndefined, "__slots__")
# The following tests ensure subclass functionality works as expected
- assert env.from_string('{{ missing.bar["baz"] }}').render() == u''
- assert env.from_string('{{ foo.bar["baz"]._undefined_name }}').render() \
- == u'foo'
- assert env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(
- foo=42) == u'bar'
- assert env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(
- foo={'bar': 42}) == u'baz'
+ assert env.from_string('{{ missing.bar["baz"] }}').render() == u""
+ assert (
+ env.from_string('{{ foo.bar["baz"]._undefined_name }}').render() == u"foo"
+ )
+ assert (
+ env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(foo=42)
+ == u"bar"
+ )
+ assert (
+ env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(
+ foo={"bar": 42}
+ )
+ == u"baz"
+ )
def test_debug_undefined(self):
env = Environment(undefined=DebugUndefined)
- assert env.from_string('{{ missing }}').render() == '{{ missing }}'
- pytest.raises(UndefinedError,
- env.from_string('{{ missing.attribute }}').render)
- assert env.from_string('{{ missing|list }}').render() == '[]'
- assert env.from_string('{{ missing is not defined }}').render() \
- == 'True'
- assert env.from_string('{{ foo.missing }}').render(foo=42) \
+ 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'
- undefined_hint = 'this is testing undefined hint of DebugUndefined'
- assert str(DebugUndefined(hint=undefined_hint)) == u'{{ undefined value printed: %s }}' % undefined_hint
+ )
+ assert env.from_string("{{ not missing }}").render() == "True"
+ undefined_hint = "this is testing undefined hint of DebugUndefined"
+ assert (
+ str(DebugUndefined(hint=undefined_hint))
+ == u"{{ undefined value printed: %s }}" % undefined_hint
+ )
with pytest.raises(AttributeError):
- getattr(DebugUndefined, '__slots__')
+ getattr(DebugUndefined, "__slots__")
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'
+ 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"
+ )
with pytest.raises(AttributeError):
- getattr(StrictUndefined, '__slots__')
- assert env.from_string('{{ "foo" if false }}').render() == ''
+ getattr(StrictUndefined, "__slots__")
+ assert env.from_string('{{ "foo" if false }}').render() == ""
def test_indexing_gives_undefined(self):
t = Template("{{ var[42].foo }}")
pytest.raises(UndefinedError, t.render, var=0)
def test_none_gives_proper_error(self):
- with pytest.raises(UndefinedError, match= "'None' has no attribute 'split'"):
- Environment().getattr(None, 'split')()
+ with pytest.raises(UndefinedError, match="'None' has no attribute 'split'"):
+ Environment().getattr(None, "split")()
def test_object_repr(self):
- with pytest.raises(UndefinedError, match="'int object' has no attribute 'upper'"):
- Undefined(obj=42, name='upper')()
+ with pytest.raises(
+ UndefinedError, match="'int object' has no attribute 'upper'"
+ ):
+ Undefined(obj=42, name="upper")()
@pytest.mark.api
@pytest.mark.lowlevel
class TestLowLevel(object):
-
def test_custom_code_generator(self):
class CustomCodeGenerator(CodeGenerator):
def visit_Const(self, node, frame=None):
# This method is pure nonsense, but works fine for testing...
- if node.value == 'foo':
- self.write(repr('bar'))
+ if node.value == "foo":
+ self.write(repr("bar"))
else:
super(CustomCodeGenerator, self).visit_Const(node, frame)
@@ -433,16 +437,16 @@ class TestLowLevel(object):
env = CustomEnvironment()
tmpl = env.from_string('{% set foo = "foo" %}{{ foo }}')
- assert tmpl.render() == 'bar'
+ assert tmpl.render() == "bar"
def test_custom_context(self):
class CustomContext(Context):
def resolve_or_missing(self, key):
- return 'resolve-' + key
+ return "resolve-" + key
class CustomEnvironment(Environment):
context_class = CustomContext
env = CustomEnvironment()
- tmpl = env.from_string('{{ foo }}')
- assert tmpl.render() == 'resolve-foo'
+ tmpl = env.from_string("{{ foo }}")
+ assert tmpl.render() == "resolve-foo"
diff --git a/tests/test_async.py b/tests/test_async.py
index 2e0728d..bae73c6 100644
--- a/tests/test_async.py
+++ b/tests/test_async.py
@@ -17,18 +17,19 @@ def run(coro):
def test_basic_async():
- t = Template('{% for item in [1, 2, 3] %}[{{ item }}]{% endfor %}',
- enable_async=True)
+ t = Template(
+ "{% for item in [1, 2, 3] %}[{{ item }}]{% endfor %}", enable_async=True
+ )
+
async def func():
return await t.render_async()
rv = run(func())
- assert rv == '[1][2][3]'
+ assert rv == "[1][2][3]"
def test_await_on_calls():
- t = Template('{{ async_func() + normal_func() }}',
- enable_async=True)
+ t = Template("{{ async_func() + normal_func() }}", enable_async=True)
async def async_func():
return 42
@@ -37,18 +38,14 @@ def test_await_on_calls():
return 23
async def func():
- return await t.render_async(
- async_func=async_func,
- normal_func=normal_func
- )
+ return await t.render_async(async_func=async_func, normal_func=normal_func)
rv = run(func())
- assert rv == '65'
+ assert rv == "65"
def test_await_on_calls_normal_render():
- t = Template('{{ async_func() + normal_func() }}',
- enable_async=True)
+ t = Template("{{ async_func() + normal_func() }}", enable_async=True)
async def async_func():
return 42
@@ -56,17 +53,16 @@ def test_await_on_calls_normal_render():
def normal_func():
return 23
- rv = t.render(
- async_func=async_func,
- normal_func=normal_func
- )
+ rv = t.render(async_func=async_func, normal_func=normal_func)
- assert rv == '65'
+ assert rv == "65"
def test_await_and_macros():
- t = Template('{% macro foo(x) %}[{{ x }}][{{ async_func() }}]'
- '{% endmacro %}{{ foo(42) }}', enable_async=True)
+ t = Template(
+ "{% macro foo(x) %}[{{ x }}][{{ async_func() }}]{% endmacro %}{{ foo(42) }}",
+ enable_async=True,
+ )
async def async_func():
return 42
@@ -75,34 +71,38 @@ def test_await_and_macros():
return await t.render_async(async_func=async_func)
rv = run(func())
- assert rv == '[42][42]'
+ assert rv == "[42][42]"
def test_async_blocks():
- t = Template('{% block foo %}<Test>{% endblock %}{{ self.foo() }}',
- enable_async=True, autoescape=True)
+ t = Template(
+ "{% block foo %}<Test>{% endblock %}{{ self.foo() }}",
+ enable_async=True,
+ autoescape=True,
+ )
+
async def func():
return await t.render_async()
rv = run(func())
- assert rv == '<Test><Test>'
+ assert rv == "<Test><Test>"
def test_async_generate():
- t = Template('{% for x in [1, 2, 3] %}{{ x }}{% endfor %}',
- enable_async=True)
+ t = Template("{% for x in [1, 2, 3] %}{{ x }}{% endfor %}", enable_async=True)
rv = list(t.generate())
- assert rv == ['1', '2', '3']
+ assert rv == ["1", "2", "3"]
def test_async_iteration_in_templates():
- t = Template('{% for x in rng %}{{ x }}{% endfor %}',
- enable_async=True)
+ t = Template("{% for x in rng %}{{ x }}{% endfor %}", enable_async=True)
+
async def async_iterator():
for item in [1, 2, 3]:
yield item
+
rv = list(t.generate(rng=async_iterator()))
- assert rv == ['1', '2', '3']
+ assert rv == ["1", "2", "3"]
def test_async_iteration_in_templates_extended():
@@ -116,39 +116,43 @@ def test_async_iteration_in_templates_extended():
@pytest.fixture
def test_env_async():
- env = Environment(loader=DictLoader(dict(
- module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}',
- header='[{{ foo }}|{{ 23 }}]',
- o_printer='({{ o }})'
- )), enable_async=True)
- env.globals['bar'] = 23
+ env = Environment(
+ loader=DictLoader(
+ dict(
+ module="{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}",
+ header="[{{ foo }}|{{ 23 }}]",
+ o_printer="({{ o }})",
+ )
+ ),
+ enable_async=True,
+ )
+ env.globals["bar"] = 23
return env
@pytest.mark.imports
class TestAsyncImports(object):
-
def test_context_imports(self, test_env_async):
t = test_env_async.from_string('{% import "module" as m %}{{ m.test() }}')
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env_async.from_string(
'{% import "module" as m without context %}{{ m.test() }}'
)
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env_async.from_string(
'{% import "module" as m with context %}{{ m.test() }}'
)
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
t = test_env_async.from_string('{% from "module" import test %}{{ test() }}')
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env_async.from_string(
'{% from "module" import test without context %}{{ test() }}'
)
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env_async.from_string(
'{% from "module" import test with context %}{{ test() }}'
)
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
def test_trailing_comma(self, test_env_async):
test_env_async.from_string('{% from "foo" import bar, baz with context %}')
@@ -158,83 +162,92 @@ class TestAsyncImports(object):
test_env_async.from_string('{% from "foo" import bar, with with context %}')
def test_exports(self, test_env_async):
- m = run(test_env_async.from_string('''
+ m = run(
+ test_env_async.from_string(
+ """
{% macro toplevel() %}...{% endmacro %}
{% macro __private() %}...{% endmacro %}
{% set variable = 42 %}
{% for item in [1] %}
{% macro notthere() %}{% endmacro %}
{% endfor %}
- ''')._get_default_module_async())
- assert run(m.toplevel()) == '...'
- assert not hasattr(m, '__missing')
+ """
+ )._get_default_module_async()
+ )
+ assert run(m.toplevel()) == "..."
+ assert not hasattr(m, "__missing")
assert m.variable == 42
- assert not hasattr(m, 'notthere')
+ assert not hasattr(m, "notthere")
@pytest.mark.imports
@pytest.mark.includes
class TestAsyncIncludes(object):
-
def test_context_include(self, test_env_async):
t = test_env_async.from_string('{% include "header" %}')
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
t = test_env_async.from_string('{% include "header" with context %}')
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
t = test_env_async.from_string('{% include "header" without context %}')
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
def test_choice_includes(self, test_env_async):
t = test_env_async.from_string('{% include ["missing", "header"] %}')
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
t = test_env_async.from_string(
'{% include ["missing", "missing2"] ignore missing %}'
)
- assert t.render(foo=42) == ''
+ assert t.render(foo=42) == ""
t = test_env_async.from_string('{% include ["missing", "missing2"] %}')
pytest.raises(TemplateNotFound, t.render)
with pytest.raises(TemplatesNotFound) as e:
t.render()
- assert e.value.templates == ['missing', 'missing2']
- assert e.value.name == 'missing2'
+ assert e.value.templates == ["missing", "missing2"]
+ assert e.value.name == "missing2"
def test_includes(t, **ctx):
- ctx['foo'] = 42
- assert t.render(ctx) == '[42|23]'
+ ctx["foo"] = 42
+ assert t.render(ctx) == "[42|23]"
t = test_env_async.from_string('{% include ["missing", "header"] %}')
test_includes(t)
- t = test_env_async.from_string('{% include x %}')
- test_includes(t, x=['missing', 'header'])
+ t = test_env_async.from_string("{% include x %}")
+ test_includes(t, x=["missing", "header"])
t = test_env_async.from_string('{% include [x, "header"] %}')
- test_includes(t, x='missing')
- t = test_env_async.from_string('{% include x %}')
- test_includes(t, x='header')
- t = test_env_async.from_string('{% include x %}')
- test_includes(t, x='header')
- t = test_env_async.from_string('{% include [x] %}')
- test_includes(t, x='header')
+ test_includes(t, x="missing")
+ t = test_env_async.from_string("{% include x %}")
+ test_includes(t, x="header")
+ t = test_env_async.from_string("{% include x %}")
+ test_includes(t, x="header")
+ t = test_env_async.from_string("{% include [x] %}")
+ test_includes(t, x="header")
def test_include_ignoring_missing(self, test_env_async):
t = test_env_async.from_string('{% include "missing" %}')
pytest.raises(TemplateNotFound, t.render)
- for extra in '', 'with context', 'without context':
- t = test_env_async.from_string('{% include "missing" ignore missing ' +
- extra + ' %}')
- assert t.render() == ''
+ for extra in "", "with context", "without context":
+ t = test_env_async.from_string(
+ '{% include "missing" ignore missing ' + extra + " %}"
+ )
+ assert t.render() == ""
def test_context_include_with_overrides(self, test_env_async):
- env = Environment(loader=DictLoader(dict(
- main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}",
- item="{{ item }}"
- )))
+ env = Environment(
+ loader=DictLoader(
+ dict(
+ main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}",
+ item="{{ item }}",
+ )
+ )
+ )
assert env.get_template("main").render() == "123"
def test_unoptimized_scopes(self, test_env_async):
- t = test_env_async.from_string("""
+ t = test_env_async.from_string(
+ """
{% macro outer(o) %}
{% macro inner() %}
{% include "o_printer" %}
@@ -242,14 +255,18 @@ class TestAsyncIncludes(object):
{{ inner() }}
{% endmacro %}
{{ outer("FOO") }}
- """)
- assert t.render().strip() == '(FOO)'
+ """
+ )
+ assert t.render().strip() == "(FOO)"
def test_unoptimized_scopes_autoescape(self):
- env = Environment(loader=DictLoader(dict(
- o_printer='({{ o }})',
- )), autoescape=True, enable_async=True)
- t = env.from_string("""
+ env = Environment(
+ loader=DictLoader(dict(o_printer="({{ o }})",)),
+ autoescape=True,
+ enable_async=True,
+ )
+ t = env.from_string(
+ """
{% macro outer(o) %}
{% macro inner() %}
{% include "o_printer" %}
@@ -257,26 +274,29 @@ class TestAsyncIncludes(object):
{{ inner() }}
{% endmacro %}
{{ outer("FOO") }}
- """)
- assert t.render().strip() == '(FOO)'
+ """
+ )
+ assert t.render().strip() == "(FOO)"
@pytest.mark.core_tags
@pytest.mark.for_loop
class TestAsyncForLoop(object):
-
def test_simple(self, test_env_async):
- tmpl = test_env_async.from_string('{% for item in seq %}{{ item }}{% endfor %}')
- assert tmpl.render(seq=list(range(10))) == '0123456789'
+ tmpl = test_env_async.from_string("{% for item in seq %}{{ item }}{% endfor %}")
+ assert tmpl.render(seq=list(range(10))) == "0123456789"
def test_else(self, test_env_async):
tmpl = test_env_async.from_string(
- '{% for item in seq %}XXX{% else %}...{% endfor %}')
- assert tmpl.render() == '...'
+ "{% for item in seq %}XXX{% else %}...{% endfor %}"
+ )
+ assert tmpl.render() == "..."
def test_empty_blocks(self, test_env_async):
- tmpl = test_env_async.from_string('<{% for item in seq %}{% else %}{% endfor %}>')
- assert tmpl.render() == '<>'
+ tmpl = test_env_async.from_string(
+ "<{% for item in seq %}{% else %}{% endfor %}>"
+ )
+ assert tmpl.render() == "<>"
@pytest.mark.parametrize(
"transform", [lambda x: x, iter, reversed, lambda x: (i for i in x), auto_aiter]
@@ -291,29 +311,35 @@ class TestAsyncForLoop(object):
assert out == "1|0|2|1|True|False|2\n2|1|1|0|False|True|2\n"
def test_cycling(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in seq %}{{
+ tmpl = test_env_async.from_string(
+ """{% for item in seq %}{{
loop.cycle('<1>', '<2>') }}{% endfor %}{%
- for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''')
- output = tmpl.render(seq=list(range(4)), through=('<1>', '<2>'))
- assert output == '<1><2>' * 4
+ for item in seq %}{{ loop.cycle(*through) }}{% endfor %}"""
+ )
+ output = tmpl.render(seq=list(range(4)), through=("<1>", "<2>"))
+ assert output == "<1><2>" * 4
def test_lookaround(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in seq -%}
+ tmpl = test_env_async.from_string(
+ """{% for item in seq -%}
{{ loop.previtem|default('x') }}-{{ item }}-{{
loop.nextitem|default('x') }}|
- {%- endfor %}''')
+ {%- endfor %}"""
+ )
output = tmpl.render(seq=list(range(4)))
- assert output == 'x-0-1|0-1-2|1-2-3|2-3-x|'
+ assert output == "x-0-1|0-1-2|1-2-3|2-3-x|"
def test_changed(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in seq -%}
+ tmpl = test_env_async.from_string(
+ """{% for item in seq -%}
{{ loop.changed(item) }},
- {%- endfor %}''')
+ {%- endfor %}"""
+ )
output = tmpl.render(seq=[None, None, 1, 2, 2, 3, 4, 4, 4])
- assert output == 'True,False,True,True,False,True,True,False,False,'
+ assert output == "True,False,True,True,False,True,True,False,False,"
def test_scope(self, test_env_async):
- tmpl = test_env_async.from_string('{% for item in seq %}{% endfor %}{{ item }}')
+ tmpl = test_env_async.from_string("{% for item in seq %}{% endfor %}{{ item }}")
output = tmpl.render(seq=list(range(10)))
assert not output
@@ -321,111 +347,160 @@ class TestAsyncForLoop(object):
def inner():
for item in range(5):
yield item
- tmpl = test_env_async.from_string('{% for item in iter %}{{ item }}{% endfor %}')
+
+ tmpl = test_env_async.from_string(
+ "{% for item in iter %}{{ item }}{% endfor %}"
+ )
output = tmpl.render(iter=inner())
- assert output == '01234'
+ assert output == "01234"
def test_noniter(self, test_env_async):
- tmpl = test_env_async.from_string('{% for item in none %}...{% endfor %}')
+ tmpl = test_env_async.from_string("{% for item in none %}...{% endfor %}")
pytest.raises(TypeError, tmpl.render)
def test_recursive(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in seq recursive -%}
+ tmpl = test_env_async.from_string(
+ """{% for item in seq recursive -%}
[{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}''')
- assert tmpl.render(seq=[
- dict(a=1, b=[dict(a=1), dict(a=2)]),
- dict(a=2, b=[dict(a=1), dict(a=2)]),
- dict(a=3, b=[dict(a='a')])
- ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]'
+ {%- endfor %}"""
+ )
+ assert (
+ tmpl.render(
+ seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a="a")]),
+ ]
+ )
+ == "[1<[1][2]>][2<[1][2]>][3<[a]>]"
+ )
def test_recursive_lookaround(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in seq recursive -%}
+ tmpl = test_env_async.from_string(
+ """{% for item in seq recursive -%}
[{{ loop.previtem.a if loop.previtem is defined else 'x' }}.{{
item.a }}.{{ loop.nextitem.a if loop.nextitem is defined else 'x'
}}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}''')
- assert tmpl.render(seq=[
- dict(a=1, b=[dict(a=1), dict(a=2)]),
- dict(a=2, b=[dict(a=1), dict(a=2)]),
- dict(a=3, b=[dict(a='a')])
- ]) == '[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]'
+ {%- endfor %}"""
+ )
+ assert (
+ tmpl.render(
+ seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a="a")]),
+ ]
+ )
+ == "[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]"
+ )
def test_recursive_depth0(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in seq recursive -%}
+ tmpl = test_env_async.from_string(
+ """{% for item in seq recursive -%}
[{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}''')
- assert tmpl.render(seq=[
- dict(a=1, b=[dict(a=1), dict(a=2)]),
- dict(a=2, b=[dict(a=1), dict(a=2)]),
- dict(a=3, b=[dict(a='a')])
- ]) == '[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]'
+ {%- endfor %}"""
+ )
+ assert (
+ tmpl.render(
+ seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a="a")]),
+ ]
+ )
+ == "[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]"
+ )
def test_recursive_depth(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in seq recursive -%}
+ tmpl = test_env_async.from_string(
+ """{% for item in seq recursive -%}
[{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}''')
- assert tmpl.render(seq=[
- dict(a=1, b=[dict(a=1), dict(a=2)]),
- dict(a=2, b=[dict(a=1), dict(a=2)]),
- dict(a=3, b=[dict(a='a')])
- ]) == '[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]'
+ {%- endfor %}"""
+ )
+ assert (
+ tmpl.render(
+ seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a="a")]),
+ ]
+ )
+ == "[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]"
+ )
def test_looploop(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for row in table %}
+ tmpl = test_env_async.from_string(
+ """{% for row in table %}
{%- set rowloop = loop -%}
{% for cell in row -%}
[{{ rowloop.index }}|{{ loop.index }}]
{%- endfor %}
- {%- endfor %}''')
- assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]'
+ {%- endfor %}"""
+ )
+ assert tmpl.render(table=["ab", "cd"]) == "[1|1][1|2][2|1][2|2]"
def test_reversed_bug(self, test_env_async):
- tmpl = test_env_async.from_string('{% for i in items %}{{ i }}'
- '{% if not loop.last %}'
- ',{% endif %}{% endfor %}')
- assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3'
+ tmpl = test_env_async.from_string(
+ "{% for i in items %}{{ i }}"
+ "{% if not loop.last %}"
+ ",{% endif %}{% endfor %}"
+ )
+ assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3"
def test_loop_errors(self, test_env_async):
- tmpl = test_env_async.from_string('''{% for item in [1] if loop.index
- == 0 %}...{% endfor %}''')
+ tmpl = test_env_async.from_string(
+ """{% for item in [1] if loop.index
+ == 0 %}...{% endfor %}"""
+ )
pytest.raises(UndefinedError, tmpl.render)
- tmpl = test_env_async.from_string('''{% for item in [] %}...{% else
- %}{{ loop }}{% endfor %}''')
- assert tmpl.render() == ''
+ tmpl = test_env_async.from_string(
+ """{% for item in [] %}...{% else
+ %}{{ loop }}{% endfor %}"""
+ )
+ assert tmpl.render() == ""
def test_loop_filter(self, test_env_async):
- tmpl = test_env_async.from_string('{% for item in range(10) if item '
- 'is even %}[{{ item }}]{% endfor %}')
- assert tmpl.render() == '[0][2][4][6][8]'
- tmpl = test_env_async.from_string('''
+ tmpl = test_env_async.from_string(
+ "{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}"
+ )
+ assert tmpl.render() == "[0][2][4][6][8]"
+ tmpl = test_env_async.from_string(
+ """
{%- for item in range(10) if item is even %}[{{
- loop.index }}:{{ item }}]{% endfor %}''')
- assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]'
+ loop.index }}:{{ item }}]{% endfor %}"""
+ )
+ assert tmpl.render() == "[1:0][2:2][3:4][4:6][5:8]"
def test_scoped_special_var(self, test_env_async):
t = test_env_async.from_string(
- '{% for s in seq %}[{{ loop.first }}{% for c in s %}'
- '|{{ loop.first }}{% endfor %}]{% endfor %}')
- assert t.render(seq=('ab', 'cd')) \
- == '[True|True|False][False|True|False]'
+ "{% for s in seq %}[{{ loop.first }}{% for c in s %}"
+ "|{{ loop.first }}{% endfor %}]{% endfor %}"
+ )
+ assert t.render(seq=("ab", "cd")) == "[True|True|False][False|True|False]"
def test_scoped_loop_var(self, test_env_async):
- t = test_env_async.from_string('{% for x in seq %}{{ loop.first }}'
- '{% for y in seq %}{% endfor %}{% endfor %}')
- assert t.render(seq='ab') == 'TrueFalse'
- t = test_env_async.from_string('{% for x in seq %}{% for y in seq %}'
- '{{ loop.first }}{% endfor %}{% endfor %}')
- assert t.render(seq='ab') == 'TrueFalseTrueFalse'
+ t = test_env_async.from_string(
+ "{% for x in seq %}{{ loop.first }}"
+ "{% for y in seq %}{% endfor %}{% endfor %}"
+ )
+ assert t.render(seq="ab") == "TrueFalse"
+ t = test_env_async.from_string(
+ "{% for x in seq %}{% for y in seq %}"
+ "{{ loop.first }}{% endfor %}{% endfor %}"
+ )
+ assert t.render(seq="ab") == "TrueFalseTrueFalse"
def test_recursive_empty_loop_iter(self, test_env_async):
- t = test_env_async.from_string('''
+ t = test_env_async.from_string(
+ """
{%- for item in foo recursive -%}{%- endfor -%}
- ''')
- assert t.render(dict(foo=[])) == ''
+ """
+ )
+ assert t.render(dict(foo=[])) == ""
def test_call_in_loop(self, test_env_async):
- t = test_env_async.from_string('''
+ t = test_env_async.from_string(
+ """
{%- macro do_something() -%}
[{{ caller() }}]
{%- endmacro %}
@@ -435,24 +510,29 @@ class TestAsyncForLoop(object):
{{ i }}
{%- endcall %}
{%- endfor -%}
- ''')
- assert t.render() == '[1][2][3]'
+ """
+ )
+ assert t.render() == "[1][2][3]"
def test_scoping_bug(self, test_env_async):
- t = test_env_async.from_string('''
+ t = test_env_async.from_string(
+ """
{%- for item in foo %}...{{ item }}...{% endfor %}
{%- macro item(a) %}...{{ a }}...{% endmacro %}
{{- item(2) -}}
- ''')
- assert t.render(foo=(1,)) == '...1......2...'
+ """
+ )
+ assert t.render(foo=(1,)) == "...1......2..."
def test_unpacking(self, test_env_async):
- tmpl = test_env_async.from_string('{% for a, b, c in [[1, 2, 3]] %}'
- '{{ a }}|{{ b }}|{{ c }}{% endfor %}')
- assert tmpl.render() == '1|2|3'
+ tmpl = test_env_async.from_string(
+ "{% for a, b, c in [[1, 2, 3]] %}{{ a }}|{{ b }}|{{ c }}{% endfor %}"
+ )
+ assert tmpl.render() == "1|2|3"
def test_recursive_loop_filter(self, test_env_async):
- t = test_env_async.from_string('''
+ t = test_env_async.from_string(
+ """
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{%- for page in [site.root] if page.url != this recursive %}
@@ -460,46 +540,46 @@ class TestAsyncForLoop(object):
{{- loop(page.children) }}
{%- endfor %}
</urlset>
- ''')
- sm =t.render(this='/foo', site={'root': {
- 'url': '/',
- 'children': [
- {'url': '/foo'},
- {'url': '/bar'},
- ]
- }})
+ """
+ )
+ sm = t.render(
+ this="/foo",
+ site={
+ "root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"},]}
+ },
+ )
lines = [x.strip() for x in sm.splitlines() if x.strip()]
assert lines == [
'<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
- '<url><loc>/</loc></url>',
- '<url><loc>/bar</loc></url>',
- '</urlset>',
+ "<url><loc>/</loc></url>",
+ "<url><loc>/bar</loc></url>",
+ "</urlset>",
]
def test_nonrecursive_loop_filter(self, test_env_async):
- t = test_env_async.from_string('''
+ t = test_env_async.from_string(
+ """
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{%- for page in items if page.url != this %}
<url><loc>{{ page.url }}</loc></url>
{%- endfor %}
</urlset>
- ''')
- sm =t.render(this='/foo', items=[
- {'url': '/'},
- {'url': '/foo'},
- {'url': '/bar'},
- ])
+ """
+ )
+ sm = t.render(
+ this="/foo", items=[{"url": "/"}, {"url": "/foo"}, {"url": "/bar"},]
+ )
lines = [x.strip() for x in sm.splitlines() if x.strip()]
assert lines == [
'<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
- '<url><loc>/</loc></url>',
- '<url><loc>/bar</loc></url>',
- '</urlset>',
+ "<url><loc>/</loc></url>",
+ "<url><loc>/bar</loc></url>",
+ "</urlset>",
]
def test_bare_async(self, test_env_async):
t = test_env_async.from_string('{% extends "header" %}')
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
diff --git a/tests/test_asyncfilters.py b/tests/test_asyncfilters.py
index 12b4e4f..23a1001 100644
--- a/tests/test_asyncfilters.py
+++ b/tests/test_asyncfilters.py
@@ -11,10 +11,10 @@ async def make_aiter(iter):
def mark_dualiter(parameter, factory):
def decorator(f):
- return pytest.mark.parametrize(parameter, [
- lambda: factory(),
- lambda: make_aiter(factory()),
- ])(f)
+ return pytest.mark.parametrize(
+ parameter, [lambda: factory(), lambda: make_aiter(factory()),]
+ )(f)
+
return decorator
@@ -23,39 +23,46 @@ def env_async():
return Environment(enable_async=True)
-@mark_dualiter('foo', lambda: range(10))
+@mark_dualiter("foo", lambda: range(10))
def test_first(env_async, foo):
- tmpl = env_async.from_string('{{ foo()|first }}')
+ tmpl = env_async.from_string("{{ foo()|first }}")
out = tmpl.render(foo=foo)
- assert out == '0'
-
-
-@mark_dualiter('items', lambda: [
- {'foo': 1, 'bar': 2},
- {'foo': 2, 'bar': 3},
- {'foo': 1, 'bar': 1},
- {'foo': 3, 'bar': 4}
-])
+ assert out == "0"
+
+
+@mark_dualiter(
+ "items",
+ lambda: [
+ {"foo": 1, "bar": 2},
+ {"foo": 2, "bar": 3},
+ {"foo": 1, "bar": 1},
+ {"foo": 3, "bar": 4},
+ ],
+)
def test_groupby(env_async, items):
- tmpl = env_async.from_string('''
+ tmpl = env_async.from_string(
+ """
{%- for grouper, list in items()|groupby('foo') -%}
{{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}|
- {%- endfor %}''')
- assert tmpl.render(items=items).split('|') == [
+ {%- endfor %}"""
+ )
+ assert tmpl.render(items=items).split("|") == [
"1: 1, 2: 1, 1",
"2: 2, 3",
"3: 3, 4",
- ""
+ "",
]
-@mark_dualiter('items', lambda: [('a', 1), ('a', 2), ('b', 1)])
+@mark_dualiter("items", lambda: [("a", 1), ("a", 2), ("b", 1)])
def test_groupby_tuple_index(env_async, items):
- tmpl = env_async.from_string('''
+ tmpl = env_async.from_string(
+ """
{%- for grouper, list in items()|groupby(0) -%}
{{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}|
- {%- endfor %}''')
- assert tmpl.render(items=items) == 'a:1:2|b:1|'
+ {%- endfor %}"""
+ )
+ assert tmpl.render(items=items) == "a:1:2|b:1|"
def make_articles():
@@ -71,80 +78,78 @@ def make_articles():
self.title = title
return [
- Article('aha', 1, 1, 1970),
- Article('interesting', 2, 1, 1970),
- Article('really?', 3, 1, 1970),
- Article('totally not', 1, 1, 1971)
+ Article("aha", 1, 1, 1970),
+ Article("interesting", 2, 1, 1970),
+ Article("really?", 3, 1, 1970),
+ Article("totally not", 1, 1, 1971),
]
-@mark_dualiter('articles', make_articles)
+@mark_dualiter("articles", make_articles)
def test_groupby_multidot(env_async, articles):
- tmpl = env_async.from_string('''
+ tmpl = env_async.from_string(
+ """
{%- for year, list in articles()|groupby('date.year') -%}
{{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}|
- {%- endfor %}''')
- assert tmpl.render(articles=articles).split('|') == [
- '1970[aha][interesting][really?]',
- '1971[totally not]',
- ''
+ {%- endfor %}"""
+ )
+ assert tmpl.render(articles=articles).split("|") == [
+ "1970[aha][interesting][really?]",
+ "1971[totally not]",
+ "",
]
-@mark_dualiter('int_items', lambda: [1, 2, 3])
+@mark_dualiter("int_items", lambda: [1, 2, 3])
def test_join_env_int(env_async, int_items):
tmpl = env_async.from_string('{{ items()|join("|") }}')
out = tmpl.render(items=int_items)
- assert out == '1|2|3'
+ assert out == "1|2|3"
-@mark_dualiter('string_items', lambda: ["<foo>", Markup("<span>foo</span>")])
+@mark_dualiter("string_items", lambda: ["<foo>", Markup("<span>foo</span>")])
def test_join_string_list(string_items):
env2 = Environment(autoescape=True, enable_async=True)
- tmpl = env2.from_string(
- '{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
- assert tmpl.render(items=string_items) == '&lt;foo&gt;<span>foo</span>'
+ tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
+ assert tmpl.render(items=string_items) == "&lt;foo&gt;<span>foo</span>"
def make_users():
class User(object):
def __init__(self, username):
self.username = username
- return map(User, ['foo', 'bar'])
+
+ return map(User, ["foo", "bar"])
-@mark_dualiter('users', make_users)
+@mark_dualiter("users", make_users)
def test_join_attribute(env_async, users):
- tmpl = env_async.from_string('''{{ users()|join(', ', 'username') }}''')
- assert tmpl.render(users=users) == 'foo, bar'
+ tmpl = env_async.from_string("""{{ users()|join(', ', 'username') }}""")
+ assert tmpl.render(users=users) == "foo, bar"
-@mark_dualiter('items', lambda: [1, 2, 3, 4, 5])
+@mark_dualiter("items", lambda: [1, 2, 3, 4, 5])
def test_simple_reject(env_async, items):
tmpl = env_async.from_string('{{ items()|reject("odd")|join("|") }}')
- assert tmpl.render(items=items) == '2|4'
+ assert tmpl.render(items=items) == "2|4"
-@mark_dualiter('items', lambda: [None, False, 0, 1, 2, 3, 4, 5])
+@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5])
def test_bool_reject(env_async, items):
- tmpl = env_async.from_string(
- '{{ items()|reject|join("|") }}'
- )
- assert tmpl.render(items=items) == 'None|False|0'
+ tmpl = env_async.from_string('{{ items()|reject|join("|") }}')
+ assert tmpl.render(items=items) == "None|False|0"
-@mark_dualiter('items', lambda: [1, 2, 3, 4, 5])
+@mark_dualiter("items", lambda: [1, 2, 3, 4, 5])
def test_simple_select(env_async, items):
tmpl = env_async.from_string('{{ items()|select("odd")|join("|") }}')
- assert tmpl.render(items=items) == '1|3|5'
+ assert tmpl.render(items=items) == "1|3|5"
-@mark_dualiter('items', lambda: [None, False, 0, 1, 2, 3, 4, 5])
+@mark_dualiter("items", lambda: [None, False, 0, 1, 2, 3, 4, 5])
def test_bool_select(env_async, items):
- tmpl = env_async.from_string(
- '{{ items()|select|join("|") }}'
- )
- assert tmpl.render(items=items) == '1|2|3|4|5'
+ tmpl = env_async.from_string('{{ items()|select|join("|") }}')
+ assert tmpl.render(items=items) == "1|2|3|4|5"
def make_users():
@@ -152,82 +157,82 @@ def make_users():
def __init__(self, name, is_active):
self.name = name
self.is_active = is_active
+
return [
- User('john', True),
- User('jane', True),
- User('mike', False),
+ User("john", True),
+ User("jane", True),
+ User("mike", False),
]
-@mark_dualiter('users', make_users)
+@mark_dualiter("users", make_users)
def test_simple_select_attr(env_async, users):
tmpl = env_async.from_string(
- '{{ users()|selectattr("is_active")|'
- 'map(attribute="name")|join("|") }}'
+ '{{ users()|selectattr("is_active")|map(attribute="name")|join("|") }}'
)
- assert tmpl.render(users=users) == 'john|jane'
+ assert tmpl.render(users=users) == "john|jane"
-@mark_dualiter('items', lambda: list('123'))
+@mark_dualiter("items", lambda: list("123"))
def test_simple_map(env_async, items):
tmpl = env_async.from_string('{{ items()|map("int")|sum }}')
- assert tmpl.render(items=items) == '6'
+ assert tmpl.render(items=items) == "6"
def test_map_sum(env_async): # async map + async filter
tmpl = env_async.from_string('{{ [[1,2], [3], [4,5,6]]|map("sum")|list }}')
- assert tmpl.render() == '[3, 3, 15]'
+ assert tmpl.render() == "[3, 3, 15]"
-@mark_dualiter('users', make_users)
+@mark_dualiter("users", make_users)
def test_attribute_map(env_async, users):
tmpl = env_async.from_string('{{ users()|map(attribute="name")|join("|") }}')
- assert tmpl.render(users=users) == 'john|jane|mike'
+ assert tmpl.render(users=users) == "john|jane|mike"
def test_empty_map(env_async):
tmpl = env_async.from_string('{{ none|map("upper")|list }}')
- assert tmpl.render() == '[]'
+ assert tmpl.render() == "[]"
-@mark_dualiter('items', lambda: [1, 2, 3, 4, 5, 6])
+@mark_dualiter("items", lambda: [1, 2, 3, 4, 5, 6])
def test_sum(env_async, items):
- tmpl = env_async.from_string('''{{ items()|sum }}''')
- assert tmpl.render(items=items) == '21'
+ tmpl = env_async.from_string("""{{ items()|sum }}""")
+ assert tmpl.render(items=items) == "21"
-@mark_dualiter('items', lambda: [
- {'value': 23},
- {'value': 1},
- {'value': 18},
-])
+@mark_dualiter("items", lambda: [{"value": 23}, {"value": 1}, {"value": 18},])
def test_sum_attributes(env_async, items):
- tmpl = env_async.from_string('''{{ items()|sum('value') }}''')
+ tmpl = env_async.from_string("""{{ items()|sum('value') }}""")
assert tmpl.render(items=items)
def test_sum_attributes_nested(env_async):
- tmpl = env_async.from_string('''{{ values|sum('real.value') }}''')
- assert tmpl.render(values=[
- {'real': {'value': 23}},
- {'real': {'value': 1}},
- {'real': {'value': 18}},
- ]) == '42'
+ tmpl = env_async.from_string("""{{ values|sum('real.value') }}""")
+ assert (
+ tmpl.render(
+ values=[
+ {"real": {"value": 23}},
+ {"real": {"value": 1}},
+ {"real": {"value": 18}},
+ ]
+ )
+ == "42"
+ )
def test_sum_attributes_tuple(env_async):
- tmpl = env_async.from_string('''{{ values.items()|sum('1') }}''')
- assert tmpl.render(values={
- 'foo': 23,
- 'bar': 1,
- 'baz': 18,
- }) == '42'
+ tmpl = env_async.from_string("""{{ values.items()|sum('1') }}""")
+ assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18,}) == "42"
-@mark_dualiter('items', lambda: range(10))
+@mark_dualiter("items", lambda: range(10))
def test_slice(env_async, items):
- tmpl = env_async.from_string('{{ items()|slice(3)|list }}|'
- '{{ items()|slice(3, "X")|list }}')
+ tmpl = env_async.from_string(
+ "{{ items()|slice(3)|list }}|" '{{ items()|slice(3, "X")|list }}'
+ )
out = tmpl.render(items=items)
- assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
- "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]")
+ assert out == (
+ "[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
+ "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]"
+ )
diff --git a/tests/test_bytecode_cache.py b/tests/test_bytecode_cache.py
index a548ab2..937c0fe 100644
--- a/tests/test_bytecode_cache.py
+++ b/tests/test_bytecode_cache.py
@@ -20,19 +20,15 @@ from jinja2.exceptions import TemplateNotFound
@pytest.fixture
def env(package_loader):
bytecode_cache = FileSystemBytecodeCache()
- return Environment(
- loader=package_loader,
- bytecode_cache=bytecode_cache,
- )
+ return Environment(loader=package_loader, bytecode_cache=bytecode_cache,)
@pytest.mark.byte_code_cache
class TestByteCodeCache(object):
-
def test_simple(self, env):
- tmpl = env.get_template('test.html')
- assert tmpl.render().strip() == 'BAR'
- pytest.raises(TemplateNotFound, env.get_template, 'missing.html')
+ tmpl = env.get_template("test.html")
+ assert tmpl.render().strip() == "BAR"
+ pytest.raises(TemplateNotFound, env.get_template, "missing.html")
class MockMemcached(object):
@@ -63,22 +59,22 @@ class TestMemcachedBytecodeCache(object):
memcached = MockMemcached()
m = MemcachedBytecodeCache(memcached)
- b = Bucket(None, 'key', '')
- b.code = 'code'
+ b = Bucket(None, "key", "")
+ b.code = "code"
m.dump_bytecode(b)
- assert memcached.key == 'jinja2/bytecode/key'
+ assert memcached.key == "jinja2/bytecode/key"
- b = Bucket(None, 'key', '')
+ b = Bucket(None, "key", "")
m.load_bytecode(b)
- assert b.code == 'code'
+ assert b.code == "code"
def test_exception(self):
memcached = MockMemcached()
memcached.get = memcached.get_side_effect
memcached.set = memcached.set_side_effect
m = MemcachedBytecodeCache(memcached)
- b = Bucket(None, 'key', '')
- b.code = 'code'
+ b = Bucket(None, "key", "")
+ b.code = "code"
m.dump_bytecode(b)
m.load_bytecode(b)
diff --git a/tests/test_core_tags.py b/tests/test_core_tags.py
index 27981be..deb0169 100644
--- a/tests/test_core_tags.py
+++ b/tests/test_core_tags.py
@@ -25,70 +25,89 @@ def env_trim():
@pytest.mark.core_tags
@pytest.mark.for_loop
class TestForLoop(object):
-
def test_simple(self, env):
- tmpl = env.from_string('{% for item in seq %}{{ item }}{% endfor %}')
- assert tmpl.render(seq=list(range(10))) == '0123456789'
+ tmpl = env.from_string("{% for item in seq %}{{ item }}{% endfor %}")
+ assert tmpl.render(seq=list(range(10))) == "0123456789"
def test_else(self, env):
- tmpl = env.from_string(
- '{% for item in seq %}XXX{% else %}...{% endfor %}')
- assert tmpl.render() == '...'
+ tmpl = env.from_string("{% for item in seq %}XXX{% else %}...{% endfor %}")
+ assert tmpl.render() == "..."
def test_else_scoping_item(self, env):
- tmpl = env.from_string(
- '{% for item in [] %}{% else %}{{ item }}{% endfor %}')
- assert tmpl.render(item=42) == '42'
+ tmpl = env.from_string("{% for item in [] %}{% else %}{{ item }}{% endfor %}")
+ assert tmpl.render(item=42) == "42"
def test_empty_blocks(self, env):
- tmpl = env.from_string('<{% for item in seq %}{% else %}{% endfor %}>')
- assert tmpl.render() == '<>'
+ tmpl = env.from_string("<{% for item in seq %}{% else %}{% endfor %}>")
+ assert tmpl.render() == "<>"
def test_context_vars(self, env):
slist = [42, 24]
for seq in [slist, iter(slist), reversed(slist), (_ for _ in slist)]:
- tmpl = env.from_string('''{% for item in seq -%}
+ tmpl = env.from_string(
+ """{% for item in seq -%}
{{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{
loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{
- loop.length }}###{% endfor %}''')
- one, two, _ = tmpl.render(seq=seq).split('###')
- (one_index, one_index0, one_revindex, one_revindex0, one_first,
- one_last, one_length) = one.split('|')
- (two_index, two_index0, two_revindex, two_revindex0, two_first,
- two_last, two_length) = two.split('|')
+ loop.length }}###{% endfor %}"""
+ )
+ one, two, _ = tmpl.render(seq=seq).split("###")
+ (
+ one_index,
+ one_index0,
+ one_revindex,
+ one_revindex0,
+ one_first,
+ one_last,
+ one_length,
+ ) = one.split("|")
+ (
+ two_index,
+ two_index0,
+ two_revindex,
+ two_revindex0,
+ two_first,
+ two_last,
+ two_length,
+ ) = two.split("|")
assert int(one_index) == 1 and int(two_index) == 2
assert int(one_index0) == 0 and int(two_index0) == 1
assert int(one_revindex) == 2 and int(two_revindex) == 1
assert int(one_revindex0) == 1 and int(two_revindex0) == 0
- assert one_first == 'True' and two_first == 'False'
- assert one_last == 'False' and two_last == 'True'
- assert one_length == two_length == '2'
+ assert one_first == "True" and two_first == "False"
+ assert one_last == "False" and two_last == "True"
+ assert one_length == two_length == "2"
def test_cycling(self, env):
- tmpl = env.from_string('''{% for item in seq %}{{
+ tmpl = env.from_string(
+ """{% for item in seq %}{{
loop.cycle('<1>', '<2>') }}{% endfor %}{%
- for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''')
- output = tmpl.render(seq=list(range(4)), through=('<1>', '<2>'))
- assert output == '<1><2>' * 4
+ for item in seq %}{{ loop.cycle(*through) }}{% endfor %}"""
+ )
+ output = tmpl.render(seq=list(range(4)), through=("<1>", "<2>"))
+ assert output == "<1><2>" * 4
def test_lookaround(self, env):
- tmpl = env.from_string('''{% for item in seq -%}
+ tmpl = env.from_string(
+ """{% for item in seq -%}
{{ loop.previtem|default('x') }}-{{ item }}-{{
loop.nextitem|default('x') }}|
- {%- endfor %}''')
+ {%- endfor %}"""
+ )
output = tmpl.render(seq=list(range(4)))
- assert output == 'x-0-1|0-1-2|1-2-3|2-3-x|'
+ assert output == "x-0-1|0-1-2|1-2-3|2-3-x|"
def test_changed(self, env):
- tmpl = env.from_string('''{% for item in seq -%}
+ tmpl = env.from_string(
+ """{% for item in seq -%}
{{ loop.changed(item) }},
- {%- endfor %}''')
+ {%- endfor %}"""
+ )
output = tmpl.render(seq=[None, None, 1, 2, 2, 3, 4, 4, 4])
- assert output == 'True,False,True,True,False,True,True,False,False,'
+ assert output == "True,False,True,True,False,True,True,False,False,"
def test_scope(self, env):
- tmpl = env.from_string('{% for item in seq %}{% endfor %}{{ item }}')
+ tmpl = env.from_string("{% for item in seq %}{% endfor %}{{ item }}")
output = tmpl.render(seq=list(range(10)))
assert not output
@@ -96,115 +115,163 @@ class TestForLoop(object):
def inner():
for item in range(5):
yield item
- tmpl = env.from_string('{% for item in iter %}{{ item }}{% endfor %}')
+
+ tmpl = env.from_string("{% for item in iter %}{{ item }}{% endfor %}")
output = tmpl.render(iter=inner())
- assert output == '01234'
+ assert output == "01234"
def test_noniter(self, env):
- tmpl = env.from_string('{% for item in none %}...{% endfor %}')
+ tmpl = env.from_string("{% for item in none %}...{% endfor %}")
pytest.raises(TypeError, tmpl.render)
def test_recursive(self, env):
- tmpl = env.from_string('''{% for item in seq recursive -%}
+ tmpl = env.from_string(
+ """{% for item in seq recursive -%}
[{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}''')
- assert tmpl.render(seq=[
- dict(a=1, b=[dict(a=1), dict(a=2)]),
- dict(a=2, b=[dict(a=1), dict(a=2)]),
- dict(a=3, b=[dict(a='a')])
- ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]'
+ {%- endfor %}"""
+ )
+ assert (
+ tmpl.render(
+ seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a="a")]),
+ ]
+ )
+ == "[1<[1][2]>][2<[1][2]>][3<[a]>]"
+ )
def test_recursive_lookaround(self, env):
- tmpl = env.from_string('''{% for item in seq recursive -%}
+ tmpl = env.from_string(
+ """{% for item in seq recursive -%}
[{{ loop.previtem.a if loop.previtem is defined else 'x' }}.{{
item.a }}.{{ loop.nextitem.a if loop.nextitem is defined else 'x'
}}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}''')
- assert tmpl.render(seq=[
- dict(a=1, b=[dict(a=1), dict(a=2)]),
- dict(a=2, b=[dict(a=1), dict(a=2)]),
- dict(a=3, b=[dict(a='a')])
- ]) == '[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]'
+ {%- endfor %}"""
+ )
+ assert (
+ tmpl.render(
+ seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a="a")]),
+ ]
+ )
+ == "[x.1.2<[x.1.2][1.2.x]>][1.2.3<[x.1.2][1.2.x]>][2.3.x<[x.a.x]>]"
+ )
def test_recursive_depth0(self, env):
- tmpl = env.from_string('''{% for item in seq recursive -%}
+ 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]>]'
+ {%- 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 -%}
+ 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]>]'
+ {%- endfor %}"""
+ )
+ assert (
+ tmpl.render(
+ seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a="a")]),
+ ]
+ )
+ == "[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]"
+ )
def test_looploop(self, env):
- tmpl = env.from_string('''{% for row in table %}
+ tmpl = env.from_string(
+ """{% for row in table %}
{%- set rowloop = loop -%}
{% for cell in row -%}
[{{ rowloop.index }}|{{ loop.index }}]
{%- endfor %}
- {%- endfor %}''')
- assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]'
+ {%- endfor %}"""
+ )
+ assert tmpl.render(table=["ab", "cd"]) == "[1|1][1|2][2|1][2|2]"
def test_reversed_bug(self, env):
- tmpl = env.from_string('{% for i in items %}{{ i }}'
- '{% if not loop.last %}'
- ',{% endif %}{% endfor %}')
- assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3'
+ tmpl = env.from_string(
+ "{% for i in items %}{{ i }}"
+ "{% if not loop.last %}"
+ ",{% endif %}{% endfor %}"
+ )
+ assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3"
def test_loop_errors(self, env):
- tmpl = env.from_string('''{% for item in [1] if loop.index
- == 0 %}...{% endfor %}''')
+ tmpl = env.from_string(
+ """{% for item in [1] if loop.index
+ == 0 %}...{% endfor %}"""
+ )
pytest.raises(UndefinedError, tmpl.render)
- tmpl = env.from_string('''{% for item in [] %}...{% else
- %}{{ loop }}{% endfor %}''')
- assert tmpl.render() == ''
+ tmpl = env.from_string(
+ """{% for item in [] %}...{% else
+ %}{{ loop }}{% endfor %}"""
+ )
+ assert tmpl.render() == ""
def test_loop_filter(self, env):
- tmpl = env.from_string('{% for item in range(10) if item '
- 'is even %}[{{ item }}]{% endfor %}')
- assert tmpl.render() == '[0][2][4][6][8]'
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ "{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}"
+ )
+ assert tmpl.render() == "[0][2][4][6][8]"
+ tmpl = env.from_string(
+ """
{%- for item in range(10) if item is even %}[{{
- loop.index }}:{{ item }}]{% endfor %}''')
- assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]'
+ loop.index }}:{{ item }}]{% endfor %}"""
+ )
+ assert tmpl.render() == "[1:0][2:2][3:4][4:6][5:8]"
def test_loop_unassignable(self, env):
- pytest.raises(TemplateSyntaxError, env.from_string,
- '{% for loop in seq %}...{% endfor %}')
+ pytest.raises(
+ TemplateSyntaxError, env.from_string, "{% for loop in seq %}...{% endfor %}"
+ )
def test_scoped_special_var(self, env):
t = env.from_string(
- '{% for s in seq %}[{{ loop.first }}{% for c in s %}'
- '|{{ loop.first }}{% endfor %}]{% endfor %}')
- assert t.render(seq=('ab', 'cd')) \
- == '[True|True|False][False|True|False]'
+ "{% for s in seq %}[{{ loop.first }}{% for c in s %}"
+ "|{{ loop.first }}{% endfor %}]{% endfor %}"
+ )
+ assert t.render(seq=("ab", "cd")) == "[True|True|False][False|True|False]"
def test_scoped_loop_var(self, env):
- t = env.from_string('{% for x in seq %}{{ loop.first }}'
- '{% for y in seq %}{% endfor %}{% endfor %}')
- assert t.render(seq='ab') == 'TrueFalse'
- t = env.from_string('{% for x in seq %}{% for y in seq %}'
- '{{ loop.first }}{% endfor %}{% endfor %}')
- assert t.render(seq='ab') == 'TrueFalseTrueFalse'
+ t = env.from_string(
+ "{% for x in seq %}{{ loop.first }}"
+ "{% for y in seq %}{% endfor %}{% endfor %}"
+ )
+ assert t.render(seq="ab") == "TrueFalse"
+ t = env.from_string(
+ "{% for x in seq %}{% for y in seq %}"
+ "{{ loop.first }}{% endfor %}{% endfor %}"
+ )
+ assert t.render(seq="ab") == "TrueFalseTrueFalse"
def test_recursive_empty_loop_iter(self, env):
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- for item in foo recursive -%}{%- endfor -%}
- ''')
- assert t.render(dict(foo=[])) == ''
+ """
+ )
+ assert t.render(dict(foo=[])) == ""
def test_call_in_loop(self, env):
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- macro do_something() -%}
[{{ caller() }}]
{%- endmacro %}
@@ -214,150 +281,179 @@ class TestForLoop(object):
{{ i }}
{%- endcall %}
{%- endfor -%}
- ''')
- assert t.render() == '[1][2][3]'
+ """
+ )
+ assert t.render() == "[1][2][3]"
def test_scoping_bug(self, env):
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- for item in foo %}...{{ item }}...{% endfor %}
{%- macro item(a) %}...{{ a }}...{% endmacro %}
{{- item(2) -}}
- ''')
- assert t.render(foo=(1,)) == '...1......2...'
+ """
+ )
+ assert t.render(foo=(1,)) == "...1......2..."
def test_unpacking(self, env):
- tmpl = env.from_string('{% for a, b, c in [[1, 2, 3]] %}'
- '{{ a }}|{{ b }}|{{ c }}{% endfor %}')
- assert tmpl.render() == '1|2|3'
+ tmpl = env.from_string(
+ "{% for a, b, c in [[1, 2, 3]] %}{{ a }}|{{ b }}|{{ c }}{% endfor %}"
+ )
+ assert tmpl.render() == "1|2|3"
def test_intended_scoping_with_set(self, env):
- tmpl = env.from_string('{% for item in seq %}{{ x }}'
- '{% set x = item %}{{ x }}{% endfor %}')
- assert tmpl.render(x=0, seq=[1, 2, 3]) == '010203'
+ tmpl = env.from_string(
+ "{% for item in seq %}{{ x }}{% set x = item %}{{ x }}{% endfor %}"
+ )
+ assert tmpl.render(x=0, seq=[1, 2, 3]) == "010203"
- tmpl = env.from_string('{% set x = 9 %}{% for item in seq %}{{ x }}'
- '{% set x = item %}{{ x }}{% endfor %}')
- assert tmpl.render(x=0, seq=[1, 2, 3]) == '919293'
+ tmpl = env.from_string(
+ "{% set x = 9 %}{% for item in seq %}{{ x }}"
+ "{% set x = item %}{{ x }}{% endfor %}"
+ )
+ assert tmpl.render(x=0, seq=[1, 2, 3]) == "919293"
@pytest.mark.core_tags
@pytest.mark.if_condition
class TestIfCondition(object):
-
def test_simple(self, env):
- tmpl = env.from_string('''{% if true %}...{% endif %}''')
- assert tmpl.render() == '...'
+ tmpl = env.from_string("""{% if true %}...{% endif %}""")
+ assert tmpl.render() == "..."
def test_elif(self, env):
- tmpl = env.from_string('''{% if false %}XXX{% elif true
- %}...{% else %}XXX{% endif %}''')
- assert tmpl.render() == '...'
+ tmpl = env.from_string(
+ """{% if false %}XXX{% elif true
+ %}...{% else %}XXX{% endif %}"""
+ )
+ assert tmpl.render() == "..."
def test_elif_deep(self, env):
- elifs = '\n'.join('{{% elif a == {0} %}}{0}'.format(i)
- for i in range(1, 1000))
- tmpl = env.from_string('{{% if a == 0 %}}0{0}{{% else %}}x{{% endif %}}'
- .format(elifs))
+ elifs = "\n".join("{{% elif a == {0} %}}{0}".format(i) for i in range(1, 1000))
+ tmpl = env.from_string(
+ "{{% if a == 0 %}}0{0}{{% else %}}x{{% endif %}}".format(elifs)
+ )
for x in (0, 10, 999):
assert tmpl.render(a=x).strip() == str(x)
- assert tmpl.render(a=1000).strip() == 'x'
+ assert tmpl.render(a=1000).strip() == "x"
def test_else(self, env):
- tmpl = env.from_string('{% if false %}XXX{% else %}...{% endif %}')
- assert tmpl.render() == '...'
+ tmpl = env.from_string("{% if false %}XXX{% else %}...{% endif %}")
+ assert tmpl.render() == "..."
def test_empty(self, env):
- tmpl = env.from_string('[{% if true %}{% else %}{% endif %}]')
- assert tmpl.render() == '[]'
+ tmpl = env.from_string("[{% if true %}{% else %}{% endif %}]")
+ assert tmpl.render() == "[]"
def test_complete(self, env):
- tmpl = env.from_string('{% if a %}A{% elif b %}B{% elif c == d %}'
- 'C{% else %}D{% endif %}')
- assert tmpl.render(a=0, b=False, c=42, d=42.0) == 'C'
+ tmpl = env.from_string(
+ "{% if a %}A{% elif b %}B{% elif c == d %}C{% else %}D{% endif %}"
+ )
+ assert tmpl.render(a=0, b=False, c=42, d=42.0) == "C"
def test_no_scope(self, env):
- tmpl = env.from_string(
- '{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}')
- assert tmpl.render(a=True) == '1'
- tmpl = env.from_string(
- '{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}')
- assert tmpl.render() == '1'
+ tmpl = env.from_string("{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}")
+ assert tmpl.render(a=True) == "1"
+ tmpl = env.from_string("{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}")
+ assert tmpl.render() == "1"
@pytest.mark.core_tags
@pytest.mark.macros
class TestMacros(object):
def test_simple(self, env_trim):
- tmpl = env_trim.from_string('''\
+ tmpl = env_trim.from_string(
+ """\
{% macro say_hello(name) %}Hello {{ name }}!{% endmacro %}
-{{ say_hello('Peter') }}''')
- assert tmpl.render() == 'Hello Peter!'
+{{ say_hello('Peter') }}"""
+ )
+ assert tmpl.render() == "Hello Peter!"
def test_scoping(self, env_trim):
- tmpl = env_trim.from_string('''\
+ tmpl = env_trim.from_string(
+ """\
{% macro level1(data1) %}
{% macro level2(data2) %}{{ data1 }}|{{ data2 }}{% endmacro %}
{{ level2('bar') }}{% endmacro %}
-{{ level1('foo') }}''')
- assert tmpl.render() == 'foo|bar'
+{{ level1('foo') }}"""
+ )
+ assert tmpl.render() == "foo|bar"
def test_arguments(self, env_trim):
- tmpl = env_trim.from_string('''\
+ tmpl = env_trim.from_string(
+ """\
{% macro m(a, b, c='c', d='d') %}{{ a }}|{{ b }}|{{ c }}|{{ d }}{% endmacro %}
-{{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}''')
- assert tmpl.render() == '||c|d|a||c|d|a|b|c|d|1|2|3|d'
+{{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}"""
+ )
+ assert tmpl.render() == "||c|d|a||c|d|a|b|c|d|1|2|3|d"
def test_arguments_defaults_nonsense(self, env_trim):
- pytest.raises(TemplateSyntaxError, env_trim.from_string, '''\
-{% macro m(a, b=1, c) %}a={{ a }}, b={{ b }}, c={{ c }}{% endmacro %}''')
+ pytest.raises(
+ TemplateSyntaxError,
+ env_trim.from_string,
+ """\
+{% macro m(a, b=1, c) %}a={{ a }}, b={{ b }}, c={{ c }}{% endmacro %}""",
+ )
def test_caller_defaults_nonsense(self, env_trim):
- pytest.raises(TemplateSyntaxError, env_trim.from_string, '''\
+ pytest.raises(
+ TemplateSyntaxError,
+ env_trim.from_string,
+ """\
{% macro a() %}{{ caller() }}{% endmacro %}
-{% call(x, y=1, z) a() %}{% endcall %}''')
+{% call(x, y=1, z) a() %}{% endcall %}""",
+ )
def test_varargs(self, env_trim):
- tmpl = env_trim.from_string('''\
+ tmpl = env_trim.from_string(
+ """\
{% macro test() %}{{ varargs|join('|') }}{% endmacro %}\
-{{ test(1, 2, 3) }}''')
- assert tmpl.render() == '1|2|3'
+{{ test(1, 2, 3) }}"""
+ )
+ assert tmpl.render() == "1|2|3"
def test_simple_call(self, env_trim):
- tmpl = env_trim.from_string('''\
+ tmpl = env_trim.from_string(
+ """\
{% macro test() %}[[{{ caller() }}]]{% endmacro %}\
-{% call test() %}data{% endcall %}''')
- assert tmpl.render() == '[[data]]'
+{% call test() %}data{% endcall %}"""
+ )
+ assert tmpl.render() == "[[data]]"
def test_complex_call(self, env_trim):
- tmpl = env_trim.from_string('''\
+ tmpl = env_trim.from_string(
+ """\
{% macro test() %}[[{{ caller('data') }}]]{% endmacro %}\
-{% call(data) test() %}{{ data }}{% endcall %}''')
- assert tmpl.render() == '[[data]]'
+{% call(data) test() %}{{ data }}{% endcall %}"""
+ )
+ assert tmpl.render() == "[[data]]"
def test_caller_undefined(self, env_trim):
- tmpl = env_trim.from_string('''\
+ tmpl = env_trim.from_string(
+ """\
{% set caller = 42 %}\
{% macro test() %}{{ caller is not defined }}{% endmacro %}\
-{{ test() }}''')
- assert tmpl.render() == 'True'
+{{ test() }}"""
+ )
+ assert tmpl.render() == "True"
def test_include(self, env_trim):
env_trim = Environment(
- loader=DictLoader({
- 'include': '{% macro test(foo) %}[{{ foo }}]{% endmacro %}'
- })
+ loader=DictLoader(
+ {"include": "{% macro test(foo) %}[{{ foo }}]{% endmacro %}"}
+ )
)
- tmpl = env_trim.from_string(
- '{% from "include" import test %}{{ test("foo") }}')
- assert tmpl.render() == '[foo]'
+ tmpl = env_trim.from_string('{% from "include" import test %}{{ test("foo") }}')
+ assert tmpl.render() == "[foo]"
def test_macro_api(self, env_trim):
tmpl = env_trim.from_string(
- '{% macro foo(a, b) %}{% endmacro %}'
- '{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}'
- '{% macro baz() %}{{ caller() }}{% endmacro %}')
- assert tmpl.module.foo.arguments == ('a', 'b')
- assert tmpl.module.foo.name == 'foo'
+ "{% macro foo(a, b) %}{% endmacro %}"
+ "{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}"
+ "{% macro baz() %}{{ caller() }}{% endmacro %}"
+ )
+ assert tmpl.module.foo.arguments == ("a", "b")
+ assert tmpl.module.foo.name == "foo"
assert not tmpl.module.foo.caller
assert not tmpl.module.foo.catch_kwargs
assert not tmpl.module.foo.catch_varargs
@@ -368,141 +464,160 @@ class TestMacros(object):
assert tmpl.module.baz.caller
def test_callself(self, env_trim):
- tmpl = env_trim.from_string('{% macro foo(x) %}{{ x }}{% if x > 1 %}|'
- '{{ foo(x - 1) }}{% endif %}{% endmacro %}'
- '{{ foo(5) }}')
- assert tmpl.render() == '5|4|3|2|1'
+ tmpl = env_trim.from_string(
+ "{% macro foo(x) %}{{ x }}{% if x > 1 %}|"
+ "{{ foo(x - 1) }}{% endif %}{% endmacro %}"
+ "{{ foo(5) }}"
+ )
+ assert tmpl.render() == "5|4|3|2|1"
def test_macro_defaults_self_ref(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{%- set x = 42 %}
{%- macro m(a, b=x, x=23) %}{{ a }}|{{ b }}|{{ x }}{% endmacro -%}
- ''')
- assert tmpl.module.m(1) == '1||23'
- assert tmpl.module.m(1, 2) == '1|2|23'
- assert tmpl.module.m(1, 2, 3) == '1|2|3'
- assert tmpl.module.m(1, x=7) == '1|7|7'
+ """
+ )
+ assert tmpl.module.m(1) == "1||23"
+ assert tmpl.module.m(1, 2) == "1|2|23"
+ assert tmpl.module.m(1, 2, 3) == "1|2|3"
+ assert tmpl.module.m(1, x=7) == "1|7|7"
@pytest.mark.core_tags
@pytest.mark.set
class TestSet(object):
-
def test_normal(self, env_trim):
- tmpl = env_trim.from_string('{% set foo = 1 %}{{ foo }}')
- assert tmpl.render() == '1'
+ tmpl = env_trim.from_string("{% set foo = 1 %}{{ foo }}")
+ assert tmpl.render() == "1"
assert tmpl.module.foo == 1
def test_block(self, env_trim):
- tmpl = env_trim.from_string('{% set foo %}42{% endset %}{{ foo }}')
- assert tmpl.render() == '42'
- assert tmpl.module.foo == u'42'
+ tmpl = env_trim.from_string("{% set foo %}42{% endset %}{{ foo }}")
+ assert tmpl.render() == "42"
+ assert tmpl.module.foo == u"42"
def test_block_escaping(self):
env = Environment(autoescape=True)
- tmpl = env.from_string('{% set foo %}<em>{{ test }}</em>'
- '{% endset %}foo: {{ foo }}')
- assert tmpl.render(test='<unsafe>') == 'foo: <em>&lt;unsafe&gt;</em>'
+ tmpl = env.from_string(
+ "{% set foo %}<em>{{ test }}</em>{% endset %}foo: {{ foo }}"
+ )
+ assert tmpl.render(test="<unsafe>") == "foo: <em>&lt;unsafe&gt;</em>"
def test_set_invalid(self, env_trim):
- pytest.raises(TemplateSyntaxError, env_trim.from_string,
- "{% set foo['bar'] = 1 %}")
- tmpl = env_trim.from_string('{% set foo.bar = 1 %}')
+ pytest.raises(
+ TemplateSyntaxError, env_trim.from_string, "{% set foo['bar'] = 1 %}"
+ )
+ tmpl = env_trim.from_string("{% set foo.bar = 1 %}")
exc_info = pytest.raises(TemplateRuntimeError, tmpl.render, foo={})
- assert 'non-namespace object' in exc_info.value.message
+ assert "non-namespace object" in exc_info.value.message
def test_namespace_redefined(self, env_trim):
- tmpl = env_trim.from_string('{% set ns = namespace() %}'
- '{% set ns.bar = "hi" %}')
- exc_info = pytest.raises(TemplateRuntimeError, tmpl.render,
- namespace=dict)
- assert 'non-namespace object' in exc_info.value.message
+ tmpl = env_trim.from_string(
+ "{% set ns = namespace() %}" '{% set ns.bar = "hi" %}'
+ )
+ exc_info = pytest.raises(TemplateRuntimeError, tmpl.render, namespace=dict)
+ assert "non-namespace object" in exc_info.value.message
def test_namespace(self, env_trim):
- tmpl = env_trim.from_string('{% set ns = namespace() %}'
- '{% set ns.bar = "42" %}'
- '{{ ns.bar }}')
- assert tmpl.render() == '42'
+ tmpl = env_trim.from_string(
+ "{% set ns = namespace() %}" '{% set ns.bar = "42" %}' "{{ ns.bar }}"
+ )
+ assert tmpl.render() == "42"
def test_namespace_block(self, env_trim):
- tmpl = env_trim.from_string('{% set ns = namespace() %}'
- '{% set ns.bar %}42{% endset %}'
- '{{ ns.bar }}')
- assert tmpl.render() == '42'
+ tmpl = env_trim.from_string(
+ "{% set ns = namespace() %}{% set ns.bar %}42{% endset %}{{ ns.bar }}"
+ )
+ assert tmpl.render() == "42"
def test_init_namespace(self, env_trim):
- tmpl = env_trim.from_string('{% set ns = namespace(d, self=37) %}'
- '{% set ns.b = 42 %}'
- '{{ ns.a }}|{{ ns.self }}|{{ ns.b }}')
- assert tmpl.render(d={'a': 13}) == '13|37|42'
+ tmpl = env_trim.from_string(
+ "{% set ns = namespace(d, self=37) %}"
+ "{% set ns.b = 42 %}"
+ "{{ ns.a }}|{{ ns.self }}|{{ ns.b }}"
+ )
+ assert tmpl.render(d={"a": 13}) == "13|37|42"
def test_namespace_loop(self, env_trim):
- tmpl = env_trim.from_string('{% set ns = namespace(found=false) %}'
- '{% for x in range(4) %}'
- '{% if x == v %}'
- '{% set ns.found = true %}'
- '{% endif %}'
- '{% endfor %}'
- '{{ ns.found }}')
- assert tmpl.render(v=3) == 'True'
- assert tmpl.render(v=4) == 'False'
+ tmpl = env_trim.from_string(
+ "{% set ns = namespace(found=false) %}"
+ "{% for x in range(4) %}"
+ "{% if x == v %}"
+ "{% set ns.found = true %}"
+ "{% endif %}"
+ "{% endfor %}"
+ "{{ ns.found }}"
+ )
+ assert tmpl.render(v=3) == "True"
+ assert tmpl.render(v=4) == "False"
def test_namespace_macro(self, env_trim):
- tmpl = env_trim.from_string('{% set ns = namespace() %}'
- '{% set ns.a = 13 %}'
- '{% macro magic(x) %}'
- '{% set x.b = 37 %}'
- '{% endmacro %}'
- '{{ magic(ns) }}'
- '{{ ns.a }}|{{ ns.b }}')
- assert tmpl.render() == '13|37'
+ tmpl = env_trim.from_string(
+ "{% set ns = namespace() %}"
+ "{% set ns.a = 13 %}"
+ "{% macro magic(x) %}"
+ "{% set x.b = 37 %}"
+ "{% endmacro %}"
+ "{{ magic(ns) }}"
+ "{{ ns.a }}|{{ ns.b }}"
+ )
+ assert tmpl.render() == "13|37"
def test_block_escaping_filtered(self):
env = Environment(autoescape=True)
- tmpl = env.from_string('{% set foo | trim %}<em>{{ test }}</em> '
- '{% endset %}foo: {{ foo }}')
- assert tmpl.render(test='<unsafe>') == 'foo: <em>&lt;unsafe&gt;</em>'
+ tmpl = env.from_string(
+ "{% set foo | trim %}<em>{{ test }}</em> {% endset %}foo: {{ foo }}"
+ )
+ assert tmpl.render(test="<unsafe>") == "foo: <em>&lt;unsafe&gt;</em>"
def test_block_filtered(self, env_trim):
tmpl = env_trim.from_string(
- '{% set foo | trim | length | string %} 42 {% endset %}'
- '{{ foo }}')
- assert tmpl.render() == '2'
- assert tmpl.module.foo == u'2'
+ "{% set foo | trim | length | string %} 42 {% endset %}{{ foo }}"
+ )
+ assert tmpl.render() == "2"
+ assert tmpl.module.foo == u"2"
def test_block_filtered_set(self, env_trim):
def _myfilter(val, arg):
- assert arg == ' xxx '
+ assert arg == " xxx "
return val
- env_trim.filters['myfilter'] = _myfilter
+
+ env_trim.filters["myfilter"] = _myfilter
tmpl = env_trim.from_string(
'{% set a = " xxx " %}'
- '{% set foo | myfilter(a) | trim | length | string %}'
+ "{% set foo | myfilter(a) | trim | length | string %}"
' {% set b = " yy " %} 42 {{ a }}{{ b }} '
- '{% endset %}'
- '{{ foo }}')
- assert tmpl.render() == '11'
- assert tmpl.module.foo == u'11'
+ "{% endset %}"
+ "{{ foo }}"
+ )
+ assert tmpl.render() == "11"
+ assert tmpl.module.foo == u"11"
@pytest.mark.core_tags
@pytest.mark.with_
class TestWith(object):
-
def test_with(self, env):
- tmpl = env.from_string('''\
+ tmpl = env.from_string(
+ """\
{% with a=42, b=23 -%}
{{ a }} = {{ b }}
{% endwith -%}
{{ a }} = {{ b }}\
- ''')
- assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] \
- == ['42 = 23', '1 = 2']
+ """
+ )
+ assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] == [
+ "42 = 23",
+ "1 = 2",
+ ]
def test_with_argument_scoping(self, env):
- tmpl = env.from_string('''\
+ tmpl = env.from_string(
+ """\
{%- with a=1, b=2, c=b, d=e, e=5 -%}
{{ a }}|{{ b }}|{{ c }}|{{ d }}|{{ e }}
{%- endwith -%}
- ''')
- assert tmpl.render(b=3, e=4) == '1|2|3|4|5'
+ """
+ )
+ assert tmpl.render(b=3, e=4) == "1|2|3|4|5"
diff --git a/tests/test_debug.py b/tests/test_debug.py
index 78583de..e19a540 100644
--- a/tests/test_debug.py
+++ b/tests/test_debug.py
@@ -23,55 +23,66 @@ from jinja2 import TemplateSyntaxError
@pytest.fixture
def fs_env(filesystem_loader):
- '''returns a new environment.
- '''
+ """returns a new environment."""
return Environment(loader=filesystem_loader)
@pytest.mark.debug
class TestDebug(object):
-
def assert_traceback_matches(self, callback, expected_tb):
try:
callback()
- except Exception as e:
+ except Exception:
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))
+ 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'
+ 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'''
+
+ 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):
# The trailing .*? is for PyPy 2 and 3, which don't seem to
# clear the exception's original traceback, leaving the syntax
# error in the middle of other compiler frames.
- self.assert_traceback_matches(lambda: fs_env.get_template('syntaxerror.html'), r'''(?sm)
+ 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'''
+ raise TemplateSyntaxError("wtf", 42)
+
+ self.assert_traceback_matches(
+ test,
+ r"""
File ".*debug.pyc?", line \d+, in test
- raise TemplateSyntaxError\('wtf', 42\)
+ raise TemplateSyntaxError\("wtf", 42\)
(jinja2\.exceptions\.)?TemplateSyntaxError: wtf
- line 42''')
+ line 42""",
+ )
def test_pickleable_syntax_error(self, fs_env):
original = TemplateSyntaxError("bad template", 42, "test", "test.txt")
@@ -80,12 +91,14 @@ ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero
assert original.name == unpickled.name
def test_include_syntax_error_source(self, filesystem_loader):
- e = Environment(loader=ChoiceLoader(
- [
- filesystem_loader,
- DictLoader({"inc": "a\n{% include 'syntaxerror.html' %}\nb"}),
- ]
- ))
+ e = Environment(
+ loader=ChoiceLoader(
+ [
+ filesystem_loader,
+ DictLoader({"inc": "a\n{% include 'syntaxerror.html' %}\nb"}),
+ ]
+ )
+ )
t = e.get_template("inc")
with pytest.raises(TemplateSyntaxError) as exc_info:
@@ -96,16 +109,19 @@ ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero
def test_local_extraction(self):
from jinja2.debug import get_template_locals
from jinja2.runtime import missing
- locals = get_template_locals({
- 'l_0_foo': 42,
- 'l_1_foo': 23,
- 'l_2_foo': 13,
- 'l_0_bar': 99,
- 'l_1_bar': missing,
- 'l_0_baz': missing,
- })
- assert locals == {'foo': 13, 'bar': 99}
+
+ locals = get_template_locals(
+ {
+ "l_0_foo": 42,
+ "l_1_foo": 23,
+ "l_2_foo": 13,
+ "l_0_bar": 99,
+ "l_1_bar": missing,
+ "l_0_baz": missing,
+ }
+ )
+ assert locals == {"foo": 13, "bar": 99}
def test_get_corresponding_lineno_traceback(self, fs_env):
- tmpl = fs_env.get_template('test.html')
+ tmpl = fs_env.get_template("test.html")
assert tmpl.get_corresponding_lineno(1) == 1
diff --git a/tests/test_ext.py b/tests/test_ext.py
index 6e8ab14..0a9b648 100644
--- a/tests/test_ext.py
+++ b/tests/test_ext.py
@@ -26,113 +26,112 @@ from jinja2.lexer import Token
importable_object = 23
-_gettext_re = re.compile(r'_\((.*?)\)', re.DOTALL)
+_gettext_re = re.compile(r"_\((.*?)\)", re.DOTALL)
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) }}'
+ "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 %}'
+ "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'
+ "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')
+ language = context.get("LANGUAGE", "en")
return languages.get(language, {}).get(string, string)
@contextfunction
def ngettext(context, s, p, n):
- language = context.get('LANGUAGE', 'en')
+ 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']
+ loader=DictLoader(i18n_templates), extensions=["jinja2.ext.i18n"]
+)
+i18n_env.globals.update({"_": gettext, "gettext": gettext, "ngettext": ngettext})
+i18n_env_trimmed = Environment(extensions=["jinja2.ext.i18n"])
+i18n_env_trimmed.policies["ext.i18n.trimmed"] = True
+i18n_env_trimmed.globals.update(
+ {"_": gettext, "gettext": gettext, "ngettext": ngettext}
)
-i18n_env.globals.update({
- '_': gettext,
- 'gettext': gettext,
- 'ngettext': ngettext
-})
-i18n_env_trimmed = Environment(extensions=['jinja2.ext.i18n'])
-i18n_env_trimmed.policies['ext.i18n.trimmed'] = True
-i18n_env_trimmed.globals.update({
- '_': gettext,
- 'gettext': gettext,
- 'ngettext': ngettext
-})
newstyle_i18n_env = Environment(
- loader=DictLoader(newstyle_i18n_templates),
- extensions=['jinja2.ext.i18n']
+ loader=DictLoader(newstyle_i18n_templates), extensions=["jinja2.ext.i18n"]
)
newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True)
class ExampleExtension(Extension):
- tags = set(['test'])
+ tags = set(["test"])
ext_attr = 42
context_reference_node_cls = nodes.ContextReference
def parse(self, parser):
- return nodes.Output([self.call_method('_dump', [
- nodes.EnvironmentAttribute('sandboxed'),
- self.attr('ext_attr'),
- nodes.ImportedName(__name__ + '.importable_object'),
- self.context_reference_node_cls()
- ])]).set_lineno(next(parser.stream).lineno)
+ return nodes.Output(
+ [
+ self.call_method(
+ "_dump",
+ [
+ nodes.EnvironmentAttribute("sandboxed"),
+ self.attr("ext_attr"),
+ nodes.ImportedName(__name__ + ".importable_object"),
+ self.context_reference_node_cls(),
+ ],
+ )
+ ]
+ ).set_lineno(next(parser.stream).lineno)
def _dump(self, sandboxed, ext_attr, imported_object, context):
- return '%s|%s|%s|%s|%s' % (
+ return "%s|%s|%s|%s|%s" % (
sandboxed,
ext_attr,
imported_object,
context.blocks,
- context.get('test_var')
+ context.get("test_var"),
)
@@ -141,16 +140,14 @@ class DerivedExampleExtension(ExampleExtension):
class PreprocessorExtension(Extension):
-
def preprocess(self, source, name, filename=None):
- return source.replace('[[TEST]]', '({{ foo }})')
+ return source.replace("[[TEST]]", "({{ foo }})")
class StreamFilterExtension(Extension):
-
def filter_stream(self, stream):
for token in stream:
- if token.type == 'data':
+ if token.type == "data":
for t in self.interpolate(token):
yield t
else:
@@ -164,75 +161,80 @@ class StreamFilterExtension(Extension):
match = _gettext_re.search(token.value, pos)
if match is None:
break
- value = token.value[pos:match.start()]
+ value = token.value[pos : match.start()]
if value:
- yield Token(lineno, 'data', value)
+ yield Token(lineno, "data", value)
lineno += count_newlines(token.value)
- yield Token(lineno, 'variable_begin', None)
- yield Token(lineno, 'name', 'gettext')
- yield Token(lineno, 'lparen', None)
- yield Token(lineno, 'string', match.group(1))
- yield Token(lineno, 'rparen', None)
- yield Token(lineno, 'variable_end', None)
+ yield Token(lineno, "variable_begin", None)
+ yield Token(lineno, "name", "gettext")
+ yield Token(lineno, "lparen", None)
+ yield Token(lineno, "string", match.group(1))
+ yield Token(lineno, "rparen", None)
+ yield Token(lineno, "variable_end", None)
pos = match.end()
if pos < end:
- yield Token(lineno, 'data', token.value[pos:])
+ yield Token(lineno, "data", token.value[pos:])
@pytest.mark.ext
class TestExtensions(object):
-
def test_extend_late(self):
env = Environment()
- env.add_extension('jinja2.ext.autoescape')
- t = env.from_string(
- '{% autoescape true %}{{ "<test>" }}{% endautoescape %}')
- assert t.render() == '&lt;test&gt;'
+ env.add_extension("jinja2.ext.autoescape")
+ t = env.from_string('{% autoescape true %}{{ "<test>" }}{% endautoescape %}')
+ assert t.render() == "&lt;test&gt;"
def test_loop_controls(self):
- env = Environment(extensions=['jinja2.ext.loopcontrols'])
+ env = Environment(extensions=["jinja2.ext.loopcontrols"])
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{%- for item in [1, 2, 3, 4] %}
{%- if item % 2 == 0 %}{% continue %}{% endif -%}
{{ item }}
- {%- endfor %}''')
- assert tmpl.render() == '13'
+ {%- endfor %}"""
+ )
+ assert tmpl.render() == "13"
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{%- for item in [1, 2, 3, 4] %}
{%- if item > 2 %}{% break %}{% endif -%}
{{ item }}
- {%- endfor %}''')
- assert tmpl.render() == '12'
+ {%- endfor %}"""
+ )
+ assert tmpl.render() == "12"
def test_do(self):
- env = Environment(extensions=['jinja2.ext.do'])
- tmpl = env.from_string('''
+ env = Environment(extensions=["jinja2.ext.do"])
+ tmpl = env.from_string(
+ """
{%- set items = [] %}
{%- for char in "foo" %}
{%- do items.append(loop.index0 ~ char) %}
- {%- endfor %}{{ items|join(', ') }}''')
- assert tmpl.render() == '0f, 1o, 2o'
+ {%- endfor %}{{ items|join(', ') }}"""
+ )
+ assert tmpl.render() == "0f, 1o, 2o"
def test_extension_nodes(self):
env = Environment(extensions=[ExampleExtension])
- tmpl = env.from_string('{% test %}')
- assert tmpl.render() == 'False|42|23|{}|None'
+ tmpl = env.from_string("{% test %}")
+ assert tmpl.render() == "False|42|23|{}|None"
def test_contextreference_node_passes_context(self):
env = Environment(extensions=[ExampleExtension])
tmpl = env.from_string('{% set test_var="test_content" %}{% test %}')
- assert tmpl.render() == 'False|42|23|{}|test_content'
+ assert tmpl.render() == "False|42|23|{}|test_content"
def test_contextreference_node_can_pass_locals(self):
env = Environment(extensions=[DerivedExampleExtension])
tmpl = env.from_string(
- '{% for test_var in ["test_content"] %}{% test %}{% endfor %}')
- assert tmpl.render() == 'False|42|23|{}|test_content'
+ '{% for test_var in ["test_content"] %}{% test %}{% endfor %}'
+ )
+ assert tmpl.render() == "False|42|23|{}|test_content"
def test_identifier(self):
- assert ExampleExtension.identifier == __name__ + '.ExampleExtension'
+ assert ExampleExtension.identifier == __name__ + ".ExampleExtension"
def test_rebinding(self):
original = Environment(extensions=[ExampleExtension])
@@ -243,15 +245,15 @@ class TestExtensions(object):
def test_preprocessor_extension(self):
env = Environment(extensions=[PreprocessorExtension])
- tmpl = env.from_string('{[[TEST]]}')
- assert tmpl.render(foo=42) == '{(42)}'
+ tmpl = env.from_string("{[[TEST]]}")
+ assert tmpl.render(foo=42) == "{(42)}"
def test_streamfilter_extension(self):
env = Environment(extensions=[StreamFilterExtension])
- env.globals['gettext'] = lambda x: x.upper()
- tmpl = env.from_string('Foo _(bar) Baz')
+ env.globals["gettext"] = lambda x: x.upper()
+ tmpl = env.from_string("Foo _(bar) Baz")
out = tmpl.render()
- assert out == 'Foo BAR Baz'
+ assert out == "Foo BAR Baz"
def test_extension_ordering(self):
class T1(Extension):
@@ -259,14 +261,15 @@ class TestExtensions(object):
class T2(Extension):
priority = 2
+
env = Environment(extensions=[T1, T2])
ext = list(env.iter_extensions())
assert ext[0].__class__ is T1
assert ext[1].__class__ is T2
def test_debug(self):
- env = Environment(extensions=['jinja2.ext.debug'])
- t = env.from_string('Hello\n{% debug %}\nGoodbye')
+ env = Environment(extensions=["jinja2.ext.debug"])
+ t = env.from_string("Hello\n{% debug %}\nGoodbye")
out = t.render()
for value in ("context", "cycler", "filters", "abs", "tests", "!="):
@@ -275,304 +278,346 @@ class TestExtensions(object):
@pytest.mark.ext
class TestInternationalization(object):
-
def test_trans(self):
- tmpl = i18n_env.get_template('child.html')
- assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf'
+ tmpl = i18n_env.get_template("child.html")
+ assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf"
def test_trans_plural(self):
- tmpl = i18n_env.get_template('plural.html')
- assert tmpl.render(LANGUAGE='de', user_count=1) \
- == 'Ein Benutzer online'
- assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online'
+ tmpl = i18n_env.get_template("plural.html")
+ assert tmpl.render(LANGUAGE="de", user_count=1) == "Ein Benutzer online"
+ assert tmpl.render(LANGUAGE="de", user_count=2) == "2 Benutzer online"
def test_trans_plural_with_functions(self):
- tmpl = i18n_env.get_template('plural2.html')
+ tmpl = i18n_env.get_template("plural2.html")
def get_user_count():
get_user_count.called += 1
return 1
+
get_user_count.called = 0
- assert tmpl.render(LANGUAGE='de', get_user_count=get_user_count) \
- == '1s'
+ assert tmpl.render(LANGUAGE="de", get_user_count=get_user_count) == "1s"
assert get_user_count.called == 1
def test_complex_plural(self):
tmpl = i18n_env.from_string(
- '{% trans foo=42, count=2 %}{{ count }} item{% '
- 'pluralize count %}{{ count }} items{% endtrans %}')
- assert tmpl.render() == '2 items'
- pytest.raises(TemplateAssertionError, i18n_env.from_string,
- '{% trans foo %}...{% pluralize bar %}...{% endtrans %}')
+ "{% trans foo=42, count=2 %}{{ count }} item{% "
+ "pluralize count %}{{ count }} items{% endtrans %}"
+ )
+ assert tmpl.render() == "2 items"
+ pytest.raises(
+ TemplateAssertionError,
+ i18n_env.from_string,
+ "{% trans foo %}...{% pluralize bar %}...{% endtrans %}",
+ )
def test_trans_stringformatting(self):
- tmpl = i18n_env.get_template('stringformat.html')
- assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5'
+ tmpl = i18n_env.get_template("stringformat.html")
+ assert tmpl.render(LANGUAGE="de", user_count=5) == "Benutzer: 5"
def test_trimmed(self):
tmpl = i18n_env.from_string(
- '{%- trans trimmed %} hello\n world {% endtrans -%}')
- assert tmpl.render() == 'hello world'
+ "{%- trans trimmed %} hello\n world {% endtrans -%}"
+ )
+ assert tmpl.render() == "hello world"
def test_trimmed_policy(self):
- s = '{%- trans %} hello\n world {% endtrans -%}'
+ s = "{%- trans %} hello\n world {% endtrans -%}"
tmpl = i18n_env.from_string(s)
trimmed_tmpl = i18n_env_trimmed.from_string(s)
- assert tmpl.render() == ' hello\n world '
- assert trimmed_tmpl.render() == 'hello world'
+ assert tmpl.render() == " hello\n world "
+ assert trimmed_tmpl.render() == "hello world"
def test_trimmed_policy_override(self):
tmpl = i18n_env_trimmed.from_string(
- '{%- trans notrimmed %} hello\n world {% endtrans -%}')
- assert tmpl.render() == ' hello\n world '
+ "{%- trans notrimmed %} hello\n world {% endtrans -%}"
+ )
+ assert tmpl.render() == " hello\n world "
def test_trimmed_vars(self):
tmpl = i18n_env.from_string(
- '{%- trans trimmed x="world" %} hello\n {{ x }} {% endtrans -%}')
- assert tmpl.render() == 'hello world'
+ '{%- trans trimmed x="world" %} hello\n {{ x }} {% endtrans -%}'
+ )
+ assert tmpl.render() == "hello world"
def test_trimmed_varname_trimmed(self):
# unlikely variable name, but when used as a variable
# it should not enable trimming
tmpl = i18n_env.from_string(
- '{%- trans trimmed = "world" %} hello\n {{ trimmed }} '
- '{% endtrans -%}')
- assert tmpl.render() == ' hello\n world '
+ '{%- trans trimmed = "world" %} hello\n {{ trimmed }} ' "{% endtrans -%}"
+ )
+ assert tmpl.render() == " hello\n world "
def test_extract(self):
from jinja2.ext import babel_extract
- source = BytesIO('''
+
+ 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), [])
+ """.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_extract_trimmed(self):
from jinja2.ext import babel_extract
- source = BytesIO('''
+
+ source = BytesIO(
+ """
{{ gettext(' Hello \n World') }}
{% trans trimmed %} Hello \n World{% endtrans %}
{% trans trimmed %}{{ users }} \n user
{%- pluralize %}{{ users }} \n users{% endtrans %}
- '''.encode('ascii')) # make python 3 happy
- assert list(babel_extract(source,
- ('gettext', 'ngettext', '_'), [], {})) == [
- (2, 'gettext', u' Hello \n World', []),
- (4, 'gettext', u'Hello World', []),
- (6, 'ngettext', (u'%(users)s user', u'%(users)s users', None), [])
+ """.encode(
+ "ascii"
+ )
+ ) # make python 3 happy
+ assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], {})) == [
+ (2, "gettext", u" Hello \n World", []),
+ (4, "gettext", u"Hello World", []),
+ (6, "ngettext", (u"%(users)s user", u"%(users)s users", None), []),
]
def test_extract_trimmed_option(self):
from jinja2.ext import babel_extract
- source = BytesIO('''
+
+ source = BytesIO(
+ """
{{ gettext(' Hello \n World') }}
{% trans %} Hello \n World{% endtrans %}
{% trans %}{{ users }} \n user
{%- pluralize %}{{ users }} \n users{% endtrans %}
- '''.encode('ascii')) # make python 3 happy
- opts = {'trimmed': 'true'}
- assert list(babel_extract(source,
- ('gettext', 'ngettext', '_'), [], opts)) == [
- (2, 'gettext', u' Hello \n World', []),
- (4, 'gettext', u'Hello World', []),
- (6, 'ngettext', (u'%(users)s user', u'%(users)s users', None), [])
+ """.encode(
+ "ascii"
+ )
+ ) # make python 3 happy
+ opts = {"trimmed": "true"}
+ assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], opts)) == [
+ (2, "gettext", u" Hello \n World", []),
+ (4, "gettext", u"Hello World", []),
+ (6, "ngettext", (u"%(users)s user", u"%(users)s users", None), []),
]
def test_comment_extract(self):
from jinja2.ext import babel_extract
- source = BytesIO('''
+
+ 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'])
+ """.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 TestScope(object):
-
def test_basic_scope_behavior(self):
# This is what the old with statement compiled down to
class ScopeExt(Extension):
- tags = set(['scope'])
+ tags = set(["scope"])
def parse(self, parser):
node = nodes.Scope(lineno=next(parser.stream).lineno)
assignments = []
- while parser.stream.current.type != 'block_end':
+ while parser.stream.current.type != "block_end":
lineno = parser.stream.current.lineno
if assignments:
- parser.stream.expect('comma')
+ parser.stream.expect("comma")
target = parser.parse_assign_target()
- parser.stream.expect('assign')
+ parser.stream.expect("assign")
expr = parser.parse_expression()
assignments.append(nodes.Assign(target, expr, lineno=lineno))
- node.body = assignments + \
- list(parser.parse_statements(('name:endscope',),
- drop_needle=True))
+ node.body = assignments + list(
+ parser.parse_statements(("name:endscope",), drop_needle=True)
+ )
return node
env = Environment(extensions=[ScopeExt])
- tmpl = env.from_string('''\
+ tmpl = env.from_string(
+ """\
{%- scope a=1, b=2, c=b, d=e, e=5 -%}
{{ a }}|{{ b }}|{{ c }}|{{ d }}|{{ e }}
{%- endscope -%}
- ''')
- assert tmpl.render(b=3, e=4) == '1|2|2|4|5'
+ """
+ )
+ assert tmpl.render(b=3, e=4) == "1|2|2|4|5"
@pytest.mark.ext
class TestNewstyleInternationalization(object):
-
def test_trans(self):
- tmpl = newstyle_i18n_env.get_template('child.html')
- assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf'
+ tmpl = newstyle_i18n_env.get_template("child.html")
+ assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf"
def test_trans_plural(self):
- tmpl = newstyle_i18n_env.get_template('plural.html')
- assert tmpl.render(LANGUAGE='de', user_count=1) \
- == 'Ein Benutzer online'
- assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online'
+ tmpl = newstyle_i18n_env.get_template("plural.html")
+ assert tmpl.render(LANGUAGE="de", user_count=1) == "Ein Benutzer online"
+ assert tmpl.render(LANGUAGE="de", user_count=2) == "2 Benutzer online"
def test_complex_plural(self):
tmpl = newstyle_i18n_env.from_string(
- '{% trans foo=42, count=2 %}{{ count }} item{% '
- 'pluralize count %}{{ count }} items{% endtrans %}')
- assert tmpl.render() == '2 items'
- pytest.raises(TemplateAssertionError, i18n_env.from_string,
- '{% trans foo %}...{% pluralize bar %}...{% endtrans %}')
+ "{% trans foo=42, count=2 %}{{ count }} item{% "
+ "pluralize count %}{{ count }} items{% endtrans %}"
+ )
+ assert tmpl.render() == "2 items"
+ pytest.raises(
+ TemplateAssertionError,
+ i18n_env.from_string,
+ "{% trans foo %}...{% pluralize bar %}...{% endtrans %}",
+ )
def test_trans_stringformatting(self):
- tmpl = newstyle_i18n_env.get_template('stringformat.html')
- assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5'
+ tmpl = newstyle_i18n_env.get_template("stringformat.html")
+ assert tmpl.render(LANGUAGE="de", user_count=5) == "Benutzer: 5"
def test_newstyle_plural(self):
- tmpl = newstyle_i18n_env.get_template('ngettext.html')
- assert tmpl.render(LANGUAGE='de', apples=1) == '1 Apfel'
- assert tmpl.render(LANGUAGE='de', apples=5) == u'5 Äpfel'
+ tmpl = newstyle_i18n_env.get_template("ngettext.html")
+ assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apfel"
+ assert tmpl.render(LANGUAGE="de", apples=5) == u"5 Äpfel"
def test_autoescape_support(self):
- env = Environment(extensions=['jinja2.ext.autoescape',
- 'jinja2.ext.i18n'])
+ 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: &lt;test&gt;</strong>'
- assert t.render(ae=False) == '<strong>Wert: <test></strong>'
+ 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: &lt;test&gt;</strong>"
+ assert t.render(ae=False) == "<strong>Wert: <test></strong>"
def test_autoescape_macros(self):
- env = Environment(autoescape=False, extensions=['jinja2.ext.autoescape'])
+ env = Environment(autoescape=False, extensions=["jinja2.ext.autoescape"])
template = (
- '{% macro m() %}<html>{% endmacro %}'
- '{% autoescape true %}{{ m() }}{% endautoescape %}'
+ "{% macro m() %}<html>{% endmacro %}"
+ "{% autoescape true %}{{ m() }}{% endautoescape %}"
)
- assert env.from_string(template).render() == '<html>'
+ assert env.from_string(template).render() == "<html>"
def test_num_used_twice(self):
- tmpl = newstyle_i18n_env.get_template('ngettext_long.html')
- assert tmpl.render(apples=5, LANGUAGE='de') == u'5 Äpfel'
+ 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('''
+ source = newstyle_i18n_env.compile(
+ """
{% trans num=3 %}{{ num }} apple{% pluralize
%}{{ num }} apples{% endtrans %}
- ''', raw=True)
+ """,
+ raw=True,
+ )
# quite hacky, but the only way to properly test that. The idea is
# that the generated code does not pass num twice (although that
# would work) for better performance. This only works on the
# newstyle gettext of course
- assert re.search(r"u?'\%\(num\)s apple', u?'\%\(num\)s "
- r"apples', 3", source) is not None
+ assert (
+ re.search(r"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'
+ 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'
+ 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(object):
-
def test_scoped_setting(self):
- env = Environment(extensions=['jinja2.ext.autoescape'],
- autoescape=True)
- tmpl = env.from_string('''
+ env = Environment(extensions=["jinja2.ext.autoescape"], autoescape=True)
+ tmpl = env.from_string(
+ """
{{ "<HelloWorld>" }}
{% autoescape false %}
{{ "<HelloWorld>" }}
{% endautoescape %}
{{ "<HelloWorld>" }}
- ''')
- assert tmpl.render().split() == \
- [u'&lt;HelloWorld&gt;', u'<HelloWorld>', u'&lt;HelloWorld&gt;']
+ """
+ )
+ assert tmpl.render().split() == [
+ u"&lt;HelloWorld&gt;",
+ u"<HelloWorld>",
+ u"&lt;HelloWorld&gt;",
+ ]
- env = Environment(extensions=['jinja2.ext.autoescape'],
- autoescape=False)
- tmpl = env.from_string('''
+ 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'&lt;HelloWorld&gt;', u'<HelloWorld>']
+ """
+ )
+ assert tmpl.render().split() == [
+ u"<HelloWorld>",
+ u"&lt;HelloWorld&gt;",
+ u"<HelloWorld>",
+ ]
def test_nonvolatile(self):
- env = Environment(extensions=['jinja2.ext.autoescape'],
- autoescape=True)
+ env = Environment(extensions=["jinja2.ext.autoescape"], autoescape=True)
tmpl = env.from_string('{{ {"foo": "<test>"}|xmlattr|escape }}')
assert tmpl.render() == ' foo="&lt;test&gt;"'
- tmpl = env.from_string('{% autoescape false %}{{ {"foo": "<test>"}'
- '|xmlattr|escape }}{% endautoescape %}')
- assert tmpl.render() == ' foo=&#34;&amp;lt;test&amp;gt;&#34;'
+ tmpl = env.from_string(
+ '{% autoescape false %}{{ {"foo": "<test>"}'
+ "|xmlattr|escape }}{% endautoescape %}"
+ )
+ assert tmpl.render() == " foo=&#34;&amp;lt;test&amp;gt;&#34;"
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=&#34;&amp;lt;test&amp;gt;&#34;'
+ 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=&#34;&amp;lt;test&amp;gt;&#34;"
assert tmpl.render(foo=True) == ' foo="&lt;test&gt;"'
def test_scoping(self):
- env = Environment(extensions=['jinja2.ext.autoescape'])
+ env = Environment(extensions=["jinja2.ext.autoescape"])
tmpl = env.from_string(
'{% autoescape true %}{% set x = "<x>" %}{{ x }}'
- '{% endautoescape %}{{ x }}{{ "<y>" }}')
- assert tmpl.render(x=1) == '&lt;x&gt;1<y>'
+ '{% endautoescape %}{{ x }}{{ "<y>" }}'
+ )
+ assert tmpl.render(x=1) == "&lt;x&gt;1<y>"
def test_volatile_scoping(self):
- env = Environment(extensions=['jinja2.ext.autoescape'])
- tmplsource = '''
+ env = Environment(extensions=["jinja2.ext.autoescape"])
+ tmplsource = """
{% autoescape val %}
{% macro foo(x) %}
[{{ x }}]
@@ -580,41 +625,45 @@ class TestAutoEscape(object):
{{ foo().__class__.__name__ }}
{% endautoescape %}
{{ '<testing>' }}
- '''
+ """
tmpl = env.from_string(tmplsource)
- assert tmpl.render(val=True).split()[0] == 'Markup'
+ 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'])
+ env = Environment(extensions=["jinja2.ext.autoescape"])
pysource = env.compile(tmplsource, raw=True)
- assert '<testing>\\n' in pysource
+ assert "<testing>\\n" in pysource
- env = Environment(extensions=['jinja2.ext.autoescape'],
- autoescape=True)
+ env = Environment(extensions=["jinja2.ext.autoescape"], autoescape=True)
pysource = env.compile(tmplsource, raw=True)
- assert '&lt;testing&gt;\\n' in pysource
+ assert "&lt;testing&gt;\\n" in pysource
def test_overlay_scopes(self):
class MagicScopeExtension(Extension):
- tags = set(['overlay'])
+ tags = set(["overlay"])
+
def parse(self, parser):
node = nodes.OverlayScope(lineno=next(parser.stream).lineno)
- node.body = list(parser.parse_statements(('name:endoverlay',),
- drop_needle=True))
- node.context = self.call_method('get_scope')
+ node.body = list(
+ parser.parse_statements(("name:endoverlay",), drop_needle=True)
+ )
+ node.context = self.call_method("get_scope")
return node
+
def get_scope(self):
- return {'x': [1, 2, 3]}
+ return {"x": [1, 2, 3]}
env = Environment(extensions=[MagicScopeExtension])
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{{- x }}|{% set z = 99 %}
{%- overlay %}
{{- y }}|{{ z }}|{% for item in x %}[{{ item }}]{% endfor %}
{%- endoverlay %}|
{{- x -}}
- ''')
- assert tmpl.render(x=42, y=23) == '42|23|99|[1][2][3]|42'
+ """
+ )
+ assert tmpl.render(x=42, y=23) == "42|23|99|[1][2][3]|42"
diff --git a/tests/test_features.py b/tests/test_features.py
index d9ee4e5..0d7baed 100644
--- a/tests/test_features.py
+++ b/tests/test_features.py
@@ -7,37 +7,35 @@ from jinja2 import Environment
from jinja2 import Template
-@pytest.mark.skipif(sys.version_info < (3, 5),
- reason='Requires 3.5 or later')
+@pytest.mark.skipif(sys.version_info < (3, 5), reason="Requires 3.5 or later")
def test_generator_stop():
class X(object):
def __getattr__(self, name):
raise StopIteration()
- t = Template('a{{ bad.bar() }}b')
+ t = Template("a{{ bad.bar() }}b")
with pytest.raises(RuntimeError):
t.render(bad=X())
-@pytest.mark.skipif(sys.version_info[0] > 2,
- reason='Feature only supported on 2.x')
+@pytest.mark.skipif(sys.version_info[0] > 2, reason="Feature only supported on 2.x")
def test_ascii_str():
@contextfilter
def assert_func(context, value):
- assert type(value) is context['expected_type']
+ assert type(value) is context["expected_type"]
env = Environment()
- env.filters['assert'] = assert_func
+ env.filters["assert"] = assert_func
- env.policies['compiler.ascii_str'] = False
+ env.policies["compiler.ascii_str"] = False
t = env.from_string('{{ "foo"|assert }}')
t.render(expected_type=unicode)
- env.policies['compiler.ascii_str'] = True
+ env.policies["compiler.ascii_str"] = True
t = env.from_string('{{ "foo"|assert }}')
t.render(expected_type=str)
for val in True, False:
- env.policies['compiler.ascii_str'] = val
+ env.policies["compiler.ascii_str"] = val
t = env.from_string(u'{{ "\N{SNOWMAN}"|assert }}')
t.render(expected_type=unicode)
diff --git a/tests/test_filters.py b/tests/test_filters.py
index b56e35b..3f74c27 100644
--- a/tests/test_filters.py
+++ b/tests/test_filters.py
@@ -34,60 +34,66 @@ class Magic2(object):
self.value2 = value2
def __str__(self):
- return u'(%s,%s)' % (text_type(self.value1), text_type(self.value2))
+ return u"(%s,%s)" % (text_type(self.value1), text_type(self.value2))
@pytest.mark.filter
class TestFilter(object):
-
def test_filter_calling(self, env):
- rv = env.call_filter('sum', [1, 2, 3])
+ rv = env.call_filter("sum", [1, 2, 3])
assert rv == 6
def test_capitalize(self, env):
tmpl = env.from_string('{{ "foo bar"|capitalize }}')
- assert tmpl.render() == 'Foo bar'
+ assert tmpl.render() == "Foo bar"
def test_center(self, env):
tmpl = env.from_string('{{ "foo"|center(9) }}')
- assert tmpl.render() == ' foo '
+ assert tmpl.render() == " foo "
def test_default(self, env):
tmpl = env.from_string(
"{{ missing|default('no') }}|{{ false|default('no') }}|"
"{{ false|default('no', true) }}|{{ given|default('no') }}"
)
- assert tmpl.render(given='yes') == 'no|False|no|yes'
+ assert tmpl.render(given="yes") == "no|False|no|yes"
- @pytest.mark.parametrize('args,expect', (
- ('', "[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]"),
- ('true', "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]"),
- ('by="value"', "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]"),
- ('reverse=true', "[('c', 2), ('b', 1), ('AB', 3), ('aa', 0)]")
- ))
+ @pytest.mark.parametrize(
+ "args,expect",
+ (
+ ("", "[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]"),
+ ("true", "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]"),
+ ('by="value"', "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]"),
+ ("reverse=true", "[('c', 2), ('b', 1), ('AB', 3), ('aa', 0)]"),
+ ),
+ )
def test_dictsort(self, env, args, expect):
- t = env.from_string('{{{{ foo|dictsort({args}) }}}}'.format(args=args))
+ t = env.from_string("{{{{ foo|dictsort({args}) }}}}".format(args=args))
out = t.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3})
assert out == expect
def test_batch(self, env):
- tmpl = env.from_string("{{ foo|batch(3)|list }}|"
- "{{ foo|batch(3, 'X')|list }}")
+ tmpl = env.from_string("{{ foo|batch(3)|list }}|{{ foo|batch(3, 'X')|list }}")
out = tmpl.render(foo=list(range(10)))
- assert out == ("[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|"
- "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]")
+ assert out == (
+ "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|"
+ "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]"
+ )
def test_slice(self, env):
- tmpl = env.from_string('{{ foo|slice(3)|list }}|'
- '{{ foo|slice(3, "X")|list }}')
+ tmpl = env.from_string(
+ "{{ foo|slice(3)|list }}|" '{{ foo|slice(3, "X")|list }}'
+ )
out = tmpl.render(foo=list(range(10)))
- assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
- "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]")
+ assert out == (
+ "[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
+ "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]"
+ )
def test_escape(self, env):
- tmpl = env.from_string('''{{ '<">&'|escape }}''')
+ tmpl = env.from_string("""{{ '<">&'|escape }}""")
out = tmpl.render()
- assert out == '&lt;&#34;&gt;&amp;'
+ assert out == "&lt;&#34;&gt;&amp;"
@pytest.mark.parametrize(
("chars", "expect"), [(None, "..stays.."), (".", " ..stays"), (" .", "stays")]
@@ -98,52 +104,53 @@ class TestFilter(object):
assert out == expect
def test_striptags(self, env):
- tmpl = env.from_string('''{{ foo|striptags }}''')
- out = tmpl.render(foo=' <p>just a small \n <a href="#">'
- 'example</a> link</p>\n<p>to a webpage</p> '
- '<!-- <p>and some commented stuff</p> -->')
- assert out == 'just a small example link to a webpage'
+ tmpl = env.from_string("""{{ foo|striptags }}""")
+ out = tmpl.render(
+ foo=' <p>just a small \n <a href="#">'
+ "example</a> link</p>\n<p>to a webpage</p> "
+ "<!-- <p>and some commented stuff</p> -->"
+ )
+ assert out == "just a small example link to a webpage"
def test_filesizeformat(self, env):
tmpl = env.from_string(
- '{{ 100|filesizeformat }}|'
- '{{ 1000|filesizeformat }}|'
- '{{ 1000000|filesizeformat }}|'
- '{{ 1000000000|filesizeformat }}|'
- '{{ 1000000000000|filesizeformat }}|'
- '{{ 100|filesizeformat(true) }}|'
- '{{ 1000|filesizeformat(true) }}|'
- '{{ 1000000|filesizeformat(true) }}|'
- '{{ 1000000000|filesizeformat(true) }}|'
- '{{ 1000000000000|filesizeformat(true) }}'
+ "{{ 100|filesizeformat }}|"
+ "{{ 1000|filesizeformat }}|"
+ "{{ 1000000|filesizeformat }}|"
+ "{{ 1000000000|filesizeformat }}|"
+ "{{ 1000000000000|filesizeformat }}|"
+ "{{ 100|filesizeformat(true) }}|"
+ "{{ 1000|filesizeformat(true) }}|"
+ "{{ 1000000|filesizeformat(true) }}|"
+ "{{ 1000000000|filesizeformat(true) }}|"
+ "{{ 1000000000000|filesizeformat(true) }}"
)
out = tmpl.render()
assert out == (
- '100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|'
- '1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB'
+ "100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|"
+ "1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB"
)
def test_filesizeformat_issue59(self, env):
tmpl = env.from_string(
- '{{ 300|filesizeformat }}|'
- '{{ 3000|filesizeformat }}|'
- '{{ 3000000|filesizeformat }}|'
- '{{ 3000000000|filesizeformat }}|'
- '{{ 3000000000000|filesizeformat }}|'
- '{{ 300|filesizeformat(true) }}|'
- '{{ 3000|filesizeformat(true) }}|'
- '{{ 3000000|filesizeformat(true) }}'
+ "{{ 300|filesizeformat }}|"
+ "{{ 3000|filesizeformat }}|"
+ "{{ 3000000|filesizeformat }}|"
+ "{{ 3000000000|filesizeformat }}|"
+ "{{ 3000000000000|filesizeformat }}|"
+ "{{ 300|filesizeformat(true) }}|"
+ "{{ 3000|filesizeformat(true) }}|"
+ "{{ 3000000|filesizeformat(true) }}"
)
out = tmpl.render()
assert out == (
- '300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|'
- '2.9 KiB|2.9 MiB'
+ "300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|2.9 KiB|2.9 MiB"
)
def test_first(self, env):
- tmpl = env.from_string('{{ foo|first }}')
+ tmpl = env.from_string("{{ foo|first }}")
out = tmpl.render(foo=list(range(10)))
- assert out == '0'
+ assert out == "0"
@pytest.mark.parametrize(
("value", "expect"), (("42", "42.0"), ("abc", "0.0"), ("32.32", "32.32"),)
@@ -157,37 +164,37 @@ class TestFilter(object):
assert t.render(value="abc") == "1.0"
def test_format(self, env):
- tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''')
+ tmpl = env.from_string("""{{ "%s|%s"|format("a", "b") }}""")
out = tmpl.render()
- assert out == 'a|b'
+ assert out == "a|b"
@staticmethod
def _test_indent_multiline_template(env, markup=False):
- text = '\n'.join(['', 'foo bar', '"baz"', ''])
+ text = "\n".join(["", "foo bar", '"baz"', ""])
if markup:
text = Markup(text)
- t = env.from_string('{{ foo|indent(2, false, false) }}')
+ t = env.from_string("{{ foo|indent(2, false, false) }}")
assert t.render(foo=text) == '\n foo bar\n "baz"\n'
- t = env.from_string('{{ foo|indent(2, false, true) }}')
+ t = env.from_string("{{ foo|indent(2, false, true) }}")
assert t.render(foo=text) == '\n foo bar\n "baz"\n '
- t = env.from_string('{{ foo|indent(2, true, false) }}')
+ t = env.from_string("{{ foo|indent(2, true, false) }}")
assert t.render(foo=text) == ' \n foo bar\n "baz"\n'
- t = env.from_string('{{ foo|indent(2, true, true) }}')
+ t = env.from_string("{{ foo|indent(2, true, true) }}")
assert t.render(foo=text) == ' \n foo bar\n "baz"\n '
def test_indent(self, env):
self._test_indent_multiline_template(env)
t = env.from_string('{{ "jinja"|indent }}')
- assert t.render() == 'jinja'
+ assert t.render() == "jinja"
t = env.from_string('{{ "jinja"|indent(first=true) }}')
- assert t.render() == ' jinja'
+ assert t.render() == " jinja"
t = env.from_string('{{ "jinja"|indent(blank=true) }}')
- assert t.render() == 'jinja'
+ assert t.render() == "jinja"
def test_indent_markup_input(self, env):
- '''
+ """
Tests cases where the filter input is a Markup type
- '''
+ """
self._test_indent_multiline_template(env, markup=True)
def test_indentfirst_deprecated(self, env):
@@ -201,7 +208,7 @@ class TestFilter(object):
("abc", "0"),
("32.32", "32"),
("12345678901234567890", "12345678901234567890"),
- )
+ ),
)
def test_int(self, env, value, expect):
t = env.from_string("{{ '%s'|int }}" % value)
@@ -209,11 +216,7 @@ class TestFilter(object):
@pytest.mark.parametrize(
("value", "base", "expect"),
- (
- ("0x4d32", 16, "19762"),
- ("011", 8, "9"),
- ("0x33Z", 16, "0"),
- )
+ (("0x4d32", 16, "19762"), ("011", 8, "9"), ("0x33Z", 16, "0"),),
)
def test_int_base(self, env, value, base, expect):
t = env.from_string("{{ '%s'|int(base=%d) }}" % (value, base))
@@ -234,38 +237,39 @@ class TestFilter(object):
def test_join(self, env):
tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}')
out = tmpl.render()
- assert out == '1|2|3'
+ assert out == "1|2|3"
env2 = Environment(autoescape=True)
- tmpl = env2.from_string(
- '{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
- assert tmpl.render() == '&lt;foo&gt;<span>foo</span>'
+ tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
+ assert tmpl.render() == "&lt;foo&gt;<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'
+
+ tmpl = env.from_string("""{{ users|join(', ', 'username') }}""")
+ assert tmpl.render(users=map(User, ["foo", "bar"])) == "foo, bar"
def test_last(self, env):
- tmpl = env.from_string('''{{ foo|last }}''')
+ tmpl = env.from_string("""{{ foo|last }}""")
out = tmpl.render(foo=list(range(10)))
- assert out == '9'
+ assert out == "9"
def test_length(self, env):
- tmpl = env.from_string('''{{ "hello world"|length }}''')
+ tmpl = env.from_string("""{{ "hello world"|length }}""")
out = tmpl.render()
- assert out == '11'
+ assert out == "11"
def test_lower(self, env):
- tmpl = env.from_string('''{{ "FOO"|lower }}''')
+ tmpl = env.from_string("""{{ "FOO"|lower }}""")
out = tmpl.render()
- assert out == 'foo'
+ assert out == "foo"
def test_pprint(self, env):
from pprint import pformat
- tmpl = env.from_string('''{{ data|pprint }}''')
+
+ tmpl = env.from_string("""{{ data|pprint }}""")
data = list(range(1000))
assert tmpl.render(data=data) == pformat(data)
@@ -274,177 +278,173 @@ class TestFilter(object):
state = random.getstate()
request.addfinalizer(lambda: random.setstate(state))
# generate the random values from a known seed
- random.seed('jinja')
- expected = [random.choice('1234567890') for _ in range(10)]
+ random.seed("jinja")
+ expected = [random.choice("1234567890") for _ in range(10)]
# check that the random sequence is generated again by a template
# ensures that filter result is not constant folded
- random.seed('jinja')
+ random.seed("jinja")
t = env.from_string('{{ "1234567890"|random }}')
for value in expected:
assert t.render() == value
def test_reverse(self, env):
- tmpl = env.from_string('{{ "foobar"|reverse|join }}|'
- '{{ [1, 2, 3]|reverse|list }}')
- assert tmpl.render() == 'raboof|[3, 2, 1]'
+ tmpl = env.from_string(
+ '{{ "foobar"|reverse|join }}|' "{{ [1, 2, 3]|reverse|list }}"
+ )
+ assert tmpl.render() == "raboof|[3, 2, 1]"
def test_string(self, env):
x = [1, 2, 3, 4, 5]
- tmpl = env.from_string('''{{ obj|string }}''')
+ 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 }}''')
+ tmpl = env.from_string("""{{ "foo bar"|title }}""")
assert tmpl.render() == "Foo Bar"
- tmpl = env.from_string('''{{ "foo's bar"|title }}''')
+ tmpl = env.from_string("""{{ "foo's bar"|title }}""")
assert tmpl.render() == "Foo's Bar"
- tmpl = env.from_string('''{{ "foo bar"|title }}''')
+ tmpl = env.from_string("""{{ "foo bar"|title }}""")
assert tmpl.render() == "Foo Bar"
- tmpl = env.from_string('''{{ "f bar f"|title }}''')
+ tmpl = env.from_string("""{{ "f bar f"|title }}""")
assert tmpl.render() == "F Bar F"
- tmpl = env.from_string('''{{ "foo-bar"|title }}''')
+ tmpl = env.from_string("""{{ "foo-bar"|title }}""")
assert tmpl.render() == "Foo-Bar"
- tmpl = env.from_string('''{{ "foo\tbar"|title }}''')
+ tmpl = env.from_string("""{{ "foo\tbar"|title }}""")
assert tmpl.render() == "Foo\tBar"
- tmpl = env.from_string('''{{ "FOO\tBAR"|title }}''')
+ tmpl = env.from_string("""{{ "FOO\tBAR"|title }}""")
assert tmpl.render() == "Foo\tBar"
- tmpl = env.from_string('''{{ "foo (bar)"|title }}''')
+ tmpl = env.from_string("""{{ "foo (bar)"|title }}""")
assert tmpl.render() == "Foo (Bar)"
- tmpl = env.from_string('''{{ "foo {bar}"|title }}''')
+ tmpl = env.from_string("""{{ "foo {bar}"|title }}""")
assert tmpl.render() == "Foo {Bar}"
- tmpl = env.from_string('''{{ "foo [bar]"|title }}''')
+ tmpl = env.from_string("""{{ "foo [bar]"|title }}""")
assert tmpl.render() == "Foo [Bar]"
- tmpl = env.from_string('''{{ "foo <bar>"|title }}''')
+ tmpl = env.from_string("""{{ "foo <bar>"|title }}""")
assert tmpl.render() == "Foo <Bar>"
class Foo:
def __str__(self):
- return 'foo-bar'
+ return "foo-bar"
- tmpl = env.from_string('''{{ data|title }}''')
+ tmpl = env.from_string("""{{ data|title }}""")
out = tmpl.render(data=Foo())
- assert out == 'Foo-Bar'
+ assert out == "Foo-Bar"
def test_truncate(self, env):
tmpl = env.from_string(
'{{ data|truncate(15, true, ">>>") }}|'
'{{ data|truncate(15, false, ">>>") }}|'
- '{{ smalldata|truncate(15) }}'
+ "{{ smalldata|truncate(15) }}"
)
- out = tmpl.render(data='foobar baz bar' * 1000,
- smalldata='foobar baz bar')
- msg = 'Current output: %s' % out
- assert out == 'foobar baz b>>>|foobar baz>>>|foobar baz bar', msg
+ 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) }}'
+ '{{ "foo bar baz"|truncate(9) }}|{{ "foo bar baz"|truncate(9, true) }}'
)
out = tmpl.render()
- assert out == 'foo bar baz|foo bar baz', out
+ assert out == "foo bar baz|foo bar baz", out
def test_truncate_end_length(self, env):
tmpl = env.from_string('{{ "Joel is a slug"|truncate(7, true) }}')
out = tmpl.render()
- assert out == 'Joel...', 'Current output: %s' % out
+ assert out == "Joel...", "Current output: %s" % out
def test_upper(self, env):
tmpl = env.from_string('{{ "foo"|upper }}')
- assert tmpl.render() == 'FOO'
+ assert tmpl.render() == "FOO"
def test_urlize(self, env):
- tmpl = env.from_string(
- '{{ "foo http://www.example.com/ bar"|urlize }}')
+ tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}')
assert tmpl.render() == (
'foo <a href="http://www.example.com/" rel="noopener">'
- 'http://www.example.com/</a> bar'
+ "http://www.example.com/</a> bar"
)
def test_urlize_rel_policy(self):
env = Environment()
- env.policies['urlize.rel'] = None
- tmpl = env.from_string(
- '{{ "foo http://www.example.com/ bar"|urlize }}')
+ env.policies["urlize.rel"] = None
+ tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}')
assert tmpl.render() == (
- 'foo <a href="http://www.example.com/">'
- 'http://www.example.com/</a> bar'
+ 'foo <a href="http://www.example.com/">' "http://www.example.com/</a> bar"
)
def test_urlize_target_parameter(self, env):
tmpl = env.from_string(
'{{ "foo http://www.example.com/ bar"|urlize(target="_blank") }}'
)
- assert tmpl.render() \
- == 'foo <a href="http://www.example.com/" rel="noopener" target="_blank">'\
- 'http://www.example.com/</a> bar'
+ assert (
+ tmpl.render()
+ == 'foo <a href="http://www.example.com/" rel="noopener" target="_blank">'
+ "http://www.example.com/</a> bar"
+ )
def test_wordcount(self, env):
tmpl = env.from_string('{{ "foo bar baz"|wordcount }}')
- assert tmpl.render() == '3'
+ assert tmpl.render() == "3"
def test_block(self, env):
- tmpl = env.from_string(
- '{% filter lower|escape %}<HEHE>{% endfilter %}'
- )
- assert tmpl.render() == '&lt;hehe&gt;'
+ tmpl = env.from_string("{% filter lower|escape %}<HEHE>{% endfilter %}")
+ assert tmpl.render() == "&lt;hehe&gt;"
def test_chaining(self, env):
- tmpl = env.from_string(
- '''{{ ['<foo>', '<bar>']|first|upper|escape }}'''
- )
- assert tmpl.render() == '&lt;FOO&gt;'
+ tmpl = env.from_string("""{{ ['<foo>', '<bar>']|first|upper|escape }}""")
+ assert tmpl.render() == "&lt;FOO&gt;"
def test_sum(self, env):
- tmpl = env.from_string('''{{ [1, 2, 3, 4, 5, 6]|sum }}''')
- assert tmpl.render() == '21'
+ tmpl = env.from_string("""{{ [1, 2, 3, 4, 5, 6]|sum }}""")
+ assert tmpl.render() == "21"
def test_sum_attributes(self, env):
- tmpl = env.from_string('''{{ values|sum('value') }}''')
- assert tmpl.render(values=[
- {'value': 23},
- {'value': 1},
- {'value': 18},
- ]) == '42'
+ tmpl = env.from_string("""{{ values|sum('value') }}""")
+ assert tmpl.render(values=[{"value": 23}, {"value": 1}, {"value": 18},]) == "42"
def test_sum_attributes_nested(self, env):
- tmpl = env.from_string('''{{ values|sum('real.value') }}''')
- assert tmpl.render(values=[
- {'real': {'value': 23}},
- {'real': {'value': 1}},
- {'real': {'value': 18}},
- ]) == '42'
+ tmpl = env.from_string("""{{ values|sum('real.value') }}""")
+ assert (
+ tmpl.render(
+ values=[
+ {"real": {"value": 23}},
+ {"real": {"value": 1}},
+ {"real": {"value": 18}},
+ ]
+ )
+ == "42"
+ )
def test_sum_attributes_tuple(self, env):
- tmpl = env.from_string('''{{ values.items()|sum('1') }}''')
- assert tmpl.render(values={
- 'foo': 23,
- 'bar': 1,
- 'baz': 18,
- }) == '42'
+ tmpl = env.from_string("""{{ values.items()|sum('1') }}""")
+ assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18,}) == "42"
def test_abs(self, env):
- tmpl = env.from_string('''{{ -1|abs }}|{{ 1|abs }}''')
- assert tmpl.render() == '1|1', tmpl.render()
+ tmpl = env.from_string("""{{ -1|abs }}|{{ 1|abs }}""")
+ assert tmpl.render() == "1|1", tmpl.render()
def test_round_positive(self, env):
- tmpl = env.from_string('{{ 2.7|round }}|{{ 2.1|round }}|'
- "{{ 2.1234|round(3, 'floor') }}|"
- "{{ 2.1|round(0, 'ceil') }}")
- assert tmpl.render() == '3.0|2.0|2.123|3.0', tmpl.render()
+ tmpl = env.from_string(
+ "{{ 2.7|round }}|{{ 2.1|round }}|"
+ "{{ 2.1234|round(3, 'floor') }}|"
+ "{{ 2.1|round(0, 'ceil') }}"
+ )
+ assert tmpl.render() == "3.0|2.0|2.123|3.0", tmpl.render()
def test_round_negative(self, env):
- tmpl = env.from_string('{{ 21.3|round(-1)}}|'
- "{{ 21.3|round(-1, 'ceil')}}|"
- "{{ 21.3|round(-1, 'floor')}}")
- assert tmpl.render() == '20.0|30.0|20.0', tmpl.render()
+ tmpl = env.from_string(
+ "{{ 21.3|round(-1)}}|"
+ "{{ 21.3|round(-1, 'ceil')}}|"
+ "{{ 21.3|round(-1, 'floor')}}"
+ )
+ assert tmpl.render() == "20.0|30.0|20.0", tmpl.render()
def test_xmlattr(self, env):
tmpl = env.from_string(
"{{ {'foo': 42, 'bar': 23, 'fish': none, "
- "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}")
+ "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}"
+ )
out = tmpl.render().split()
assert len(out) == 3
assert 'foo="42"' in out
@@ -452,44 +452,60 @@ class TestFilter(object):
assert 'blub:blub="&lt;?&gt;"' in out
def test_sort1(self, env):
- tmpl = env.from_string(
- '{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}')
- assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+ tmpl = env.from_string("{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}")
+ assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]"
def test_sort2(self, env):
tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}')
- assert tmpl.render() == 'AbcD'
+ assert tmpl.render() == "AbcD"
def test_sort3(self, env):
- tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''')
+ tmpl = env.from_string("""{{ ['foo', 'Bar', 'blah']|sort }}""")
assert tmpl.render() == "['Bar', 'blah', 'foo']"
def test_sort4(self, env):
- tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''')
- assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234'
+ tmpl = env.from_string("""{{ items|sort(attribute='value')|join }}""")
+ assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == "1234"
def test_sort5(self, env):
- tmpl = env.from_string('''{{ items|sort(attribute='value.0')|join }}''')
- assert tmpl.render(items=map(Magic, [[3], [2], [4], [1]])) == '[1][2][3][4]'
+ tmpl = env.from_string("""{{ items|sort(attribute='value.0')|join }}""")
+ assert tmpl.render(items=map(Magic, [[3], [2], [4], [1]])) == "[1][2][3][4]"
def test_sort6(self, env):
- tmpl = env.from_string('''{{ items|sort(attribute='value1,value2')|join }}''')
- assert (tmpl.render(items=map(
- lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)]))
- == '(2,1)(2,2)(2,5)(3,1)')
+ tmpl = env.from_string("""{{ items|sort(attribute='value1,value2')|join }}""")
+ assert (
+ tmpl.render(
+ items=map(
+ lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)]
+ )
+ )
+ == "(2,1)(2,2)(2,5)(3,1)"
+ )
def test_sort7(self, env):
- tmpl = env.from_string('''{{ items|sort(attribute='value2,value1')|join }}''')
- assert (tmpl.render(items=map(lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)])) ==
- '(2,1)(3,1)(2,2)(2,5)')
+ tmpl = env.from_string("""{{ items|sort(attribute='value2,value1')|join }}""")
+ assert (
+ tmpl.render(
+ items=map(
+ lambda x: Magic2(x[0], x[1]), [(3, 1), (2, 2), (2, 1), (2, 5)]
+ )
+ )
+ == "(2,1)(3,1)(2,2)(2,5)"
+ )
def test_sort8(self, env):
tmpl = env.from_string(
- '''{{ items|sort(attribute='value1.0,value2.0')|join }}''')
- assert (tmpl.render(items=map(
- lambda x: Magic2(x[0], x[1]),
- [([3], [1]), ([2], [2]), ([2], [1]), ([2], [5])]))
- == '([2],[1])([2],[2])([2],[5])([3],[1])')
+ """{{ items|sort(attribute='value1.0,value2.0')|join }}"""
+ )
+ assert (
+ tmpl.render(
+ items=map(
+ lambda x: Magic2(x[0], x[1]),
+ [([3], [1]), ([2], [2]), ([2], [1]), ([2], [5])],
+ )
+ )
+ == "([2],[1])([2],[2])([2],[5])([3],[1])"
+ )
def test_unique(self, env):
t = env.from_string('{{ "".join(["b", "A", "a", "b"]|unique) }}')
@@ -501,49 +517,48 @@ class TestFilter(object):
def test_unique_attribute(self, env):
t = env.from_string("{{ items|unique(attribute='value')|join }}")
- assert t.render(items=map(Magic, [3, 2, 4, 1, 2])) == '3241'
-
- @pytest.mark.parametrize('source,expect', (
- ('{{ ["a", "B"]|min }}', 'a'),
- ('{{ ["a", "B"]|min(case_sensitive=true) }}', 'B'),
- ('{{ []|min }}', ''),
- ('{{ ["a", "B"]|max }}', 'B'),
- ('{{ ["a", "B"]|max(case_sensitive=true) }}', 'a'),
- ('{{ []|max }}', ''),
- ))
+ assert t.render(items=map(Magic, [3, 2, 4, 1, 2])) == "3241"
+
+ @pytest.mark.parametrize(
+ "source,expect",
+ (
+ ('{{ ["a", "B"]|min }}', "a"),
+ ('{{ ["a", "B"]|min(case_sensitive=true) }}', "B"),
+ ("{{ []|min }}", ""),
+ ('{{ ["a", "B"]|max }}', "B"),
+ ('{{ ["a", "B"]|max(case_sensitive=true) }}', "a"),
+ ("{{ []|max }}", ""),
+ ),
+ )
def test_min_max(self, env, source, expect):
t = env.from_string(source)
assert t.render() == expect
- @pytest.mark.parametrize('name,expect', (
- ('min', '1'),
- ('max', '9'),
- ))
+ @pytest.mark.parametrize("name,expect", (("min", "1"), ("max", "9"),))
def test_min_max_attribute(self, env, name, expect):
- t = env.from_string('{{ items|' + name + '(attribute="value") }}')
+ t = env.from_string("{{ items|" + name + '(attribute="value") }}')
assert t.render(items=map(Magic, [5, 1, 9])) == expect
def test_groupby(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{%- for grouper, list in [{'foo': 1, 'bar': 2},
{'foo': 2, 'bar': 3},
{'foo': 1, 'bar': 1},
{'foo': 3, 'bar': 4}]|groupby('foo') -%}
{{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}|
- {%- endfor %}''')
- assert tmpl.render().split('|') == [
- "1: 1, 2: 1, 1",
- "2: 2, 3",
- "3: 3, 4",
- ""
- ]
+ {%- endfor %}"""
+ )
+ assert tmpl.render().split("|") == ["1: 1, 2: 1, 1", "2: 2, 3", "3: 3, 4", ""]
def test_groupby_tuple_index(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%}
{{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}|
- {%- endfor %}''')
- assert tmpl.render() == 'a:1:2|b:1|'
+ {%- endfor %}"""
+ )
+ assert tmpl.render() == "a:1:2|b:1|"
def test_groupby_multidot(self, env):
class Date(object):
@@ -556,49 +571,53 @@ class TestFilter(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)
+ 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('''
+ tmpl = env.from_string(
+ """
{%- for year, list in articles|groupby('date.year') -%}
{{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}|
- {%- endfor %}''')
- assert tmpl.render(articles=articles).split('|') == [
- '1970[aha][interesting][really?]',
- '1971[totally not]',
- ''
+ {%- endfor %}"""
+ )
+ assert tmpl.render(articles=articles).split("|") == [
+ "1970[aha][interesting][really?]",
+ "1971[totally not]",
+ "",
]
def test_filtertag(self, env):
- tmpl = env.from_string("{% filter upper|replace('FOO', 'foo') %}"
- "foobar{% endfilter %}")
- assert tmpl.render() == 'fooBAR'
+ tmpl = env.from_string(
+ "{% filter upper|replace('FOO', 'foo') %}foobar{% endfilter %}"
+ )
+ assert tmpl.render() == "fooBAR"
def test_replace(self, env):
env = Environment()
tmpl = env.from_string('{{ string|replace("o", 42) }}')
- assert tmpl.render(string='<foo>') == '<f4242>'
+ assert tmpl.render(string="<foo>") == "<f4242>"
env = Environment(autoescape=True)
tmpl = env.from_string('{{ string|replace("o", 42) }}')
- assert tmpl.render(string='<foo>') == '&lt;f4242&gt;'
+ assert tmpl.render(string="<foo>") == "&lt;f4242&gt;"
tmpl = env.from_string('{{ string|replace("<", 42) }}')
- assert tmpl.render(string='<foo>') == '42foo&gt;'
+ assert tmpl.render(string="<foo>") == "42foo&gt;"
tmpl = env.from_string('{{ string|replace("o", ">x<") }}')
- assert tmpl.render(string=Markup('foo')) == 'f&gt;x&lt;&gt;x&lt;'
+ assert tmpl.render(string=Markup("foo")) == "f&gt;x&lt;&gt;x&lt;"
def test_forceescape(self, env):
- tmpl = env.from_string('{{ x|forceescape }}')
- assert tmpl.render(x=Markup('<div />')) == u'&lt;div /&gt;'
+ tmpl = env.from_string("{{ x|forceescape }}")
+ assert tmpl.render(x=Markup("<div />")) == u"&lt;div /&gt;"
def test_safe(self, env):
env = Environment(autoescape=True)
tmpl = env.from_string('{{ "<div>foo</div>"|safe }}')
- assert tmpl.render() == '<div>foo</div>'
+ assert tmpl.render() == "<div>foo</div>"
tmpl = env.from_string('{{ "<div>foo</div>" }}')
- assert tmpl.render() == '&lt;div&gt;foo&lt;/div&gt;'
+ assert tmpl.render() == "&lt;div&gt;foo&lt;/div&gt;"
@pytest.mark.parametrize(
("value", "expect"),
@@ -606,11 +625,11 @@ class TestFilter(object):
("Hello, world!", "Hello%2C%20world%21"),
(u"Hello, world\u203d", "Hello%2C%20world%E2%80%BD"),
({"f": 1}, "f=1"),
- ([('f', 1), ("z", 2)], "f=1&amp;z=2"),
+ ([("f", 1), ("z", 2)], "f=1&amp;z=2"),
({u"\u203d": 1}, "%E2%80%BD=1"),
({0: 1}, "0=1"),
([("a b/c", "a b/c")], "a+b%2Fc=a+b%2Fc"),
- ("a b/c", "a%20b/c")
+ ("a b/c", "a%20b/c"),
],
)
def test_urlencode(self, value, expect):
@@ -621,29 +640,30 @@ class TestFilter(object):
def test_simple_map(self, env):
env = Environment()
tmpl = env.from_string('{{ ["1", "2", "3"]|map("int")|sum }}')
- assert tmpl.render() == '6'
+ assert tmpl.render() == "6"
def test_map_sum(self, env):
tmpl = env.from_string('{{ [[1,2], [3], [4,5,6]]|map("sum")|list }}')
- assert tmpl.render() == '[3, 3, 15]'
+ assert tmpl.render() == "[3, 3, 15]"
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'),
+ User("john"),
+ User("jane"),
+ User("mike"),
]
tmpl = env.from_string('{{ users|map(attribute="name")|join("|") }}')
- assert tmpl.render(users=users) == 'john|jane|mike'
+ assert tmpl.render(users=users) == "john|jane|mike"
def test_empty_map(self, env):
env = Environment()
tmpl = env.from_string('{{ none|map("upper")|list }}')
- assert tmpl.render() == '[]'
+ assert tmpl.render() == "[]"
def test_map_default(self, env):
class Fullname(object):
@@ -663,109 +683,112 @@ class TestFilter(object):
Fullname("john", "lennon"),
Fullname("jane", "edwards"),
Fullname("jon", None),
- Firstname("mike")
+ Firstname("mike"),
]
assert tmpl.render(users=users) == "lennon, edwards, None, smith"
def test_simple_select(self, env):
env = Environment()
tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|select("odd")|join("|") }}')
- assert tmpl.render() == '1|3|5'
+ assert tmpl.render() == "1|3|5"
def test_bool_select(self, env):
env = Environment()
- tmpl = env.from_string(
- '{{ [none, false, 0, 1, 2, 3, 4, 5]|select|join("|") }}'
- )
- assert tmpl.render() == '1|2|3|4|5'
+ tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|select|join("|") }}')
+ assert tmpl.render() == "1|2|3|4|5"
def test_simple_reject(self, env):
env = Environment()
tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|reject("odd")|join("|") }}')
- assert tmpl.render() == '2|4'
+ assert tmpl.render() == "2|4"
def test_bool_reject(self, env):
env = Environment()
- tmpl = env.from_string(
- '{{ [none, false, 0, 1, 2, 3, 4, 5]|reject|join("|") }}'
- )
- assert tmpl.render() == 'None|False|0'
+ tmpl = env.from_string('{{ [none, false, 0, 1, 2, 3, 4, 5]|reject|join("|") }}')
+ assert tmpl.render() == "None|False|0"
def test_simple_select_attr(self, env):
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),
+ User("john", True),
+ User("jane", True),
+ User("mike", False),
]
tmpl = env.from_string(
- '{{ users|selectattr("is_active")|'
- 'map(attribute="name")|join("|") }}'
+ '{{ users|selectattr("is_active")|map(attribute="name")|join("|") }}'
)
- assert tmpl.render(users=users) == 'john|jane'
+ assert tmpl.render(users=users) == "john|jane"
def test_simple_reject_attr(self, env):
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),
+ User("john", True),
+ User("jane", True),
+ User("mike", False),
]
- tmpl = env.from_string('{{ users|rejectattr("is_active")|'
- 'map(attribute="name")|join("|") }}')
- assert tmpl.render(users=users) == 'mike'
+ tmpl = env.from_string(
+ '{{ users|rejectattr("is_active")|map(attribute="name")|join("|") }}'
+ )
+ assert tmpl.render(users=users) == "mike"
def test_func_select_attr(self, env):
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'),
+ User(1, "john"),
+ User(2, "jane"),
+ User(3, "mike"),
]
- tmpl = env.from_string('{{ users|selectattr("id", "odd")|'
- 'map(attribute="name")|join("|") }}')
- assert tmpl.render(users=users) == 'john|mike'
+ tmpl = env.from_string(
+ '{{ users|selectattr("id", "odd")|map(attribute="name")|join("|") }}'
+ )
+ assert tmpl.render(users=users) == "john|mike"
def test_func_reject_attr(self, env):
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'),
+ User(1, "john"),
+ User(2, "jane"),
+ User(3, "mike"),
]
- tmpl = env.from_string('{{ users|rejectattr("id", "odd")|'
- 'map(attribute="name")|join("|") }}')
- assert tmpl.render(users=users) == 'jane'
+ tmpl = env.from_string(
+ '{{ users|rejectattr("id", "odd")|map(attribute="name")|join("|") }}'
+ )
+ assert tmpl.render(users=users) == "jane"
def test_json_dump(self):
env = Environment(autoescape=True)
- t = env.from_string('{{ x|tojson }}')
- assert t.render(x={'foo': 'bar'}) == '{"foo": "bar"}'
- assert t.render(x='"ba&r\'') == r'"\"ba\u0026r\u0027"'
- assert t.render(x='<bar>') == r'"\u003cbar\u003e"'
+ t = env.from_string("{{ x|tojson }}")
+ assert t.render(x={"foo": "bar"}) == '{"foo": "bar"}'
+ assert t.render(x="\"ba&r'") == r'"\"ba\u0026r\u0027"'
+ assert t.render(x="<bar>") == r'"\u003cbar\u003e"'
def my_dumps(value, **options):
- assert options == {'foo': 'bar'}
- return '42'
- env.policies['json.dumps_function'] = my_dumps
- env.policies['json.dumps_kwargs'] = {'foo': 'bar'}
- assert t.render(x=23) == '42'
+ assert options == {"foo": "bar"}
+ return "42"
+
+ env.policies["json.dumps_function"] = my_dumps
+ env.policies["json.dumps_kwargs"] = {"foo": "bar"}
+ assert t.render(x=23) == "42"
def test_wordwrap(self, env):
env.newline_sequence = "\n"
diff --git a/tests/test_idtracking.py b/tests/test_idtracking.py
index 29312d3..4c79ee6 100644
--- a/tests/test_idtracking.py
+++ b/tests/test_idtracking.py
@@ -4,213 +4,286 @@ from jinja2.idtracking import symbols_for_node
def test_basics():
for_loop = nodes.For(
- nodes.Name('foo', 'store'),
- nodes.Name('seq', 'load'),
- [nodes.Output([nodes.Name('foo', 'load')])],
- [], None, False)
- tmpl = nodes.Template([
- nodes.Assign(
- nodes.Name('foo', 'store'),
- nodes.Name('bar', 'load')),
- for_loop])
+ nodes.Name("foo", "store"),
+ nodes.Name("seq", "load"),
+ [nodes.Output([nodes.Name("foo", "load")])],
+ [],
+ None,
+ False,
+ )
+ tmpl = nodes.Template(
+ [nodes.Assign(nodes.Name("foo", "store"), nodes.Name("bar", "load")), for_loop]
+ )
sym = symbols_for_node(tmpl)
assert sym.refs == {
- 'foo': 'l_0_foo',
- 'bar': 'l_0_bar',
- 'seq': 'l_0_seq',
+ "foo": "l_0_foo",
+ "bar": "l_0_bar",
+ "seq": "l_0_seq",
}
assert sym.loads == {
- 'l_0_foo': ('undefined', None),
- 'l_0_bar': ('resolve', 'bar'),
- 'l_0_seq': ('resolve', 'seq'),
+ "l_0_foo": ("undefined", None),
+ "l_0_bar": ("resolve", "bar"),
+ "l_0_seq": ("resolve", "seq"),
}
sym = symbols_for_node(for_loop, sym)
assert sym.refs == {
- 'foo': 'l_1_foo',
+ "foo": "l_1_foo",
}
assert sym.loads == {
- 'l_1_foo': ('param', None),
+ "l_1_foo": ("param", None),
}
def test_complex():
- title_block = nodes.Block('title', [
- nodes.Output([nodes.TemplateData(u'Page Title')])
- ], False)
-
- render_title_macro = nodes.Macro('render_title', [nodes.Name('title', 'param')], [], [
- nodes.Output([
- nodes.TemplateData(u'\n <div class="title">\n <h1>'),
- nodes.Name('title', 'load'),
- nodes.TemplateData(u'</h1>\n <p>'),
- nodes.Name('subtitle', 'load'),
- nodes.TemplateData(u'</p>\n ')]),
- nodes.Assign(
- nodes.Name('subtitle', 'store'), nodes.Const('something else')),
- nodes.Output([
- nodes.TemplateData(u'\n <p>'),
- nodes.Name('subtitle', 'load'),
- nodes.TemplateData(u'</p>\n </div>\n'),
- nodes.If(
- nodes.Name('something', 'load'), [
- nodes.Assign(nodes.Name('title_upper', 'store'),
- nodes.Filter(nodes.Name('title', 'load'),
- 'upper', [], [], None, None)),
- nodes.Output([
- nodes.Name('title_upper', 'load'),
- nodes.Call(nodes.Name('render_title', 'load'), [
- nodes.Const('Aha')], [], None, None)])], [], [])])])
+ title_block = nodes.Block(
+ "title", [nodes.Output([nodes.TemplateData(u"Page Title")])], False
+ )
+
+ render_title_macro = nodes.Macro(
+ "render_title",
+ [nodes.Name("title", "param")],
+ [],
+ [
+ nodes.Output(
+ [
+ nodes.TemplateData(u'\n <div class="title">\n <h1>'),
+ nodes.Name("title", "load"),
+ nodes.TemplateData(u"</h1>\n <p>"),
+ nodes.Name("subtitle", "load"),
+ nodes.TemplateData(u"</p>\n "),
+ ]
+ ),
+ nodes.Assign(
+ nodes.Name("subtitle", "store"), nodes.Const("something else")
+ ),
+ nodes.Output(
+ [
+ nodes.TemplateData(u"\n <p>"),
+ nodes.Name("subtitle", "load"),
+ nodes.TemplateData(u"</p>\n </div>\n"),
+ nodes.If(
+ nodes.Name("something", "load"),
+ [
+ nodes.Assign(
+ nodes.Name("title_upper", "store"),
+ nodes.Filter(
+ nodes.Name("title", "load"),
+ "upper",
+ [],
+ [],
+ None,
+ None,
+ ),
+ ),
+ nodes.Output(
+ [
+ nodes.Name("title_upper", "load"),
+ nodes.Call(
+ nodes.Name("render_title", "load"),
+ [nodes.Const("Aha")],
+ [],
+ None,
+ None,
+ ),
+ ]
+ ),
+ ],
+ [],
+ [],
+ ),
+ ]
+ ),
+ ],
+ )
for_loop = nodes.For(
- nodes.Name('item', 'store'),
- nodes.Name('seq', 'load'), [
- nodes.Output([
- nodes.TemplateData(u'\n <li>'),
- nodes.Name('item', 'load'),
- nodes.TemplateData(u'</li>\n <span>')]),
- nodes.Include(nodes.Const('helper.html'), True, False),
- nodes.Output([
- nodes.TemplateData(u'</span>\n ')])], [], None, False)
-
- body_block = nodes.Block('body', [
- nodes.Output([
- nodes.TemplateData(u'\n '),
- nodes.Call(nodes.Name('render_title', 'load'), [
- nodes.Name('item', 'load')], [], None, None),
- nodes.TemplateData(u'\n <ul>\n ')]),
- for_loop,
- nodes.Output([nodes.TemplateData(u'\n </ul>\n')])],
- False)
-
- tmpl = nodes.Template([
- nodes.Extends(nodes.Const('layout.html')),
- title_block,
- render_title_macro,
- body_block,
- ])
+ nodes.Name("item", "store"),
+ nodes.Name("seq", "load"),
+ [
+ nodes.Output(
+ [
+ nodes.TemplateData(u"\n <li>"),
+ nodes.Name("item", "load"),
+ nodes.TemplateData(u"</li>\n <span>"),
+ ]
+ ),
+ nodes.Include(nodes.Const("helper.html"), True, False),
+ nodes.Output([nodes.TemplateData(u"</span>\n ")]),
+ ],
+ [],
+ None,
+ False,
+ )
+
+ body_block = nodes.Block(
+ "body",
+ [
+ nodes.Output(
+ [
+ nodes.TemplateData(u"\n "),
+ nodes.Call(
+ nodes.Name("render_title", "load"),
+ [nodes.Name("item", "load")],
+ [],
+ None,
+ None,
+ ),
+ nodes.TemplateData(u"\n <ul>\n "),
+ ]
+ ),
+ for_loop,
+ nodes.Output([nodes.TemplateData(u"\n </ul>\n")]),
+ ],
+ False,
+ )
+
+ tmpl = nodes.Template(
+ [
+ nodes.Extends(nodes.Const("layout.html")),
+ title_block,
+ render_title_macro,
+ body_block,
+ ]
+ )
tmpl_sym = symbols_for_node(tmpl)
assert tmpl_sym.refs == {
- 'render_title': 'l_0_render_title',
+ "render_title": "l_0_render_title",
}
assert tmpl_sym.loads == {
- 'l_0_render_title': ('undefined', None),
+ "l_0_render_title": ("undefined", None),
}
- assert tmpl_sym.stores == set(['render_title'])
+ assert tmpl_sym.stores == set(["render_title"])
assert tmpl_sym.dump_stores() == {
- 'render_title': 'l_0_render_title',
+ "render_title": "l_0_render_title",
}
macro_sym = symbols_for_node(render_title_macro, tmpl_sym)
assert macro_sym.refs == {
- 'subtitle': 'l_1_subtitle',
- 'something': 'l_1_something',
- 'title': 'l_1_title',
- 'title_upper': 'l_1_title_upper',
+ "subtitle": "l_1_subtitle",
+ "something": "l_1_something",
+ "title": "l_1_title",
+ "title_upper": "l_1_title_upper",
}
assert macro_sym.loads == {
- 'l_1_subtitle': ('resolve', 'subtitle'),
- 'l_1_something': ('resolve','something'),
- 'l_1_title': ('param', None),
- 'l_1_title_upper': ('resolve', 'title_upper'),
+ "l_1_subtitle": ("resolve", "subtitle"),
+ "l_1_something": ("resolve", "something"),
+ "l_1_title": ("param", None),
+ "l_1_title_upper": ("resolve", "title_upper"),
}
- assert macro_sym.stores == set(['title', 'title_upper', 'subtitle'])
- assert macro_sym.find_ref('render_title') == 'l_0_render_title'
+ assert macro_sym.stores == set(["title", "title_upper", "subtitle"])
+ assert macro_sym.find_ref("render_title") == "l_0_render_title"
assert macro_sym.dump_stores() == {
- 'title': 'l_1_title',
- 'title_upper': 'l_1_title_upper',
- 'subtitle': 'l_1_subtitle',
- 'render_title': 'l_0_render_title',
+ "title": "l_1_title",
+ "title_upper": "l_1_title_upper",
+ "subtitle": "l_1_subtitle",
+ "render_title": "l_0_render_title",
}
body_sym = symbols_for_node(body_block)
assert body_sym.refs == {
- 'item': 'l_0_item',
- 'seq': 'l_0_seq',
- 'render_title': 'l_0_render_title',
+ "item": "l_0_item",
+ "seq": "l_0_seq",
+ "render_title": "l_0_render_title",
}
assert body_sym.loads == {
- 'l_0_item': ('resolve', 'item'),
- 'l_0_seq': ('resolve', 'seq'),
- 'l_0_render_title': ('resolve', 'render_title'),
+ "l_0_item": ("resolve", "item"),
+ "l_0_seq": ("resolve", "seq"),
+ "l_0_render_title": ("resolve", "render_title"),
}
assert body_sym.stores == set([])
for_sym = symbols_for_node(for_loop, body_sym)
assert for_sym.refs == {
- 'item': 'l_1_item',
+ "item": "l_1_item",
}
assert for_sym.loads == {
- 'l_1_item': ('param', None),
+ "l_1_item": ("param", None),
}
- assert for_sym.stores == set(['item'])
+ assert for_sym.stores == set(["item"])
assert for_sym.dump_stores() == {
- 'item': 'l_1_item',
+ "item": "l_1_item",
}
def test_if_branching_stores():
- tmpl = nodes.Template([
- nodes.If(nodes.Name('expression', 'load'), [
- nodes.Assign(nodes.Name('variable', 'store'),
- nodes.Const(42))], [], [])])
+ tmpl = nodes.Template(
+ [
+ nodes.If(
+ nodes.Name("expression", "load"),
+ [nodes.Assign(nodes.Name("variable", "store"), nodes.Const(42))],
+ [],
+ [],
+ )
+ ]
+ )
sym = symbols_for_node(tmpl)
- assert sym.refs == {
- 'variable': 'l_0_variable',
- 'expression': 'l_0_expression'
- }
- assert sym.stores == set(['variable'])
+ assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"}
+ assert sym.stores == set(["variable"])
assert sym.loads == {
- 'l_0_variable': ('resolve', 'variable'),
- 'l_0_expression': ('resolve', 'expression')
+ "l_0_variable": ("resolve", "variable"),
+ "l_0_expression": ("resolve", "expression"),
}
assert sym.dump_stores() == {
- 'variable': 'l_0_variable',
+ "variable": "l_0_variable",
}
def test_if_branching_stores_undefined():
- tmpl = nodes.Template([
- nodes.Assign(nodes.Name('variable', 'store'), nodes.Const(23)),
- nodes.If(nodes.Name('expression', 'load'), [
- nodes.Assign(nodes.Name('variable', 'store'),
- nodes.Const(42))], [], [])])
+ tmpl = nodes.Template(
+ [
+ nodes.Assign(nodes.Name("variable", "store"), nodes.Const(23)),
+ nodes.If(
+ nodes.Name("expression", "load"),
+ [nodes.Assign(nodes.Name("variable", "store"), nodes.Const(42))],
+ [],
+ [],
+ ),
+ ]
+ )
sym = symbols_for_node(tmpl)
- assert sym.refs == {
- 'variable': 'l_0_variable',
- 'expression': 'l_0_expression'
- }
- assert sym.stores == set(['variable'])
+ assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"}
+ assert sym.stores == set(["variable"])
assert sym.loads == {
- 'l_0_variable': ('undefined', None),
- 'l_0_expression': ('resolve', 'expression')
+ "l_0_variable": ("undefined", None),
+ "l_0_expression": ("resolve", "expression"),
}
assert sym.dump_stores() == {
- 'variable': 'l_0_variable',
+ "variable": "l_0_variable",
}
def test_if_branching_multi_scope():
- for_loop = nodes.For(nodes.Name('item', 'store'), nodes.Name('seq', 'load'), [
- nodes.If(nodes.Name('expression', 'load'), [
- nodes.Assign(nodes.Name('x', 'store'), nodes.Const(42))], [], []),
- nodes.Include(nodes.Const('helper.html'), True, False)
- ], [], None, False)
+ for_loop = nodes.For(
+ nodes.Name("item", "store"),
+ nodes.Name("seq", "load"),
+ [
+ nodes.If(
+ nodes.Name("expression", "load"),
+ [nodes.Assign(nodes.Name("x", "store"), nodes.Const(42))],
+ [],
+ [],
+ ),
+ nodes.Include(nodes.Const("helper.html"), True, False),
+ ],
+ [],
+ None,
+ False,
+ )
- tmpl = nodes.Template([
- nodes.Assign(nodes.Name('x', 'store'), nodes.Const(23)),
- for_loop
- ])
+ tmpl = nodes.Template(
+ [nodes.Assign(nodes.Name("x", "store"), nodes.Const(23)), for_loop]
+ )
tmpl_sym = symbols_for_node(tmpl)
for_sym = symbols_for_node(for_loop, tmpl_sym)
- assert for_sym.stores == set(['item', 'x'])
+ assert for_sym.stores == set(["item", "x"])
assert for_sym.loads == {
- 'l_1_x': ('alias', 'l_0_x'),
- 'l_1_item': ('param', None),
- 'l_1_expression': ('resolve', 'expression'),
+ "l_1_x": ("alias", "l_0_x"),
+ "l_1_item": ("param", None),
+ "l_1_expression": ("resolve", "expression"),
}
diff --git a/tests/test_imports.py b/tests/test_imports.py
index 2224fb8..f2895d6 100644
--- a/tests/test_imports.py
+++ b/tests/test_imports.py
@@ -19,39 +19,42 @@ from jinja2.exceptions import TemplateSyntaxError
@pytest.fixture
def test_env():
- env = Environment(loader=DictLoader(dict(
- module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}',
- header='[{{ foo }}|{{ 23 }}]',
- o_printer='({{ o }})'
- )))
- env.globals['bar'] = 23
+ env = Environment(
+ loader=DictLoader(
+ dict(
+ module="{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}",
+ header="[{{ foo }}|{{ 23 }}]",
+ o_printer="({{ o }})",
+ )
+ )
+ )
+ env.globals["bar"] = 23
return env
@pytest.mark.imports
class TestImports(object):
-
def test_context_imports(self, test_env):
t = test_env.from_string('{% import "module" as m %}{{ m.test() }}')
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env.from_string(
'{% import "module" as m without context %}{{ m.test() }}'
)
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env.from_string(
'{% import "module" as m with context %}{{ m.test() }}'
)
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
t = test_env.from_string('{% from "module" import test %}{{ test() }}')
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env.from_string(
'{% from "module" import test without context %}{{ test() }}'
)
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
t = test_env.from_string(
'{% from "module" import test with context %}{{ test() }}'
)
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
def test_import_needs_name(self, test_env):
test_env.from_string('{% from "foo" import bar %}')
@@ -84,81 +87,86 @@ class TestImports(object):
test_env.from_string('{% from "foo" import bar with context, %}')
def test_exports(self, test_env):
- m = test_env.from_string('''
+ m = test_env.from_string(
+ """
{% macro toplevel() %}...{% endmacro %}
{% macro __private() %}...{% endmacro %}
{% set variable = 42 %}
{% for item in [1] %}
{% macro notthere() %}{% endmacro %}
{% endfor %}
- ''').module
- assert m.toplevel() == '...'
- assert not hasattr(m, '__missing')
+ """
+ ).module
+ assert m.toplevel() == "..."
+ assert not hasattr(m, "__missing")
assert m.variable == 42
- assert not hasattr(m, 'notthere')
+ assert not hasattr(m, "notthere")
@pytest.mark.imports
@pytest.mark.includes
class TestIncludes(object):
-
def test_context_include(self, test_env):
t = test_env.from_string('{% include "header" %}')
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
t = test_env.from_string('{% include "header" with context %}')
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
t = test_env.from_string('{% include "header" without context %}')
- assert t.render(foo=42) == '[|23]'
+ assert t.render(foo=42) == "[|23]"
def test_choice_includes(self, test_env):
t = test_env.from_string('{% include ["missing", "header"] %}')
- assert t.render(foo=42) == '[42|23]'
+ assert t.render(foo=42) == "[42|23]"
- t = test_env.from_string(
- '{% include ["missing", "missing2"] ignore missing %}'
- )
- assert t.render(foo=42) == ''
+ t = test_env.from_string('{% include ["missing", "missing2"] ignore missing %}')
+ assert t.render(foo=42) == ""
t = test_env.from_string('{% include ["missing", "missing2"] %}')
pytest.raises(TemplateNotFound, t.render)
with pytest.raises(TemplatesNotFound) as e:
t.render()
- assert e.value.templates == ['missing', 'missing2']
- assert e.value.name == 'missing2'
+ assert e.value.templates == ["missing", "missing2"]
+ assert e.value.name == "missing2"
def test_includes(t, **ctx):
- ctx['foo'] = 42
- assert t.render(ctx) == '[42|23]'
+ ctx["foo"] = 42
+ assert t.render(ctx) == "[42|23]"
t = test_env.from_string('{% include ["missing", "header"] %}')
test_includes(t)
- t = test_env.from_string('{% include x %}')
- test_includes(t, x=['missing', 'header'])
+ t = test_env.from_string("{% include x %}")
+ test_includes(t, x=["missing", "header"])
t = test_env.from_string('{% include [x, "header"] %}')
- test_includes(t, x='missing')
- t = test_env.from_string('{% include x %}')
- test_includes(t, x='header')
- t = test_env.from_string('{% include [x] %}')
- test_includes(t, x='header')
+ test_includes(t, x="missing")
+ t = test_env.from_string("{% include x %}")
+ test_includes(t, x="header")
+ t = test_env.from_string("{% include [x] %}")
+ test_includes(t, x="header")
def test_include_ignoring_missing(self, test_env):
t = test_env.from_string('{% include "missing" %}')
pytest.raises(TemplateNotFound, t.render)
- for extra in '', 'with context', 'without context':
- t = test_env.from_string('{% include "missing" ignore missing ' +
- extra + ' %}')
- assert t.render() == ''
+ for extra in "", "with context", "without context":
+ t = test_env.from_string(
+ '{% include "missing" ignore missing ' + extra + " %}"
+ )
+ assert t.render() == ""
def test_context_include_with_overrides(self, test_env):
- env = Environment(loader=DictLoader(dict(
- main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}",
- item="{{ item }}"
- )))
+ env = Environment(
+ loader=DictLoader(
+ dict(
+ main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}",
+ item="{{ item }}",
+ )
+ )
+ )
assert env.get_template("main").render() == "123"
def test_unoptimized_scopes(self, test_env):
- t = test_env.from_string("""
+ t = test_env.from_string(
+ """
{% macro outer(o) %}
{% macro inner() %}
{% include "o_printer" %}
@@ -166,13 +174,15 @@ class TestIncludes(object):
{{ inner() }}
{% endmacro %}
{{ outer("FOO") }}
- """)
- assert t.render().strip() == '(FOO)'
+ """
+ )
+ assert t.render().strip() == "(FOO)"
def test_import_from_with_context(self):
- env = Environment(loader=DictLoader({
- 'a': '{% macro x() %}{{ foobar }}{% endmacro %}',
- }))
- t = env.from_string('{% set foobar = 42 %}{% from "a" '
- 'import x with context %}{{ x() }}')
- assert t.render() == '42'
+ env = Environment(
+ loader=DictLoader({"a": "{% macro x() %}{{ foobar }}{% endmacro %}",})
+ )
+ t = env.from_string(
+ '{% set foobar = 42 %}{% from "a" ' "import x with context %}{{ x() }}"
+ )
+ assert t.render() == "42"
diff --git a/tests/test_inheritance.py b/tests/test_inheritance.py
index 32df340..6d44d4f 100644
--- a/tests/test_inheritance.py
+++ b/tests/test_inheritance.py
@@ -15,34 +15,34 @@ from jinja2 import Environment
from jinja2 import TemplateError
from jinja2 import TemplateRuntimeError
-LAYOUTTEMPLATE = '''\
+LAYOUTTEMPLATE = """\
|{% block block1 %}block 1 from layout{% endblock %}
|{% block block2 %}block 2 from layout{% endblock %}
|{% block block3 %}
{% block block4 %}nested block 4 from layout{% endblock %}
-{% endblock %}|'''
+{% endblock %}|"""
-LEVEL1TEMPLATE = '''\
+LEVEL1TEMPLATE = """\
{% extends "layout" %}
-{% block block1 %}block 1 from level1{% endblock %}'''
+{% block block1 %}block 1 from level1{% endblock %}"""
-LEVEL2TEMPLATE = '''\
+LEVEL2TEMPLATE = """\
{% extends "level1" %}
{% block block2 %}{% block block5 %}nested block 5 from level2{%
-endblock %}{% endblock %}'''
+endblock %}{% endblock %}"""
-LEVEL3TEMPLATE = '''\
+LEVEL3TEMPLATE = """\
{% extends "level2" %}
{% block block5 %}block 5 from level3{% endblock %}
{% block block4 %}block 4 from level3{% endblock %}
-'''
+"""
-LEVEL4TEMPLATE = '''\
+LEVEL4TEMPLATE = """\
{% extends "level3" %}
{% block block3 %}block 3 from level4{% endblock %}
-'''
+"""
-WORKINGTEMPLATE = '''\
+WORKINGTEMPLATE = """\
{% extends "layout" %}
{% block block1 %}
{% if false %}
@@ -51,9 +51,9 @@ WORKINGTEMPLATE = '''\
{% endblock %}
{% endif %}
{% endblock %}
-'''
+"""
-DOUBLEEXTENDS = '''\
+DOUBLEEXTENDS = """\
{% extends "layout" %}
{% extends "layout" %}
{% block block1 %}
@@ -63,127 +63,166 @@ DOUBLEEXTENDS = '''\
{% endblock %}
{% endif %}
{% endblock %}
-'''
+"""
@pytest.fixture
def env():
- return Environment(loader=DictLoader({
- 'layout': LAYOUTTEMPLATE,
- 'level1': LEVEL1TEMPLATE,
- 'level2': LEVEL2TEMPLATE,
- 'level3': LEVEL3TEMPLATE,
- 'level4': LEVEL4TEMPLATE,
- 'working': WORKINGTEMPLATE,
- 'doublee': DOUBLEEXTENDS,
- }), trim_blocks=True)
+ 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(object):
-
def test_layout(self, env):
- tmpl = env.get_template('layout')
- assert tmpl.render() == ('|block 1 from layout|block 2 from '
- 'layout|nested block 4 from layout|')
+ tmpl = env.get_template("layout")
+ assert tmpl.render() == (
+ "|block 1 from layout|block 2 from layout|nested block 4 from layout|"
+ )
def test_level1(self, env):
- tmpl = env.get_template('level1')
- assert tmpl.render() == ('|block 1 from level1|block 2 from '
- 'layout|nested block 4 from layout|')
+ tmpl = env.get_template("level1")
+ assert tmpl.render() == (
+ "|block 1 from level1|block 2 from layout|nested block 4 from layout|"
+ )
def test_level2(self, env):
- tmpl = env.get_template('level2')
- assert tmpl.render() == ('|block 1 from level1|nested block 5 from '
- 'level2|nested block 4 from layout|')
+ tmpl = env.get_template("level2")
+ assert tmpl.render() == (
+ "|block 1 from level1|nested block 5 from "
+ "level2|nested block 4 from layout|"
+ )
def test_level3(self, env):
- tmpl = env.get_template('level3')
- assert tmpl.render() == ('|block 1 from level1|block 5 from level3|'
- 'block 4 from level3|')
+ tmpl = env.get_template("level3")
+ assert tmpl.render() == (
+ "|block 1 from level1|block 5 from level3|block 4 from level3|"
+ )
def test_level4(self, env):
- tmpl = env.get_template('level4')
- assert tmpl.render() == ('|block 1 from level1|block 5 from '
- 'level3|block 3 from level4|')
+ tmpl = env.get_template("level4")
+ assert tmpl.render() == (
+ "|block 1 from level1|block 5 from level3|block 3 from level4|"
+ )
def test_super(self, env):
- env = Environment(loader=DictLoader({
- 'a': '{% block intro %}INTRO{% endblock %}|'
- 'BEFORE|{% block data %}INNER{% endblock %}|AFTER',
- 'b': '{% extends "a" %}{% block data %}({{ '
- 'super() }}){% endblock %}',
- 'c': '{% extends "b" %}{% block intro %}--{{ '
- 'super() }}--{% endblock %}\n{% block data '
- '%}[{{ super() }}]{% endblock %}'
- }))
- tmpl = env.get_template('c')
- assert tmpl.render() == '--INTRO--|BEFORE|[(INNER)]|AFTER'
+ env = Environment(
+ loader=DictLoader(
+ {
+ "a": "{% block intro %}INTRO{% endblock %}|"
+ "BEFORE|{% block data %}INNER{% endblock %}|AFTER",
+ "b": '{% extends "a" %}{% block data %}({{ '
+ "super() }}){% endblock %}",
+ "c": '{% extends "b" %}{% block intro %}--{{ '
+ "super() }}--{% endblock %}\n{% block data "
+ "%}[{{ super() }}]{% endblock %}",
+ }
+ )
+ )
+ tmpl = env.get_template("c")
+ assert tmpl.render() == "--INTRO--|BEFORE|[(INNER)]|AFTER"
def test_working(self, env):
- tmpl = env.get_template('working')
+ 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'
+ tmpl = env.from_string(
+ "{{ self.foo() }}|{% block foo %}42{% endblock %}|{{ self.foo() }}"
+ )
+ assert tmpl.render() == "42|42|42"
def test_preserve_blocks(self, env):
- env = Environment(loader=DictLoader({
- 'a': '{% if false %}{% block x %}A{% endblock %}'
- '{% endif %}{{ self.x() }}',
- 'b': '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}'
- }))
- tmpl = env.get_template('b')
- assert tmpl.render() == 'BA'
+ env = Environment(
+ loader=DictLoader(
+ {
+ "a": "{% if false %}{% block x %}A{% endblock %}"
+ "{% endif %}{{ self.x() }}",
+ "b": '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}',
+ }
+ )
+ )
+ tmpl = env.get_template("b")
+ assert tmpl.render() == "BA"
def test_dynamic_inheritance(self, env):
- env = Environment(loader=DictLoader({
- 'master1': 'MASTER1{% block x %}{% endblock %}',
- 'master2': 'MASTER2{% block x %}{% endblock %}',
- 'child': '{% extends master %}{% block x %}CHILD{% endblock %}'
- }))
- tmpl = env.get_template('child')
+ 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
+ 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'
+ 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]'
+ 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]'
+ 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': '''
+ env = Environment(
+ loader=DictLoader(
+ {
+ "layout.html": """
{% block useless %}{% endblock %}
- ''',
- 'index.html': '''
+ """,
+ "index.html": """
{%- extends 'layout.html' %}
{% from 'helpers.html' import foo with context %}
{% block useless %}
@@ -193,21 +232,25 @@ class TestInheritance(object):
{% endblock %}
{% endfor %}
{% endblock %}
- ''',
- 'helpers.html': '''
+ """,
+ "helpers.html": """
{% macro foo(x) %}{{ the_foo + x }}{% endmacro %}
- '''
- }))
- rv = env.get_template('index.html').render(the_foo=42).split()
- assert rv == ['43', '44', '45']
+ """,
+ }
+ )
+ )
+ rv = env.get_template("index.html").render(the_foo=42).split()
+ assert rv == ["43", "44", "45"]
@pytest.mark.inheritance
class TestBugFix(object):
-
def test_fixed_macro_scoping_bug(self, env):
- assert Environment(loader=DictLoader({
- 'test.html': '''\
+ assert (
+ Environment(
+ loader=DictLoader(
+ {
+ "test.html": """\
{% extends 'details.html' %}
{% macro my_macro() %}
@@ -217,8 +260,8 @@ class TestBugFix(object):
{% block inner_box %}
{{ my_macro() }}
{% endblock %}
- ''',
- 'details.html': '''\
+ """,
+ "details.html": """\
{% extends 'standard.html' %}
{% macro my_macro() %}
@@ -233,16 +276,22 @@ class TestBugFix(object):
{% endblock %}
{% endblock %}
{% endblock %}
- ''',
- 'standard.html': '''
+ """,
+ "standard.html": """
{% block content %}&nbsp;{% endblock %}
- '''
- })).get_template("test.html").render().split() \
- == [u'outer_box', u'my_macro']
+ """,
+ }
+ )
+ )
+ .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``.
"""
with pytest.raises(TemplateRuntimeError, match="extended multiple times"):
- env.get_template('doublee').render()
+ env.get_template("doublee").render()
diff --git a/tests/test_lexnparse.py b/tests/test_lexnparse.py
index a7ef46a..fb54ac8 100644
--- a/tests/test_lexnparse.py
+++ b/tests/test_lexnparse.py
@@ -27,8 +27,11 @@ from jinja2.lexer import TokenStream
# 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
@@ -36,9 +39,10 @@ else:
@pytest.mark.lexnparse
@pytest.mark.tokenstream
class TestTokenStream(object):
- test_tokens = [Token(1, TOKEN_BLOCK_BEGIN, ''),
- Token(2, TOKEN_BLOCK_END, ''),
- ]
+ test_tokens = [
+ Token(1, TOKEN_BLOCK_BEGIN, ""),
+ Token(2, TOKEN_BLOCK_END, ""),
+ ]
def test_simple(self, env):
ts = TokenStream(self.test_tokens, "foo", "bar")
@@ -55,117 +59,129 @@ class TestTokenStream(object):
assert bool(ts.eos)
def test_iter(self, env):
- token_types = [
- t.type for t in TokenStream(self.test_tokens, "foo", "bar")
+ token_types = [t.type for t in TokenStream(self.test_tokens, "foo", "bar")]
+ assert token_types == [
+ "block_begin",
+ "block_end",
]
- assert token_types == ['block_begin', 'block_end', ]
@pytest.mark.lexnparse
@pytest.mark.lexer
class TestLexer(object):
-
def test_raw1(self, env):
tmpl = env.from_string(
- '{% raw %}foo{% endraw %}|'
- '{%raw%}{{ bar }}|{% baz %}{% endraw %}')
- assert tmpl.render() == 'foo|{{ bar }}|{% baz %}'
+ "{% raw %}foo{% endraw %}|"
+ "{%raw%}{{ bar }}|{% baz %}{% endraw %}"
+ )
+ assert tmpl.render() == "foo|{{ bar }}|{% baz %}"
def test_raw2(self, env):
- tmpl = env.from_string('1 {%- raw -%} 2 {%- endraw -%} 3')
- assert tmpl.render() == '123'
+ tmpl = env.from_string("1 {%- raw -%} 2 {%- endraw -%} 3")
+ assert tmpl.render() == "123"
def test_raw3(self, env):
# The second newline after baz exists because it is AFTER the {% raw %} and is ignored.
env = Environment(lstrip_blocks=True, trim_blocks=True)
tmpl = env.from_string("bar\n{% raw %}\n {{baz}}2 spaces\n{% endraw %}\nfoo")
- assert tmpl.render(baz='test') == "bar\n\n {{baz}}2 spaces\nfoo"
+ assert tmpl.render(baz="test") == "bar\n\n {{baz}}2 spaces\nfoo"
def test_raw4(self, env):
# The trailing dash of the {% raw -%} cleans both the spaces and newlines up to the first character of data.
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string("bar\n{%- raw -%}\n\n \n 2 spaces\n space{%- endraw -%}\nfoo")
+ tmpl = env.from_string(
+ "bar\n{%- raw -%}\n\n \n 2 spaces\n space{%- endraw -%}\nfoo"
+ )
assert tmpl.render() == "bar2 spaces\n spacefoo"
def test_balancing(self, env):
- env = Environment('{%', '%}', '${', '}')
- tmpl = env.from_string('''{% for item in seq
- %}${{'foo': item}|upper}{% endfor %}''')
- assert tmpl.render(seq=list(range(3))) \
- == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
+ env = Environment("{%", "%}", "${", "}")
+ tmpl = env.from_string(
+ """{% for item in seq
+ %}${{'foo': item}|upper}{% endfor %}"""
+ )
+ assert tmpl.render(seq=list(range(3))) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
def test_comments(self, env):
- env = Environment('<!--', '-->', '{', '}')
- tmpl = env.from_string('''\
+ env = Environment("<!--", "-->", "{", "}")
+ tmpl = env.from_string(
+ """\
<ul>
<!--- for item in seq -->
<li>{item}</li>
<!--- endfor -->
-</ul>''')
- assert tmpl.render(seq=list(range(3))) \
- == ("<ul>\n <li>0</li>\n ""<li>1</li>\n <li>2</li>\n</ul>")
+</ul>"""
+ )
+ assert tmpl.render(seq=list(range(3))) == (
+ "<ul>\n <li>0</li>\n <li>1</li>\n <li>2</li>\n</ul>"
+ )
def test_string_escapes(self, env):
- for char in u'\0', u'\u2668', u'\xe4', u'\t', u'\r', u'\n':
- tmpl = env.from_string('{{ %s }}' % jinja_string_repr(char))
+ 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'
+ 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')
+
+ 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 '([{}])':
+ if test in "([{}])":
continue
- stream = env.lexer.tokenize('{{ %s }}' % test)
+ 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':
+ for seq in "\r", "\r\n", "\n":
env = Environment(newline_sequence=seq)
- tmpl = env.from_string('1\n2\r\n3\n4\n')
+ tmpl = env.from_string("1\n2\r\n3\n4\n")
result = tmpl.render()
- assert result.replace(seq, 'X') == '1X2X3X4'
+ assert result.replace(seq, "X") == "1X2X3X4"
def test_trailing_newline(self, env):
for keep in [True, False]:
env = Environment(keep_trailing_newline=keep)
for template, expected in [
- ('', {}),
- ('no\nnewline', {}),
- ('with\nnewline\n', {False: 'with\nnewline'}),
- ('with\nseveral\n\n\n', {False: 'with\nseveral\n\n'}),
- ]:
+ ("", {}),
+ ("no\nnewline", {}),
+ ("with\nnewline\n", {False: "with\nnewline"}),
+ ("with\nseveral\n\n\n", {False: "with\nseveral\n\n"}),
+ ]:
tmpl = env.from_string(template)
expect = expected.get(keep, template)
result = tmpl.render()
assert result == expect, (keep, template, result, expect)
- @pytest.mark.parametrize('name,valid2,valid3', (
- (u'foo', True, True),
- (u'föö', False, True),
- (u'き', False, True),
- (u'_', True, True),
- (u'1a', False, False), # invalid ascii start
- (u'a-', False, False), # invalid ascii continue
- (u'🐍', False, False), # invalid unicode start
- (u'a🐍', False, False), # invalid unicode continue
- # start characters not matched by \w
- (u'\u1885', False, True),
- (u'\u1886', False, True),
- (u'\u2118', False, True),
- (u'\u212e', False, True),
- # continue character not matched by \w
- (u'\xb7', False, False),
- (u'a\xb7', False, True),
- ))
+ @pytest.mark.parametrize(
+ "name,valid2,valid3",
+ (
+ (u"foo", True, True),
+ (u"föö", False, True),
+ (u"き", False, True),
+ (u"_", True, True),
+ (u"1a", False, False), # invalid ascii start
+ (u"a-", False, False), # invalid ascii continue
+ (u"🐍", False, False), # invalid unicode start
+ (u"a🐍", False, False), # invalid unicode continue
+ # start characters not matched by \w
+ (u"\u1885", False, True),
+ (u"\u1886", False, True),
+ (u"\u2118", False, True),
+ (u"\u212e", False, True),
+ # continue character not matched by \w
+ (u"\xb7", False, False),
+ (u"a\xb7", False, True),
+ ),
+ )
def test_name(self, env, name, valid2, valid3):
- t = u'{{ ' + name + u' }}'
+ t = u"{{ " + name + u" }}"
if (valid2 and PY2) or (valid3 and not PY2):
# valid for version being tested, shouldn't raise
@@ -177,159 +193,178 @@ class TestLexer(object):
@pytest.mark.lexnparse
@pytest.mark.parser
class TestParser(object):
-
def test_php_syntax(self, env):
- env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->')
- tmpl = env.from_string('''\
+ env = Environment("<?", "?>", "<?=", "?>", "<!--", "-->")
+ tmpl = env.from_string(
+ """\
<!-- I'm a comment, I'm not interesting -->\
<? for item in seq -?>
<?= item ?>
-<?- endfor ?>''')
- assert tmpl.render(seq=list(range(5))) == '01234'
+<?- endfor ?>"""
+ )
+ assert tmpl.render(seq=list(range(5))) == "01234"
def test_erb_syntax(self, env):
- env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>')
- tmpl = env.from_string('''\
+ env = Environment("<%", "%>", "<%=", "%>", "<%#", "%>")
+ tmpl = env.from_string(
+ """\
<%# I'm a comment, I'm not interesting %>\
<% for item in seq -%>
<%= item %>
-<%- endfor %>''')
- assert tmpl.render(seq=list(range(5))) == '01234'
+<%- endfor %>"""
+ )
+ assert tmpl.render(seq=list(range(5))) == "01234"
def test_comment_syntax(self, env):
- env = Environment('<!--', '-->', '${', '}', '<!--#', '-->')
- tmpl = env.from_string('''\
+ env = Environment("<!--", "-->", "${", "}", "<!--#", "-->")
+ tmpl = env.from_string(
+ """\
<!--# I'm a comment, I'm not interesting -->\
<!-- for item in seq --->
${item}
-<!--- endfor -->''')
- assert tmpl.render(seq=list(range(5))) == '01234'
+<!--- endfor -->"""
+ )
+ assert tmpl.render(seq=list(range(5))) == "01234"
def test_balancing(self, env):
- tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''')
- assert tmpl.render() == 'bar'
+ tmpl = env.from_string("""{{{'foo':'bar'}.foo}}""")
+ assert tmpl.render() == "bar"
def test_start_comment(self, env):
- tmpl = env.from_string('''{# foo comment
+ tmpl = env.from_string(
+ """{# foo comment
and bar comment #}
{% macro blub() %}foo{% endmacro %}
-{{ blub() }}''')
- assert tmpl.render().strip() == 'foo'
+{{ blub() }}"""
+ )
+ assert tmpl.render().strip() == "foo"
def test_line_syntax(self, env):
- env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%')
- tmpl = env.from_string('''\
+ env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%")
+ tmpl = env.from_string(
+ """\
<%# regular comment %>
% for item in seq:
${item}
-% endfor''')
+% endfor"""
+ )
assert [
int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()
] == list(range(5))
- env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##')
- tmpl = env.from_string('''\
+ env = Environment("<%", "%>", "${", "}", "<%#", "%>", "%", "##")
+ tmpl = env.from_string(
+ """\
<%# regular comment %>
% for item in seq:
${item} ## the rest of the stuff
-% endfor''')
+% endfor"""
+ )
assert [
int(x.strip()) for x in tmpl.render(seq=list(range(5))).split()
] == list(range(5))
def test_line_syntax_priority(self, env):
# XXX: why is the whitespace there in front of the newline?
- env = Environment('{%', '%}', '${', '}', '/*', '*/', '##', '#')
- tmpl = env.from_string('''\
+ env = Environment("{%", "%}", "${", "}", "/*", "*/", "##", "#")
+ tmpl = env.from_string(
+ """\
/* ignore me.
I'm a multiline comment */
## for item in seq:
* ${item} # this is just extra stuff
-## endfor''')
- assert tmpl.render(seq=[1, 2]).strip() == '* 1\n* 2'
- env = Environment('{%', '%}', '${', '}', '/*', '*/', '#', '##')
- tmpl = env.from_string('''\
+## endfor"""
+ )
+ assert tmpl.render(seq=[1, 2]).strip() == "* 1\n* 2"
+ env = Environment("{%", "%}", "${", "}", "/*", "*/", "#", "##")
+ tmpl = env.from_string(
+ """\
/* ignore me.
I'm a multiline comment */
# for item in seq:
* ${item} ## this is just extra stuff
## extra stuff i just want to ignore
-# endfor''')
- assert tmpl.render(seq=[1, 2]).strip() == '* 1\n\n* 2'
+# endfor"""
+ )
+ assert tmpl.render(seq=[1, 2]).strip() == "* 1\n\n* 2"
def test_error_messages(self, env):
def assert_error(code, expected):
with pytest.raises(TemplateSyntaxError, match=expected):
Template(code)
- assert_error('{% for item in seq %}...{% endif %}',
- "Encountered unknown tag 'endif'. Jinja was looking "
- "for the following tags: 'endfor' or 'else'. The "
- "innermost block that needs to be closed is 'for'.")
assert_error(
- '{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}',
+ "{% for item in seq %}...{% endif %}",
+ "Encountered unknown tag 'endif'. Jinja was looking "
+ "for the following tags: 'endfor' or 'else'. The "
+ "innermost block that needs to be closed is 'for'.",
+ )
+ assert_error(
+ "{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}",
"Encountered unknown tag 'endfor'. Jinja was looking for "
"the following tags: 'elif' or 'else' or 'endif'. The "
- "innermost block that needs to be closed is 'if'.")
- 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'.")
+ "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(
- '{% block foo-bar-baz %}',
+ "{% 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'.")
+ "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(object):
-
def test_call(self, env):
env = Environment()
- env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g
- tmpl = env.from_string(
- "{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}"
- )
- assert tmpl.render() == 'abdfh'
+ env.globals["foo"] = lambda a, b, c, e, g: a + b + c + e + g
+ tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}")
+ assert tmpl.render() == "abdfh"
def test_slicing(self, env):
- tmpl = env.from_string('{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}')
- assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+ tmpl = env.from_string("{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}")
+ assert tmpl.render() == "[1, 2, 3]|[3, 2, 1]"
def test_attr(self, env):
tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}")
- assert tmpl.render(foo={'bar': 42}) == '42|42'
+ assert tmpl.render(foo={"bar": 42}) == "42|42"
def test_subscript(self, env):
tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}")
- assert tmpl.render(foo=[0, 1, 2]) == '0|2'
+ assert tmpl.render(foo=[0, 1, 2]) == "0|2"
def test_tuple(self, env):
- tmpl = env.from_string('{{ () }}|{{ (1,) }}|{{ (1, 2) }}')
- assert tmpl.render() == '()|(1,)|(1, 2)'
+ tmpl = env.from_string("{{ () }}|{{ (1,) }}|{{ (1, 2) }}")
+ assert tmpl.render() == "()|(1,)|(1, 2)"
def test_math(self, env):
- tmpl = env.from_string('{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}')
- assert tmpl.render() == '1.5|8'
+ tmpl = env.from_string("{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}")
+ assert tmpl.render() == "1.5|8"
def test_div(self, env):
- tmpl = env.from_string('{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}')
- assert tmpl.render() == '1|1.5|1'
+ tmpl = env.from_string("{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}")
+ assert tmpl.render() == "1|1.5|1"
def test_unary(self, env):
- tmpl = env.from_string('{{ +3 }}|{{ -3 }}')
- assert tmpl.render() == '3|-3'
+ tmpl = env.from_string("{{ +3 }}|{{ -3 }}")
+ assert tmpl.render() == "3|-3"
def test_concat(self, env):
tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}")
- assert tmpl.render() == '[1, 2]foo'
+ assert tmpl.render() == "[1, 2]foo"
@pytest.mark.parametrize(
("a", "op", "b"),
@@ -366,8 +401,8 @@ class TestSyntax(object):
assert t.render(a=4, b=2, c=3) == expect
def test_inop(self, env):
- tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}')
- assert tmpl.render() == 'True|False'
+ tmpl = env.from_string("{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}")
+ assert tmpl.render() == "True|False"
@pytest.mark.parametrize("value", ("[]", "{}", "()"))
def test_collection_literal(self, env, value):
@@ -389,332 +424,414 @@ class TestSyntax(object):
("2.5e+100", "2.5e+100"),
("25.6e-10", "2.56e-09"),
("1_2.3_4e5_6", "1.234e+57"),
- )
+ ),
)
def test_numeric_literal(self, env, value, expect):
t = env.from_string("{{ %s }}" % value)
assert t.render() == expect
def test_bool(self, env):
- tmpl = env.from_string('{{ true and false }}|{{ false '
- 'or true }}|{{ not false }}')
- assert tmpl.render() == 'False|True|True'
+ tmpl = env.from_string(
+ "{{ true and false }}|{{ false or true }}|{{ not false }}"
+ )
+ assert tmpl.render() == "False|True|True"
def test_grouping(self, env):
tmpl = env.from_string(
- '{{ (true and false) or (false and true) and not false }}')
- assert tmpl.render() == 'False'
+ "{{ (true and false) or (false and true) and not false }}"
+ )
+ assert tmpl.render() == "False"
def test_django_attr(self, env):
- tmpl = env.from_string('{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}')
- assert tmpl.render() == '1|1'
+ tmpl = env.from_string("{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}")
+ assert tmpl.render() == "1|1"
def test_conditional_expression(self, env):
- tmpl = env.from_string('''{{ 0 if true else 1 }}''')
- assert tmpl.render() == '0'
+ tmpl = env.from_string("""{{ 0 if true else 1 }}""")
+ assert tmpl.render() == "0"
def test_short_conditional_expression(self, env):
- tmpl = env.from_string('<{{ 1 if false }}>')
- assert tmpl.render() == '<>'
+ tmpl = env.from_string("<{{ 1 if false }}>")
+ assert tmpl.render() == "<>"
- tmpl = env.from_string('<{{ (1 if false).bar }}>')
+ tmpl = env.from_string("<{{ (1 if false).bar }}>")
pytest.raises(UndefinedError, tmpl.render)
def test_filter_priority(self, env):
tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}')
- assert tmpl.render() == 'FOOBAR'
+ assert tmpl.render() == "FOOBAR"
def test_function_calls(self, env):
tests = [
- (True, '*foo, bar'),
- (True, '*foo, *bar'),
- (True, '**foo, *bar'),
- (True, '**foo, bar'),
- (True, '**foo, **bar'),
- (True, '**foo, bar=42'),
- (False, 'foo, bar'),
- (False, 'foo, bar=42'),
- (False, 'foo, bar=23, *args'),
- (False, 'foo, *args, bar=23'),
- (False, 'a, b=c, *d, **e'),
- (False, '*foo, bar=42'),
- (False, '*foo, **bar'),
- (False, '*foo, bar=42, **baz'),
- (False, 'foo, *args, bar=23, **baz'),
+ (True, "*foo, bar"),
+ (True, "*foo, *bar"),
+ (True, "**foo, *bar"),
+ (True, "**foo, bar"),
+ (True, "**foo, **bar"),
+ (True, "**foo, bar=42"),
+ (False, "foo, bar"),
+ (False, "foo, bar=42"),
+ (False, "foo, bar=23, *args"),
+ (False, "foo, *args, bar=23"),
+ (False, "a, b=c, *d, **e"),
+ (False, "*foo, bar=42"),
+ (False, "*foo, **bar"),
+ (False, "*foo, bar=42, **baz"),
+ (False, "foo, *args, bar=23, **baz"),
]
for should_fail, sig in tests:
if should_fail:
- pytest.raises(TemplateSyntaxError,
- env.from_string, '{{ foo(%s) }}' % sig)
+ pytest.raises(
+ TemplateSyntaxError, env.from_string, "{{ foo(%s) }}" % sig
+ )
else:
- env.from_string('foo(%s)' % sig)
+ 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 %}'
+ "{{ () }}",
+ "{{ (1, 2) }}",
+ "{{ (1, 2,) }}",
+ "{{ 1, }}",
+ "{{ 1, 2 }}",
+ "{% for foo, bar in seq %}...{% endfor %}",
+ "{% for x in foo, bar %}...{% endfor %}",
+ "{% for x in foo, %}...{% endfor %}",
]:
assert env.from_string(tmpl)
def test_trailing_comma(self, env):
- tmpl = env.from_string('{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}')
- assert tmpl.render().lower() == '(1, 2)|[1, 2]|{1: 2}'
+ tmpl = env.from_string("{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}")
+ assert tmpl.render().lower() == "(1, 2)|[1, 2]|{1: 2}"
def test_block_end_name(self, env):
- env.from_string('{% block foo %}...{% endblock foo %}')
- pytest.raises(TemplateSyntaxError, env.from_string,
- '{% block x %}{% endblock y %}')
+ env.from_string("{% block foo %}...{% endblock foo %}")
+ pytest.raises(
+ TemplateSyntaxError, env.from_string, "{% block x %}{% endblock y %}"
+ )
def test_constant_casing(self, env):
for const in True, False, None:
- tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % (
- str(const), str(const).lower(), str(const).upper()
- ))
- assert tmpl.render() == '%s|%s|' % (const, const)
+ 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'
+ pytest.raises(
+ TemplateSyntaxError, env.from_string, "{{ foo is string is sequence }}"
+ )
+ assert env.from_string("{{ 42 is string or 42 is number }}").render() == "True"
def test_string_concatenation(self, env):
tmpl = env.from_string('{{ "foo" "bar" "baz" }}')
- assert tmpl.render() == 'foobarbaz'
+ assert tmpl.render() == "foobarbaz"
def test_notin(self, env):
bar = range(100)
- tmpl = env.from_string('''{{ not 42 in bar }}''')
+ tmpl = env.from_string("""{{ not 42 in bar }}""")
assert tmpl.render(bar=bar) == text_type(not 42 in bar)
def test_operator_precedence(self, env):
- tmpl = env.from_string('''{{ 2 * 3 + 4 % 2 + 1 - 2 }}''')
+ tmpl = env.from_string("""{{ 2 * 3 + 4 % 2 + 1 - 2 }}""")
assert tmpl.render() == text_type(2 * 3 + 4 % 2 + 1 - 2)
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)'
+
+ 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 %}'
+ tmpl = env.from_string("{% raw %}{{ FOO }} and {% BAR %}{% endraw %}")
+ assert tmpl.render() == "{{ FOO }} and {% BAR %}"
def test_const(self, env):
tmpl = env.from_string(
- '{{ true }}|{{ false }}|{{ none }}|'
- '{{ none is defined }}|{{ missing is defined }}')
- assert tmpl.render() == 'True|False|None|True|False'
+ "{{ true }}|{{ false }}|{{ none }}|"
+ "{{ none is defined }}|{{ missing is defined }}"
+ )
+ assert tmpl.render() == "True|False|None|True|False"
def test_neg_filter_priority(self, env):
- node = env.parse('{{ -1|foo }}')
+ node = env.parse("{{ -1|foo }}")
assert isinstance(node.body[0].nodes[0], nodes.Filter)
assert isinstance(node.body[0].nodes[0].node, nodes.Neg)
def test_const_assign(self, env):
- constass1 = '''{% set true = 42 %}'''
- constass2 = '''{% for none in seq %}{% endfor %}'''
+ constass1 = """{% set true = 42 %}"""
+ constass2 = """{% for none in seq %}{% endfor %}"""
for tmpl in constass1, constass2:
pytest.raises(TemplateSyntaxError, env.from_string, tmpl)
def test_localset(self, env):
- tmpl = env.from_string('''{% set foo = 0 %}\
+ tmpl = env.from_string(
+ """{% set foo = 0 %}\
{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\
-{{ foo }}''')
- assert tmpl.render() == '0'
+{{ foo }}"""
+ )
+ assert tmpl.render() == "0"
def test_parse_unary(self, env):
tmpl = env.from_string('{{ -foo["bar"] }}')
- assert tmpl.render(foo={'bar': 42}) == '-42'
+ assert tmpl.render(foo={"bar": 42}) == "-42"
tmpl = env.from_string('{{ -foo["bar"]|abs }}')
- assert tmpl.render(foo={'bar': 42}) == '42'
+ assert tmpl.render(foo={"bar": 42}) == "42"
@pytest.mark.lexnparse
@pytest.mark.lstripblocks
class TestLstripBlocks(object):
-
def test_lstrip(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string(''' {% if True %}\n {% endif %}''')
+ tmpl = env.from_string(""" {% if True %}\n {% endif %}""")
assert tmpl.render() == "\n"
def test_lstrip_trim(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=True)
- tmpl = env.from_string(''' {% if True %}\n {% endif %}''')
+ tmpl = env.from_string(""" {% if True %}\n {% endif %}""")
assert tmpl.render() == ""
def test_no_lstrip(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string(''' {%+ if True %}\n {%+ endif %}''')
+ tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""")
assert tmpl.render() == " \n "
def test_lstrip_blocks_false_with_no_lstrip(self, env):
# Test that + is a NOP (but does not cause an error) if lstrip_blocks=False
env = Environment(lstrip_blocks=False, trim_blocks=False)
- tmpl = env.from_string(''' {% if True %}\n {% endif %}''')
+ tmpl = env.from_string(""" {% if True %}\n {% endif %}""")
assert tmpl.render() == " \n "
- tmpl = env.from_string(''' {%+ if True %}\n {%+ endif %}''')
+ tmpl = env.from_string(""" {%+ if True %}\n {%+ endif %}""")
assert tmpl.render() == " \n "
def test_lstrip_endline(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string(
- ''' hello{% if True %}\n goodbye{% endif %}''')
+ tmpl = env.from_string(""" hello{% if True %}\n goodbye{% endif %}""")
assert tmpl.render() == " hello\n goodbye"
def test_lstrip_inline(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string(''' {% if True %}hello {% endif %}''')
- assert tmpl.render() == 'hello '
+ tmpl = env.from_string(""" {% if True %}hello {% endif %}""")
+ assert tmpl.render() == "hello "
def test_lstrip_nested(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
tmpl = env.from_string(
- ''' {% if True %}a {% if True %}b {% endif %}c {% endif %}''')
- assert tmpl.render() == 'a b c '
+ """ {% if True %}a {% if True %}b {% endif %}c {% endif %}"""
+ )
+ assert tmpl.render() == "a b c "
def test_lstrip_left_chars(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string(''' abc {% if True %}
- hello{% endif %}''')
- assert tmpl.render() == ' abc \n hello'
+ tmpl = env.from_string(
+ """ abc {% if True %}
+ hello{% endif %}"""
+ )
+ assert tmpl.render() == " abc \n hello"
def test_lstrip_embeded_strings(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string(''' {% set x = " {% str %} " %}{{ x }}''')
- assert tmpl.render() == ' {% str %} '
+ tmpl = env.from_string(""" {% set x = " {% str %} " %}{{ x }}""")
+ assert tmpl.render() == " {% str %} "
def test_lstrip_preserve_leading_newlines(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string('''\n\n\n{% set hello = 1 %}''')
- assert tmpl.render() == '\n\n\n'
+ tmpl = env.from_string("""\n\n\n{% set hello = 1 %}""")
+ assert tmpl.render() == "\n\n\n"
def test_lstrip_comment(self, env):
env = Environment(lstrip_blocks=True, trim_blocks=False)
- tmpl = env.from_string(''' {# if True #}
+ tmpl = env.from_string(
+ """ {# if True #}
hello
- {#endif#}''')
- assert tmpl.render() == '\nhello\n'
+ {#endif#}"""
+ )
+ assert tmpl.render() == "\nhello\n"
def test_lstrip_angle_bracket_simple(self, env):
- env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##',
- lstrip_blocks=True, trim_blocks=True)
- tmpl = env.from_string(''' <% if True %>hello <% endif %>''')
- assert tmpl.render() == 'hello '
+ env = Environment(
+ "<%",
+ "%>",
+ "${",
+ "}",
+ "<%#",
+ "%>",
+ "%",
+ "##",
+ lstrip_blocks=True,
+ trim_blocks=True,
+ )
+ tmpl = env.from_string(""" <% if True %>hello <% endif %>""")
+ assert tmpl.render() == "hello "
def test_lstrip_angle_bracket_comment(self, env):
- env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##',
- lstrip_blocks=True, trim_blocks=True)
- tmpl = env.from_string(''' <%# if True %>hello <%# endif %>''')
- assert tmpl.render() == 'hello '
+ env = Environment(
+ "<%",
+ "%>",
+ "${",
+ "}",
+ "<%#",
+ "%>",
+ "%",
+ "##",
+ lstrip_blocks=True,
+ trim_blocks=True,
+ )
+ tmpl = env.from_string(""" <%# if True %>hello <%# endif %>""")
+ assert tmpl.render() == "hello "
def test_lstrip_angle_bracket(self, env):
- env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##',
- lstrip_blocks=True, trim_blocks=True)
- tmpl = env.from_string('''\
+ env = Environment(
+ "<%",
+ "%>",
+ "${",
+ "}",
+ "<%#",
+ "%>",
+ "%",
+ "##",
+ lstrip_blocks=True,
+ trim_blocks=True,
+ )
+ tmpl = env.from_string(
+ """\
<%# regular comment %>
<% for item in seq %>
${item} ## the rest of the stuff
- <% endfor %>''')
- assert tmpl.render(seq=range(5)) == \
- ''.join('%s\n' % x for x in range(5))
+ <% 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('''\
+ 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))
+ <%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('''\
+ env = Environment(
+ "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
+ )
+ tmpl = env.from_string(
+ """\
<!-- I'm a comment, I'm not interesting -->
<? for item in seq -?>
<?= item ?>
- <?- endfor ?>''')
- assert tmpl.render(seq=range(5)) == '01234'
+ <?- endfor ?>"""
+ )
+ assert tmpl.render(seq=range(5)) == "01234"
def test_php_syntax(self, env):
- env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->',
- lstrip_blocks=True, trim_blocks=True)
- tmpl = env.from_string('''\
+ env = Environment(
+ "<?", "?>", "<?=", "?>", "<!--", "-->", lstrip_blocks=True, trim_blocks=True
+ )
+ tmpl = env.from_string(
+ """\
<!-- I'm a comment, I'm not interesting -->
<? for item in seq ?>
<?= item ?>
- <? endfor ?>''')
- assert tmpl.render(seq=range(5)) \
- == ''.join(' %s\n' % x for x in range(5))
+ <? 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('''\
+ 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))
+ <?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 = 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('''\
+ 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))
+"""
+ )
+ 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('''\
+ env = Environment(
+ "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
+ )
+ tmpl = env.from_string(
+ """\
<%# I'm a comment, I'm not interesting %>
<% for item in seq -%>
<%= item %>
- <%- endfor %>''')
- assert tmpl.render(seq=range(5)) == '01234'
+ <%- endfor %>"""
+ )
+ assert tmpl.render(seq=range(5)) == "01234"
def test_erb_syntax_no_lstrip(self, env):
- env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>',
- lstrip_blocks=True, trim_blocks=True)
- tmpl = env.from_string('''\
+ env = Environment(
+ "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
+ )
+ tmpl = env.from_string(
+ """\
<%# I'm a comment, I'm not interesting %>
<%+ for item in seq -%>
<%= item %>
- <%- endfor %>''')
- assert tmpl.render(seq=range(5)) == ' 01234'
+ <%- endfor %>"""
+ )
+ assert tmpl.render(seq=range(5)) == " 01234"
def test_comment_syntax(self, env):
- env = Environment('<!--', '-->', '${', '}', '<!--#', '-->',
- lstrip_blocks=True, trim_blocks=True)
- tmpl = env.from_string('''\
+ env = Environment(
+ "<!--",
+ "-->",
+ "${",
+ "}",
+ "<!--#",
+ "-->",
+ lstrip_blocks=True,
+ trim_blocks=True,
+ )
+ tmpl = env.from_string(
+ """\
<!--# I'm a comment, I'm not interesting -->\
<!-- for item in seq --->
${item}
-<!--- endfor -->''')
- assert tmpl.render(seq=range(5)) == '01234'
+<!--- endfor -->"""
+ )
+ assert tmpl.render(seq=range(5)) == "01234"
diff --git a/tests/test_loader.py b/tests/test_loader.py
index 68c1384..44000e1 100644
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -30,15 +30,15 @@ from jinja2.loaders import split_template_path
class TestLoaders(object):
def test_dict_loader(self, dict_loader):
env = Environment(loader=dict_loader)
- tmpl = env.get_template('justdict.html')
- assert tmpl.render().strip() == 'FOO'
- pytest.raises(TemplateNotFound, env.get_template, 'missing.html')
+ tmpl = env.get_template("justdict.html")
+ assert tmpl.render().strip() == "FOO"
+ pytest.raises(TemplateNotFound, env.get_template, "missing.html")
def test_package_loader(self, package_loader):
env = Environment(loader=package_loader)
- tmpl = env.get_template('test.html')
- assert tmpl.render().strip() == 'BAR'
- pytest.raises(TemplateNotFound, env.get_template, 'missing.html')
+ tmpl = env.get_template("test.html")
+ assert tmpl.render().strip() == "BAR"
+ pytest.raises(TemplateNotFound, env.get_template, "missing.html")
def test_filesystem_loader_overlapping_names(self, filesystem_loader):
res = os.path.dirname(filesystem_loader.searchpath[0])
@@ -52,91 +52,94 @@ class TestLoaders(object):
def test_choice_loader(self, choice_loader):
env = Environment(loader=choice_loader)
- tmpl = env.get_template('justdict.html')
- assert tmpl.render().strip() == 'FOO'
- tmpl = env.get_template('test.html')
- assert tmpl.render().strip() == 'BAR'
- pytest.raises(TemplateNotFound, env.get_template, 'missing.html')
+ tmpl = env.get_template("justdict.html")
+ assert tmpl.render().strip() == "FOO"
+ tmpl = env.get_template("test.html")
+ assert tmpl.render().strip() == "BAR"
+ pytest.raises(TemplateNotFound, env.get_template, "missing.html")
def test_function_loader(self, function_loader):
env = Environment(loader=function_loader)
- tmpl = env.get_template('justfunction.html')
- assert tmpl.render().strip() == 'FOO'
- pytest.raises(TemplateNotFound, env.get_template, 'missing.html')
+ tmpl = env.get_template("justfunction.html")
+ assert tmpl.render().strip() == "FOO"
+ pytest.raises(TemplateNotFound, env.get_template, "missing.html")
def test_prefix_loader(self, prefix_loader):
env = Environment(loader=prefix_loader)
- tmpl = env.get_template('a/test.html')
- assert tmpl.render().strip() == 'BAR'
- tmpl = env.get_template('b/justdict.html')
- assert tmpl.render().strip() == 'FOO'
- pytest.raises(TemplateNotFound, env.get_template, 'missing')
+ tmpl = env.get_template("a/test.html")
+ assert tmpl.render().strip() == "BAR"
+ tmpl = env.get_template("b/justdict.html")
+ assert tmpl.render().strip() == "FOO"
+ pytest.raises(TemplateNotFound, env.get_template, "missing")
def test_caching(self):
changed = False
class TestLoader(loaders.BaseLoader):
def get_source(self, environment, template):
- return u'foo', None, lambda: not changed
+ 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')
+ tmpl = env.get_template("template")
+ assert tmpl is env.get_template("template")
changed = True
- assert tmpl is not env.get_template('template')
+ assert tmpl is not env.get_template("template")
changed = False
def test_no_cache(self):
- mapping = {'foo': 'one'}
+ mapping = {"foo": "one"}
env = Environment(loader=loaders.DictLoader(mapping), cache_size=0)
- assert env.get_template('foo') is not env.get_template('foo')
+ assert env.get_template("foo") is not env.get_template("foo")
def test_limited_size_cache(self):
- mapping = {'one': 'foo', 'two': 'bar', 'three': 'baz'}
+ mapping = {"one": "foo", "two": "bar", "three": "baz"}
loader = loaders.DictLoader(mapping)
env = Environment(loader=loader, cache_size=2)
- t1 = env.get_template('one')
- t2 = env.get_template('two')
- assert t2 is env.get_template('two')
- assert t1 is env.get_template('one')
- t3 = env.get_template('three')
+ t1 = env.get_template("one")
+ t2 = env.get_template("two")
+ assert t2 is env.get_template("two")
+ assert t1 is env.get_template("one")
+ t3 = env.get_template("three")
loader_ref = weakref.ref(loader)
- assert (loader_ref, 'one') in env.cache
- assert (loader_ref, 'two') not in env.cache
- assert (loader_ref, 'three') in env.cache
+ assert (loader_ref, "one") in env.cache
+ assert (loader_ref, "two") not in env.cache
+ assert (loader_ref, "three") in env.cache
def test_cache_loader_change(self):
- loader1 = loaders.DictLoader({'foo': 'one'})
- loader2 = loaders.DictLoader({'foo': 'two'})
+ loader1 = loaders.DictLoader({"foo": "one"})
+ loader2 = loaders.DictLoader({"foo": "two"})
env = Environment(loader=loader1, cache_size=2)
- assert env.get_template('foo').render() == 'one'
+ assert env.get_template("foo").render() == "one"
env.loader = loader2
- assert env.get_template('foo').render() == 'two'
+ assert env.get_template("foo").render() == "two"
def test_dict_loader_cache_invalidates(self):
- mapping = {'foo': "one"}
+ mapping = {"foo": "one"}
env = Environment(loader=loaders.DictLoader(mapping))
- assert env.get_template('foo').render() == "one"
- mapping['foo'] = "two"
- assert env.get_template('foo').render() == "two"
+ assert env.get_template("foo").render() == "one"
+ mapping["foo"] = "two"
+ assert env.get_template("foo").render() == "two"
def test_split_template_path(self):
- assert split_template_path('foo/bar') == ['foo', 'bar']
- assert split_template_path('./foo/bar') == ['foo', 'bar']
- pytest.raises(TemplateNotFound, split_template_path, '../foo')
+ 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.filesystemloader
class TestFileSystemLoader(object):
- searchpath = os.path.dirname(os.path.abspath(__file__)) + '/res/templates'
+ searchpath = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), "res", "templates"
+ )
@staticmethod
def _test_common(env):
- tmpl = env.get_template('test.html')
- assert tmpl.render().strip() == 'BAR'
- tmpl = env.get_template('foo/test.html')
- assert tmpl.render().strip() == 'FOO'
- pytest.raises(TemplateNotFound, env.get_template, 'missing.html')
+ tmpl = env.get_template("test.html")
+ assert tmpl.render().strip() == "BAR"
+ tmpl = env.get_template("foo/test.html")
+ assert tmpl.render().strip() == "FOO"
+ pytest.raises(TemplateNotFound, env.get_template, "missing.html")
def test_searchpath_as_str(self):
filesystem_loader = loaders.FileSystemLoader(self.searchpath)
@@ -144,9 +147,10 @@ class TestFileSystemLoader(object):
env = Environment(loader=filesystem_loader)
self._test_common(env)
- @pytest.mark.skipif(PY2, reason='pathlib is not available in Python 2')
+ @pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
def test_searchpath_as_pathlib(self):
import pathlib
+
searchpath = pathlib.Path(self.searchpath)
filesystem_loader = loaders.FileSystemLoader(searchpath)
@@ -154,12 +158,13 @@ class TestFileSystemLoader(object):
env = Environment(loader=filesystem_loader)
self._test_common(env)
- @pytest.mark.skipif(PY2, reason='pathlib is not available in Python 2')
+ @pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
def test_searchpath_as_list_including_pathlib(self):
import pathlib
+
searchpath = pathlib.Path(self.searchpath)
- filesystem_loader = loaders.FileSystemLoader(['/tmp/templates', searchpath])
+ filesystem_loader = loaders.FileSystemLoader(["/tmp/templates", searchpath])
env = Environment(loader=filesystem_loader)
self._test_common(env)
@@ -168,26 +173,23 @@ class TestFileSystemLoader(object):
filesystem_loader = loaders.FileSystemLoader(self.searchpath)
env = Environment(loader=filesystem_loader)
- tmpl1 = env.get_template('test.html')
- tmpl2 = env.get_template('test.html')
+ tmpl1 = env.get_template("test.html")
+ tmpl2 = env.get_template("test.html")
assert tmpl1 is tmpl2
- os.utime(
- os.path.join(self.searchpath, "test.html"),
- (time.time(), time.time())
- )
- tmpl3 = env.get_template('test.html')
+ os.utime(os.path.join(self.searchpath, "test.html"), (time.time(), time.time()))
+ tmpl3 = env.get_template("test.html")
assert tmpl1 is not tmpl3
- @pytest.mark.parametrize('encoding, expected_text', [
- ('utf-8', u'tech'),
- ('utf-16', u'整档'),
- ])
- def test_uses_specified_encoding(self, encoding, expected_text):
- filesystem_loader = loaders.FileSystemLoader(self.searchpath, encoding=encoding)
- env = Environment(loader=filesystem_loader)
- tmpl = env.get_template('variable_encoding.txt')
- assert tmpl.render().strip() == expected_text
+ @pytest.mark.parametrize(
+ ("encoding", "expect"),
+ [("utf-8", u"文字化け"), ("iso-8859-1", u"æ\x96\x87å\xad\x97å\x8c\x96ã\x81\x91"),],
+ )
+ def test_uses_specified_encoding(self, encoding, expect):
+ loader = loaders.FileSystemLoader(self.searchpath, encoding=encoding)
+ e = Environment(loader=loader)
+ t = e.get_template("mojibake.txt")
+ assert t.render() == expect
@pytest.mark.loaders
@@ -195,22 +197,22 @@ class TestFileSystemLoader(object):
class TestModuleLoader(object):
archive = None
- def compile_down(self, prefix_loader, zip='deflated', py_compile=False):
+ 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')
+ 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.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)
+ return "".join(log)
def teardown(self):
- if hasattr(self, 'mod_env'):
+ if hasattr(self, "mod_env"):
if os.path.isfile(self.archive):
os.remove(self.archive)
else:
@@ -219,27 +221,31 @@ class TestModuleLoader(object):
def test_log(self, prefix_loader):
log = self.compile_down(prefix_loader)
- assert 'Compiled "a/foo/test.html" as ' \
- 'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log
- assert 'Finished compiling templates' in log
- assert 'Could not compile "a/syntaxerror.html": ' \
- 'Encountered unknown tag \'endif\'' in log
+ assert (
+ 'Compiled "a/foo/test.html" as '
+ "tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a" in log
+ )
+ assert "Finished compiling templates" in log
+ assert (
+ 'Could not compile "a/syntaxerror.html": '
+ "Encountered unknown tag 'endif'" in log
+ )
def _test_common(self):
- tmpl1 = self.reg_env.get_template('a/test.html')
- tmpl2 = self.mod_env.get_template('a/test.html')
+ tmpl1 = self.reg_env.get_template("a/test.html")
+ tmpl2 = self.mod_env.get_template("a/test.html")
assert tmpl1.render() == tmpl2.render()
- tmpl1 = self.reg_env.get_template('b/justdict.html')
- tmpl2 = self.mod_env.get_template('b/justdict.html')
+ tmpl1 = self.reg_env.get_template("b/justdict.html")
+ tmpl2 = self.mod_env.get_template("b/justdict.html")
assert tmpl1.render() == tmpl2.render()
def test_deflated_zip_compile(self, prefix_loader):
- self.compile_down(prefix_loader, zip='deflated')
+ self.compile_down(prefix_loader, zip="deflated")
self._test_common()
def test_stored_zip_compile(self, prefix_loader):
- self.compile_down(prefix_loader, zip='stored')
+ self.compile_down(prefix_loader, zip="stored")
self._test_common()
def test_filesystem_compile(self, prefix_loader):
@@ -248,8 +254,8 @@ class TestModuleLoader(object):
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')
+ 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)
@@ -260,6 +266,7 @@ class TestModuleLoader(object):
try:
import gc
+
gc.collect()
except:
pass
@@ -268,65 +275,64 @@ class TestModuleLoader(object):
# 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')
+ 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')
+ 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'})
- ])
+ 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'
+ 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'})
- })
+ 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'
+ 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"
- @pytest.mark.skipif(PY2, reason='pathlib is not available in Python 2')
+ @pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
def test_path_as_pathlib(self, prefix_loader):
self.compile_down(prefix_loader)
mod_path = self.mod_env.loader.module.__path__[0]
import pathlib
+
mod_loader = loaders.ModuleLoader(pathlib.Path(mod_path))
self.mod_env = Environment(loader=mod_loader)
self._test_common()
- @pytest.mark.skipif(PY2, reason='pathlib is not available in Python 2')
+ @pytest.mark.skipif(PY2, reason="pathlib is not available in Python 2")
def test_supports_pathlib_in_list_of_paths(self, prefix_loader):
self.compile_down(prefix_loader)
mod_path = self.mod_env.loader.module.__path__[0]
import pathlib
- mod_loader = loaders.ModuleLoader([
- pathlib.Path(mod_path),
- '/tmp/templates'
- ])
+
+ mod_loader = loaders.ModuleLoader([pathlib.Path(mod_path), "/tmp/templates"])
self.mod_env = Environment(loader=mod_loader)
self._test_common()
diff --git a/tests/test_nativetypes.py b/tests/test_nativetypes.py
index 6761a5f..77d378d 100644
--- a/tests/test_nativetypes.py
+++ b/tests/test_nativetypes.py
@@ -13,17 +13,17 @@ def env():
def test_is_defined_native_return(env):
- t = env.from_string('{{ missing is defined }}')
+ t = env.from_string("{{ missing is defined }}")
assert not t.render()
def test_undefined_native_return(env):
- t = env.from_string('{{ missing }}')
+ t = env.from_string("{{ missing }}")
assert isinstance(t.render(), Undefined)
def test_adding_undefined_native_return(env):
- t = env.from_string('{{ 3 + missing }}')
+ t = env.from_string("{{ 3 + missing }}")
with pytest.raises(UndefinedError):
t.render()
@@ -31,30 +31,30 @@ def test_adding_undefined_native_return(env):
def test_cast_int(env):
t = env.from_string("{{ value|int }}")
- result = t.render(value='3')
+ result = t.render(value="3")
assert isinstance(result, int)
assert result == 3
def test_list_add(env):
t = env.from_string("{{ a + b }}")
- result = t.render(a=['a', 'b'], b=['c', 'd'])
+ result = t.render(a=["a", "b"], b=["c", "d"])
assert isinstance(result, list)
- assert result == ['a', 'b', 'c', 'd']
+ assert result == ["a", "b", "c", "d"]
def test_multi_expression_add(env):
t = env.from_string("{{ a }} + {{ b }}")
- result = t.render(a=['a', 'b'], b=['c', 'd'])
+ result = t.render(a=["a", "b"], b=["c", "d"])
assert not isinstance(result, list)
assert result == "['a', 'b'] + ['c', 'd']"
def test_loops(env):
t = env.from_string("{% for x in value %}{{ x }}{% endfor %}")
- result = t.render(value=['a', 'b', 'c', 'd'])
+ result = t.render(value=["a", "b", "c", "d"])
assert isinstance(result, text_type)
- assert result == 'abcd'
+ assert result == "abcd"
def test_loops_with_ints(env):
@@ -71,14 +71,17 @@ def test_loop_look_alike(env):
assert result == 1
-@pytest.mark.parametrize(("source", "expect"), (
- ("{{ value }}", True),
- ("{{ value }}", False),
- ("{{ 1 == 1 }}", True),
- ("{{ 2 + 2 == 5 }}", False),
- ("{{ None is none }}", True),
- ("{{ '' == None }}", False),
-))
+@pytest.mark.parametrize(
+ ("source", "expect"),
+ (
+ ("{{ value }}", True),
+ ("{{ value }}", False),
+ ("{{ 1 == 1 }}", True),
+ ("{{ 2 + 2 == 5 }}", False),
+ ("{{ None is none }}", True),
+ ("{{ '' == None }}", False),
+ ),
+)
def test_booleans(env, source, expect):
t = env.from_string(source)
result = t.render(value=expect)
@@ -115,7 +118,7 @@ def test_string_literal_var(env):
def test_string_top_level(env):
t = env.from_string("'Jinja'")
result = t.render()
- assert result == 'Jinja'
+ assert result == "Jinja"
def test_tuple_of_variable_strings(env):
diff --git a/tests/test_regression.py b/tests/test_regression.py
index df91785..47e0073 100644
--- a/tests/test_regression.py
+++ b/tests/test_regression.py
@@ -24,94 +24,109 @@ from jinja2._compat import text_type
@pytest.mark.regression
class TestCorner(object):
-
def test_assigned_scoping(self, env):
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- for item in (1, 2, 3, 4) -%}
[{{ item }}]
{%- endfor %}
{{- item -}}
- ''')
- assert t.render(item=42) == '[1][2][3][4]42'
+ """
+ )
+ assert t.render(item=42) == "[1][2][3][4]42"
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- for item in (1, 2, 3, 4) -%}
[{{ item }}]
{%- endfor %}
{%- set item = 42 %}
{{- item -}}
- ''')
- assert t.render() == '[1][2][3][4]42'
+ """
+ )
+ assert t.render() == "[1][2][3][4]42"
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- set item = 42 %}
{%- for item in (1, 2, 3, 4) -%}
[{{ item }}]
{%- endfor %}
{{- item -}}
- ''')
- assert t.render() == '[1][2][3][4]42'
+ """
+ )
+ assert t.render() == "[1][2][3][4]42"
def test_closure_scoping(self, env):
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- set wrapper = "<FOO>" %}
{%- for item in (1, 2, 3, 4) %}
{%- macro wrapper() %}[{{ item }}]{% endmacro %}
{{- wrapper() }}
{%- endfor %}
{{- wrapper -}}
- ''')
- assert t.render() == '[1][2][3][4]<FOO>'
+ """
+ )
+ assert t.render() == "[1][2][3][4]<FOO>"
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- for item in (1, 2, 3, 4) %}
{%- macro wrapper() %}[{{ item }}]{% endmacro %}
{{- wrapper() }}
{%- endfor %}
{%- set wrapper = "<FOO>" %}
{{- wrapper -}}
- ''')
- assert t.render() == '[1][2][3][4]<FOO>'
+ """
+ )
+ assert t.render() == "[1][2][3][4]<FOO>"
- t = env.from_string('''
+ t = env.from_string(
+ """
{%- for item in (1, 2, 3, 4) %}
{%- macro wrapper() %}[{{ item }}]{% endmacro %}
{{- wrapper() }}
{%- endfor %}
{{- wrapper -}}
- ''')
- assert t.render(wrapper=23) == '[1][2][3][4]23'
+ """
+ )
+ assert t.render(wrapper=23) == "[1][2][3][4]23"
@pytest.mark.regression
class TestBug(object):
-
def test_keyword_folding(self, env):
env = Environment()
- env.filters['testing'] = lambda value, some: value + some
- assert env.from_string("{{ 'test'|testing(some='stuff') }}") \
- .render() == 'teststuff'
+ env.filters["testing"] = lambda value, some: value + some
+ assert (
+ env.from_string("{{ 'test'|testing(some='stuff') }}").render()
+ == "teststuff"
+ )
def test_extends_output_bugs(self, env):
- env = Environment(loader=DictLoader({
- 'parent.html': '(({% block title %}{% endblock %}))'
- }))
+ env = Environment(
+ loader=DictLoader({"parent.html": "(({% block title %}{% endblock %}))"})
+ )
t = env.from_string(
'{% if expr %}{% extends "parent.html" %}{% endif %}'
- '[[{% block title %}title{% endblock %}]]'
- '{% for item in [1, 2, 3] %}({{ item }}){% endfor %}'
+ "[[{% block title %}title{% endblock %}]]"
+ "{% for item in [1, 2, 3] %}({{ item }}){% endfor %}"
)
- assert t.render(expr=False) == '[[title]](1)(2)(3)'
- assert t.render(expr=True) == '((title))'
+ assert t.render(expr=False) == "[[title]](1)(2)(3)"
+ assert t.render(expr=True) == "((title))"
def test_urlize_filter_escaping(self, env):
tmpl = env.from_string('{{ "http://www.example.org/<foo"|urlize }}')
- assert tmpl.render() == '<a href="http://www.example.org/&lt;foo" rel="noopener">'\
- 'http://www.example.org/&lt;foo</a>'
+ assert (
+ tmpl.render() == '<a href="http://www.example.org/&lt;foo" rel="noopener">'
+ "http://www.example.org/&lt;foo</a>"
+ )
def test_loop_call_loop(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{% macro test() %}
{{ caller() }}
@@ -125,29 +140,35 @@ class TestBug(object):
{% endcall %}
{% endfor %}
- ''')
+ """
+ )
- assert tmpl.render().split() \
- == [text_type(x) for x in range(1, 11)] * 5
+ 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')
+ env = Environment(line_statement_prefix="%")
+ pytest.raises(
+ TemplateSyntaxError,
+ env.from_string,
+ "% for item in seq {# missing #}\n...% endfor",
+ )
def test_old_macro_loop_scoping_bug(self, env):
- tmpl = env.from_string('{% for i in (1, 2) %}{{ i }}{% endfor %}'
- '{% macro i() %}3{% endmacro %}{{ i() }}')
- assert tmpl.render() == '123'
+ tmpl = env.from_string(
+ "{% for i in (1, 2) %}{{ i }}{% endfor %}"
+ "{% macro i() %}3{% endmacro %}{{ i() }}"
+ )
+ assert tmpl.render() == "123"
def test_partial_conditional_assignments(self, env):
- tmpl = env.from_string('{% if b %}{% set a = 42 %}{% endif %}{{ a }}')
- assert tmpl.render(a=23) == '23'
- assert tmpl.render(b=True) == '42'
+ tmpl = env.from_string("{% if b %}{% set a = 42 %}{% endif %}{{ a }}")
+ assert tmpl.render(a=23) == "23"
+ assert tmpl.render(b=True) == "42"
def test_stacked_locals_scoping_bug(self, env):
- env = Environment(line_statement_prefix='#')
- t = env.from_string('''\
+ env = Environment(line_statement_prefix="#")
+ t = env.from_string(
+ """\
# for j in [1, 2]:
# set x = 1
# for i in [1, 2]:
@@ -166,11 +187,13 @@ class TestBug(object):
# else
# print 'D'
# endif
- ''')
- assert t.render(a=0, b=False, c=42, d=42.0) == '1111C'
+ """
+ )
+ assert t.render(a=0, b=False, c=42, d=42.0) == "1111C"
def test_stacked_locals_scoping_bug_twoframe(self, env):
- t = Template('''
+ t = Template(
+ """
{% set x = 1 %}
{% for item in foo %}
{% if item == 1 %}
@@ -178,12 +201,14 @@ class TestBug(object):
{% endif %}
{% endfor %}
{{ x }}
- ''')
+ """
+ )
rv = t.render(foo=[1]).strip()
- assert rv == u'1'
+ assert rv == u"1"
def test_call_with_args(self, env):
- t = Template("""{% macro dump_users(users) -%}
+ t = Template(
+ """{% macro dump_users(users) -%}
<ul>
{%- for user in users -%}
<li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
@@ -198,32 +223,40 @@ class TestBug(object):
<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>'
+ {% 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 %}')
+ 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("""
+ tpl1 = Template(
+ """
{% for p in foo recursive%}
{{p.bar}}
{% for f in p.fields recursive%}
@@ -234,9 +267,11 @@ class TestBug(object):
{% endif %}
{% endfor %}
{% endfor %}
- """)
+ """
+ )
- tpl2 = Template("""
+ tpl2 = Template(
+ """
{% for p in foo%}
{{p.bar}}
{% for f in p.fields recursive%}
@@ -247,26 +282,27 @@ class TestBug(object):
{% endif %}
{% endfor %}
{% endfor %}
- """)
+ """
+ )
def test_else_loop_bug(self, env):
- t = Template('''
+ t = Template(
+ """
{% for x in y %}
{{ loop.index0 }}
{% else %}
{% for i in range(3) %}{{ i }}{% endfor %}
{% endfor %}
- ''')
- assert t.render(y=[]).strip() == '012'
+ """
+ )
+ assert t.render(y=[]).strip() == "012"
def test_correct_prefix_loader_name(self, env):
- env = Environment(loader=PrefixLoader({
- 'foo': DictLoader({})
- }))
+ env = Environment(loader=PrefixLoader({"foo": DictLoader({})}))
with pytest.raises(TemplateNotFound) as e:
- env.get_template('foo/bar.html')
+ env.get_template("foo/bar.html")
- assert e.value.name == 'foo/bar.html'
+ assert e.value.name == "foo/bar.html"
def test_contextfunction_callable_classes(self, env):
from jinja2.utils import contextfunction
@@ -274,74 +310,84 @@ class TestBug(object):
class CallableClass(object):
@contextfunction
def __call__(self, ctx):
- return ctx.resolve('hello')
+ return ctx.resolve("hello")
tpl = Template("""{{ callableclass() }}""")
- output = tpl.render(callableclass=CallableClass(), hello='TEST')
- expected = 'TEST'
+ output = tpl.render(callableclass=CallableClass(), hello="TEST")
+ expected = "TEST"
assert output == expected
- @pytest.mark.skipif(sys.version_info[0] > 2,
- reason='This only works on 2.x')
+ @pytest.mark.skipif(sys.version_info[0] > 2, reason="This only works on 2.x")
def test_old_style_attribute(self, env):
class Foo:
x = 42
- assert env.getitem(Foo(), 'x') == 42
+
+ assert env.getitem(Foo(), "x") == 42
def test_block_set_with_extends(self):
- env = Environment(loader=DictLoader({
- 'main': '{% block body %}[{{ x }}]{% endblock %}',
- }))
+ env = Environment(
+ loader=DictLoader({"main": "{% block body %}[{{ x }}]{% endblock %}",})
+ )
t = env.from_string('{% extends "main" %}{% set x %}42{% endset %}')
- assert t.render() == '[42]'
+ assert t.render() == "[42]"
def test_nested_for_else(self, env):
- tmpl = env.from_string('{% for x in y %}{{ loop.index0 }}{% else %}'
- '{% for i in range(3) %}{{ i }}{% endfor %}'
- '{% endfor %}')
- assert tmpl.render() == '012'
+ tmpl = env.from_string(
+ "{% for x in y %}{{ loop.index0 }}{% else %}"
+ "{% for i in range(3) %}{{ i }}{% endfor %}"
+ "{% endfor %}"
+ )
+ assert tmpl.render() == "012"
def test_macro_var_bug(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{% set i = 1 %}
{% macro test() %}
{% for i in range(0, 10) %}{{ i }}{% endfor %}
{% endmacro %}{{ test() }}
- ''')
- assert tmpl.render().strip() == '0123456789'
+ """
+ )
+ assert tmpl.render().strip() == "0123456789"
def test_macro_var_bug_advanced(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{% macro outer() %}
{% set i = 1 %}
{% macro test() %}
{% for i in range(0, 10) %}{{ i }}{% endfor %}
{% endmacro %}{{ test() }}
{% endmacro %}{{ outer() }}
- ''')
- assert tmpl.render().strip() == '0123456789'
+ """
+ )
+ assert tmpl.render().strip() == "0123456789"
def test_callable_defaults(self):
env = Environment()
- env.globals['get_int'] = lambda: 42
- t = env.from_string('''
+ env.globals["get_int"] = lambda: 42
+ t = env.from_string(
+ """
{% macro test(a, b, c=get_int()) -%}
{{ a + b + c }}
{%- endmacro %}
{{ test(1, 2) }}|{{ test(1, 2, 3) }}
- ''')
- assert t.render().strip() == '45|6'
+ """
+ )
+ assert t.render().strip() == "45|6"
def test_macro_escaping(self):
env = Environment(
- autoescape=lambda x: False, extensions=['jinja2.ext.autoescape'])
+ autoescape=lambda x: False, extensions=["jinja2.ext.autoescape"]
+ )
template = "{% macro m() %}<html>{% endmacro %}"
template += "{% autoescape true %}{{ m() }}{% endautoescape %}"
assert env.from_string(template).render()
def test_macro_scoping(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{% set n=[1,2,3,4,5] %}
{% for n in [[1,2,3], [3,4,5], [5,6,7]] %}
@@ -353,47 +399,56 @@ class TestBug(object):
{{ x(n) }}
{% endfor %}
- ''')
- assert list(map(int, tmpl.render().split())) == \
- [3, 2, 1, 5, 4, 3, 7, 6, 5]
+ """
+ )
+ assert list(map(int, tmpl.render().split())) == [3, 2, 1, 5, 4, 3, 7, 6, 5]
def test_scopes_and_blocks(self):
- env = Environment(loader=DictLoader({
- 'a.html': '''
+ env = Environment(
+ loader=DictLoader(
+ {
+ "a.html": """
{%- set foo = 'bar' -%}
{% include 'x.html' -%}
- ''',
- 'b.html': '''
+ """,
+ "b.html": """
{%- set foo = 'bar' -%}
{% block test %}{% include 'x.html' %}{% endblock -%}
- ''',
- 'c.html': '''
+ """,
+ "c.html": """
{%- set foo = 'bar' -%}
{% block test %}{% set foo = foo
%}{% include 'x.html' %}{% endblock -%}
- ''',
- 'x.html': '''{{ foo }}|{{ test }}'''
- }))
+ """,
+ "x.html": """{{ foo }}|{{ test }}""",
+ }
+ )
+ )
- a = env.get_template('a.html')
- b = env.get_template('b.html')
- c = env.get_template('c.html')
+ a = env.get_template("a.html")
+ b = env.get_template("b.html")
+ c = env.get_template("c.html")
- assert a.render(test='x').strip() == 'bar|x'
- assert b.render(test='x').strip() == 'bar|x'
- assert c.render(test='x').strip() == 'bar|x'
+ assert a.render(test="x").strip() == "bar|x"
+ assert b.render(test="x").strip() == "bar|x"
+ assert c.render(test="x").strip() == "bar|x"
def test_scopes_and_include(self):
- env = Environment(loader=DictLoader({
- 'include.html': '{{ var }}',
- 'base.html': '{% include "include.html" %}',
- 'child.html': '{% extends "base.html" %}{% set var = 42 %}',
- }))
- t = env.get_template('child.html')
- assert t.render() == '42'
+ env = Environment(
+ loader=DictLoader(
+ {
+ "include.html": "{{ var }}",
+ "base.html": '{% include "include.html" %}',
+ "child.html": '{% extends "base.html" %}{% set var = 42 %}',
+ }
+ )
+ )
+ t = env.get_template("child.html")
+ assert t.render() == "42"
def test_caller_scoping(self, env):
- t = env.from_string('''
+ t = env.from_string(
+ """
{% macro detail(icon, value) -%}
{% if value -%}
<p><span class="fa fa-fw fa-{{ icon }}"></span>
@@ -411,54 +466,67 @@ class TestBug(object):
<a href="{{ href }}">{{ value }}</a>
{%- endcall %}
{%- endmacro %}
- ''')
+ """
+ )
- assert t.module.link_detail('circle', 'Index', '/') == (
- '<p><span class="fa fa-fw fa-circle">'
- '</span><a href="/">Index</a></p>')
+ assert t.module.link_detail("circle", "Index", "/") == (
+ '<p><span class="fa fa-fw fa-circle"></span><a href="/">Index</a></p>'
+ )
def test_variable_reuse(self, env):
- t = env.from_string('{% for x in x.y %}{{ x }}{% endfor %}')
- assert t.render(x={'y': [0, 1, 2]}) == '012'
+ t = env.from_string("{% for x in x.y %}{{ x }}{% endfor %}")
+ assert t.render(x={"y": [0, 1, 2]}) == "012"
- t = env.from_string('{% for x in x.y %}{{ loop.index0 }}|{{ x }}{% endfor %}')
- assert t.render(x={'y': [0, 1, 2]}) == '0|01|12|2'
+ t = env.from_string("{% for x in x.y %}{{ loop.index0 }}|{{ x }}{% endfor %}")
+ assert t.render(x={"y": [0, 1, 2]}) == "0|01|12|2"
- t = env.from_string('{% for x in x.y recursive %}{{ x }}{% endfor %}')
- assert t.render(x={'y': [0, 1, 2]}) == '012'
+ t = env.from_string("{% for x in x.y recursive %}{{ x }}{% endfor %}")
+ assert t.render(x={"y": [0, 1, 2]}) == "012"
def test_double_caller(self, env):
- t = env.from_string('{% macro x(caller=none) %}[{% if caller %}'
- '{{ caller() }}{% endif %}]{% endmacro %}'
- '{{ x() }}{% call x() %}aha!{% endcall %}')
- assert t.render() == '[][aha!]'
+ t = env.from_string(
+ "{% macro x(caller=none) %}[{% if caller %}"
+ "{{ caller() }}{% endif %}]{% endmacro %}"
+ "{{ x() }}{% call x() %}aha!{% endcall %}"
+ )
+ assert t.render() == "[][aha!]"
def test_double_caller_no_default(self, env):
with pytest.raises(TemplateAssertionError) as exc_info:
- env.from_string('{% macro x(caller) %}[{% if caller %}'
- '{{ caller() }}{% endif %}]{% endmacro %}')
- assert exc_info.match(r'"caller" argument must be omitted or '
- r'be given a default')
+ env.from_string(
+ "{% macro x(caller) %}[{% if caller %}"
+ "{{ caller() }}{% endif %}]{% endmacro %}"
+ )
+ assert exc_info.match(
+ r'"caller" argument must be omitted or ' r"be given a default"
+ )
- t = env.from_string('{% macro x(caller=none) %}[{% if caller %}'
- '{{ caller() }}{% endif %}]{% endmacro %}')
+ t = env.from_string(
+ "{% macro x(caller=none) %}[{% if caller %}"
+ "{{ caller() }}{% endif %}]{% endmacro %}"
+ )
with pytest.raises(TypeError) as exc_info:
t.module.x(None, caller=lambda: 42)
- assert exc_info.match(r'\'x\' was invoked with two values for the '
- r'special caller argument')
+ assert exc_info.match(
+ r"\'x\' was invoked with two values for the " r"special caller argument"
+ )
def test_macro_blocks(self, env):
- t = env.from_string('{% macro x() %}{% block foo %}x{% '
- 'endblock %}{% endmacro %}{{ x() }}')
- assert t.render() == 'x'
+ t = env.from_string(
+ "{% macro x() %}{% block foo %}x{% endblock %}{% endmacro %}{{ x() }}"
+ )
+ assert t.render() == "x"
def test_scoped_block(self, env):
- t = env.from_string('{% set x = 1 %}{% with x = 2 %}{% block y scoped %}'
- '{{ x }}{% endblock %}{% endwith %}')
- assert t.render() == '2'
+ t = env.from_string(
+ "{% set x = 1 %}{% with x = 2 %}{% block y scoped %}"
+ "{{ x }}{% endblock %}{% endwith %}"
+ )
+ assert t.render() == "2"
def test_recursive_loop_filter(self, env):
- t = env.from_string('''
+ t = env.from_string(
+ """
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{%- for page in [site.root] if page.url != this recursive %}
@@ -466,49 +534,65 @@ class TestBug(object):
{{- loop(page.children) }}
{%- endfor %}
</urlset>
- ''')
- sm =t.render(this='/foo', site={'root': {
- 'url': '/',
- 'children': [
- {'url': '/foo'},
- {'url': '/bar'},
- ]
- }})
+ """
+ )
+ sm = t.render(
+ this="/foo",
+ site={
+ "root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"},]}
+ },
+ )
lines = [x.strip() for x in sm.splitlines() if x.strip()]
assert lines == [
'<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
- '<url><loc>/</loc></url>',
- '<url><loc>/bar</loc></url>',
- '</urlset>',
+ "<url><loc>/</loc></url>",
+ "<url><loc>/bar</loc></url>",
+ "</urlset>",
]
def test_empty_if(self, env):
- t = env.from_string('{% if foo %}{% else %}42{% endif %}')
- assert t.render(foo=False) == '42'
+ t = env.from_string("{% if foo %}{% else %}42{% endif %}")
+ assert t.render(foo=False) == "42"
def test_subproperty_if(self, env):
- t = env.from_string('{% if object1.subproperty1 is eq object2.subproperty2 %}42{% endif %}')
- assert t.render(object1={'subproperty1': 'value'}, object2={'subproperty2': 'value'}) == '42'
+ t = env.from_string(
+ "{% if object1.subproperty1 is eq object2.subproperty2 %}42{% endif %}"
+ )
+ assert (
+ t.render(
+ object1={"subproperty1": "value"}, object2={"subproperty2": "value"}
+ )
+ == "42"
+ )
def test_set_and_include(self):
- env = Environment(loader=DictLoader({
- 'inc': 'bar',
- 'main': '{% set foo = "foo" %}{{ foo }}{% include "inc" %}'
- }))
- assert env.get_template('main').render() == 'foobar'
+ env = Environment(
+ loader=DictLoader(
+ {
+ "inc": "bar",
+ "main": '{% set foo = "foo" %}{{ foo }}{% include "inc" %}',
+ }
+ )
+ )
+ assert env.get_template("main").render() == "foobar"
def test_loop_include(self):
- env = Environment(loader=DictLoader({
- 'inc': '{{ item }}',
- 'main': '{% for item in [1, 2, 3] %}{% include "inc" %}{% endfor %}',
- }))
- assert env.get_template('main').render() == '123'
+ env = Environment(
+ loader=DictLoader(
+ {
+ "inc": "{{ item }}",
+ "main": '{% for item in [1, 2, 3] %}{% include "inc" %}{% endfor %}',
+ }
+ )
+ )
+ assert env.get_template("main").render() == "123"
def test_grouper_repr(self):
from jinja2.filters import _GroupTuple
- t = _GroupTuple('foo', [1, 2])
- assert t.grouper == 'foo'
+
+ t = _GroupTuple("foo", [1, 2])
+ assert t.grouper == "foo"
assert t.list == [1, 2]
assert repr(t) == "('foo', [1, 2])"
assert str(t) == "('foo', [1, 2])"
@@ -522,33 +606,35 @@ class TestBug(object):
class MyEnvironment(Environment):
context_class = MyContext
- loader = DictLoader({'base': '{{ foobar }}',
- 'test': '{% extends "base" %}'})
+ loader = DictLoader({"base": "{{ foobar }}", "test": '{% extends "base" %}'})
env = MyEnvironment(loader=loader)
- assert env.get_template('test').render(foobar='test') == 'test'
+ assert env.get_template("test").render(foobar="test") == "test"
def test_legacy_custom_context(self, env):
from jinja2.runtime import Context, Undefined, missing
class MyContext(Context):
def resolve(self, name):
- if name == 'foo':
+ if name == "foo":
return 42
return super(MyContext, self).resolve(name)
- x = MyContext(env, parent={'bar': 23}, name='foo', blocks={})
+ x = MyContext(env, parent={"bar": 23}, name="foo", blocks={})
assert x._legacy_resolve_mode
- assert x.resolve_or_missing('foo') == 42
- assert x.resolve_or_missing('bar') == 23
- assert x.resolve_or_missing('baz') is missing
+ assert x.resolve_or_missing("foo") == 42
+ assert x.resolve_or_missing("bar") == 23
+ assert x.resolve_or_missing("baz") is missing
def test_recursive_loop_bug(self, env):
- tmpl = env.from_string('''
+ tmpl = env.from_string(
+ """
{%- for value in values recursive %}1{% else %}0{% endfor -%}
- ''')
- assert tmpl.render(values=[]) == '0'
+ """
+ )
+ assert tmpl.render(values=[]) == "0"
def test_markup_and_chainable_undefined(self):
from jinja2 import Markup
from jinja2.runtime import ChainableUndefined
- assert str(Markup(ChainableUndefined())) == ''
+
+ assert str(Markup(ChainableUndefined())) == ""
diff --git a/tests/test_runtime.py b/tests/test_runtime.py
index 1afcb3f..307f055 100644
--- a/tests/test_runtime.py
+++ b/tests/test_runtime.py
@@ -52,9 +52,9 @@ def test_loopcontext2():
def test_iterator_not_advanced_early():
t = Template("{% for _, g in gs %}{{ loop.index }} {{ g|list }}\n{% endfor %}")
- out = t.render(gs=itertools.groupby(
- [(1, "a"), (1, "b"), (2, "c"), (3, "d")], lambda x: x[0]
- ))
+ out = t.render(
+ gs=itertools.groupby([(1, "a"), (1, "b"), (2, "c"), (3, "d")], lambda x: x[0])
+ )
# groupby groups depend on the current position of the iterator. If
# it was advanced early, the lists would appear empty.
assert out == "1 [(1, 'a'), (1, 'b')]\n2 [(2, 'c')]\n3 [(3, 'd')]\n"
diff --git a/tests/test_security.py b/tests/test_security.py
index b37b655..39045fd 100644
--- a/tests/test_security.py
+++ b/tests/test_security.py
@@ -24,7 +24,6 @@ from jinja2.sandbox import unsafe
class PrivateStuff(object):
-
def bar(self):
return 23
@@ -33,7 +32,7 @@ class PrivateStuff(object):
return 42
def __repr__(self):
- return 'PrivateStuff'
+ return "PrivateStuff"
class PublicStuff(object):
@@ -41,57 +40,64 @@ class PublicStuff(object):
_foo = lambda self: 42
def __repr__(self):
- return 'PublicStuff'
+ return "PublicStuff"
@pytest.mark.sandbox
class TestSandbox(object):
-
def test_unsafe(self, env):
env = SandboxedEnvironment()
- pytest.raises(SecurityError, env.from_string("{{ foo.foo() }}").render,
- foo=PrivateStuff())
- assert env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()) == '23'
-
- pytest.raises(SecurityError,
- env.from_string("{{ foo._foo() }}").render,
- foo=PublicStuff())
- assert env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()) == '23'
- assert env.from_string("{{ foo.__class__ }}").render(foo=42) == ''
- assert env.from_string("{{ foo.func_code }}").render(foo=lambda:None) == ''
+ pytest.raises(
+ SecurityError, env.from_string("{{ foo.foo() }}").render, foo=PrivateStuff()
+ )
+ assert env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()) == "23"
+
+ pytest.raises(
+ SecurityError, env.from_string("{{ foo._foo() }}").render, foo=PublicStuff()
+ )
+ assert env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()) == "23"
+ assert env.from_string("{{ foo.__class__ }}").render(foo=42) == ""
+ assert env.from_string("{{ foo.func_code }}").render(foo=lambda: None) == ""
# security error comes from __class__ already.
- pytest.raises(SecurityError, env.from_string(
- "{{ foo.__class__.__subclasses__() }}").render, foo=42)
+ pytest.raises(
+ SecurityError,
+ env.from_string("{{ foo.__class__.__subclasses__() }}").render,
+ foo=42,
+ )
def test_immutable_environment(self, env):
env = ImmutableSandboxedEnvironment()
- pytest.raises(SecurityError, env.from_string(
- '{{ [].append(23) }}').render)
- pytest.raises(SecurityError, env.from_string(
- '{{ {1:2}.clear() }}').render)
+ pytest.raises(SecurityError, env.from_string("{{ [].append(23) }}").render)
+ pytest.raises(SecurityError, env.from_string("{{ {1:2}.clear() }}").render)
def test_restricted(self, env):
env = SandboxedEnvironment()
- pytest.raises(TemplateSyntaxError, env.from_string,
- "{% for item.attribute in seq %}...{% endfor %}")
- pytest.raises(TemplateSyntaxError, env.from_string,
- "{% for foo, bar.baz in seq %}...{% endfor %}")
+ pytest.raises(
+ TemplateSyntaxError,
+ env.from_string,
+ "{% for item.attribute in seq %}...{% endfor %}",
+ )
+ pytest.raises(
+ TemplateSyntaxError,
+ env.from_string,
+ "{% for foo, bar.baz in seq %}...{% endfor %}",
+ )
def test_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>')
+ 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>&lt;bad user&gt;</em>'
- assert Markup('<em>%(username)s</em>') % {
- 'username': '<bad user>'
- } == '<em>&lt;bad user&gt;</em>'
+ assert Markup("<em>%s</em>") % "<bad user>" == "<em>&lt;bad user&gt;</em>"
+ assert (
+ Markup("<em>%(username)s</em>") % {"username": "<bad user>"}
+ == "<em>&lt;bad user&gt;</em>"
+ )
# an escaped object is markup too
- assert type(Markup('foo') + 'bar') is Markup
+ assert type(Markup("foo") + "bar") is Markup
# and it implements __html__ by returning itself
x = Markup("foo")
@@ -100,33 +106,38 @@ class TestSandbox(object):
# it also knows how to treat __html__ objects
class Foo(object):
def __html__(self):
- return '<em>awesome</em>'
+ 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>'
+ return "awesome"
+
+ assert Markup(Foo()) == "<em>awesome</em>"
+ assert (
+ Markup("<strong>%s</strong>") % Foo() == "<strong><em>awesome</em></strong>"
+ )
# escaping and unescaping
- assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
+ assert escape("\"<>&'") == "&#34;&lt;&gt;&amp;&#39;"
assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
assert Markup("&lt;test&gt;").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 &lt;blink&gt;foo&lt;/blink&gt;!</p>'
+ t = env.from_string(
+ "{% macro say_hello(name) %}"
+ "<p>Hello {{ name }}!</p>{% endmacro %}"
+ '{{ say_hello("<blink>foo</blink>") }}'
+ )
+ escaped_out = "<p>Hello &lt;blink&gt;foo&lt;/blink&gt;!</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(
- EvalContext(env), '<blink>foo</blink>')) == escaped_out
- assert escape(t.module.say_hello(
- '<blink>foo</blink>')) == escaped_out
+ assert t.module.say_hello("<blink>foo</blink>") == escaped_out
+ assert (
+ escape(t.module.say_hello(EvalContext(env), "<blink>foo</blink>"))
+ == escaped_out
+ )
+ assert escape(t.module.say_hello("<blink>foo</blink>")) == escaped_out
def test_attr_filter(self, env):
env = SandboxedEnvironment()
@@ -135,69 +146,74 @@ class TestSandbox(object):
def test_binary_operator_intercepting(self, env):
def disable_op(left, right):
- raise TemplateRuntimeError('that operator so does not work')
- for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'):
+ raise TemplateRuntimeError("that operator so does not work")
+
+ for expr, ctx, rv in ("1 + 2", {}, "3"), ("a + 2", {"a": 2}, "4"):
env = SandboxedEnvironment()
- env.binop_table['+'] = disable_op
- t = env.from_string('{{ %s }}' % expr)
+ 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)
+ env.intercepted_binops = frozenset(["+"])
+ t = env.from_string("{{ %s }}" % expr)
with pytest.raises(TemplateRuntimeError):
t.render(ctx)
def test_unary_operator_intercepting(self, env):
def disable_op(arg):
- raise TemplateRuntimeError('that operator so does not work')
- for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'):
+ raise TemplateRuntimeError("that operator so does not work")
+
+ for expr, ctx, rv in ("-1", {}, "-1"), ("-a", {"a": 2}, "-2"):
env = SandboxedEnvironment()
- env.unop_table['-'] = disable_op
- t = env.from_string('{{ %s }}' % expr)
+ 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)
+ env.intercepted_unops = frozenset(["-"])
+ t = env.from_string("{{ %s }}" % expr)
with pytest.raises(TemplateRuntimeError):
t.render(ctx)
@pytest.mark.sandbox
class TestStringFormat(object):
-
def test_basic_format_safety(self):
env = SandboxedEnvironment()
t = env.from_string('{{ "a{0.__class__}b".format(42) }}')
- assert t.render() == 'ab'
+ assert t.render() == "ab"
def test_basic_format_all_okay(self):
env = SandboxedEnvironment()
t = env.from_string('{{ "a{0.foo}b".format({"foo": 42}) }}')
- assert t.render() == 'a42b'
+ assert t.render() == "a42b"
def test_safe_format_safety(self):
env = SandboxedEnvironment()
t = env.from_string('{{ ("a{0.__class__}b{1}"|safe).format(42, "<foo>") }}')
- assert t.render() == 'ab&lt;foo&gt;'
+ assert t.render() == "ab&lt;foo&gt;"
def test_safe_format_all_okay(self):
env = SandboxedEnvironment()
t = env.from_string('{{ ("a{0.foo}b{1}"|safe).format({"foo": 42}, "<foo>") }}')
- assert t.render() == 'a42b&lt;foo&gt;'
+ assert t.render() == "a42b&lt;foo&gt;"
@pytest.mark.sandbox
-@pytest.mark.skipif(not hasattr(str, 'format_map'), reason='requires str.format_map method')
+@pytest.mark.skipif(
+ not hasattr(str, "format_map"), reason="requires str.format_map method"
+)
class TestStringFormatMap(object):
def test_basic_format_safety(self):
env = SandboxedEnvironment()
t = env.from_string('{{ "a{x.__class__}b".format_map({"x":42}) }}')
- assert t.render() == 'ab'
+ assert t.render() == "ab"
def test_basic_format_all_okay(self):
env = SandboxedEnvironment()
t = env.from_string('{{ "a{x.foo}b".format_map({"x":{"foo": 42}}) }}')
- assert t.render() == 'a42b'
+ assert t.render() == "a42b"
def test_safe_format_all_okay(self):
env = SandboxedEnvironment()
- t = env.from_string('{{ ("a{x.foo}b{y}"|safe).format_map({"x":{"foo": 42}, "y":"<foo>"}) }}')
- assert t.render() == 'a42b&lt;foo&gt;'
+ t = env.from_string(
+ '{{ ("a{x.foo}b{y}"|safe).format_map({"x":{"foo": 42}, "y":"<foo>"}) }}'
+ )
+ assert t.render() == "a42b&lt;foo&gt;"
diff --git a/tests/test_tests.py b/tests/test_tests.py
index dd83efa..27871ca 100644
--- a/tests/test_tests.py
+++ b/tests/test_tests.py
@@ -13,208 +13,208 @@ import pytest
from jinja2 import Environment
from jinja2 import Markup
+
class MyDict(dict):
pass
+
@pytest.mark.test_tests
class TestTestsCase(object):
-
def test_defined(self, env):
- tmpl = env.from_string('{{ missing is defined }}|'
- '{{ true is defined }}')
- assert tmpl.render() == 'False|True'
+ tmpl = env.from_string("{{ missing is defined }}|{{ true is defined }}")
+ assert tmpl.render() == "False|True"
def test_even(self, env):
- tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''')
- assert tmpl.render() == 'False|True'
+ tmpl = env.from_string("""{{ 1 is even }}|{{ 2 is even }}""")
+ assert tmpl.render() == "False|True"
def test_odd(self, env):
- tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''')
- assert tmpl.render() == 'True|False'
+ tmpl = env.from_string("""{{ 1 is odd }}|{{ 2 is odd }}""")
+ assert tmpl.render() == "True|False"
def test_lower(self, env):
- tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''')
- assert tmpl.render() == 'True|False'
+ tmpl = env.from_string("""{{ "foo" is lower }}|{{ "FOO" is lower }}""")
+ assert tmpl.render() == "True|False"
# Test type checks
- @pytest.mark.parametrize('op,expect', (
- ('none is none', True),
- ('false is none', False),
- ('true is none', False),
- ('42 is none', False),
-
- ('none is true', False),
- ('false is true', False),
- ('true is true', True),
- ('0 is true', False),
- ('1 is true', False),
- ('42 is true', False),
-
- ('none is false', False),
- ('false is false', True),
- ('true is false', False),
- ('0 is false', False),
- ('1 is false', False),
- ('42 is false', False),
-
- ('none is boolean', False),
- ('false is boolean', True),
- ('true is boolean', True),
- ('0 is boolean', False),
- ('1 is boolean', False),
- ('42 is boolean', False),
- ('0.0 is boolean', False),
- ('1.0 is boolean', False),
- ('3.14159 is boolean', False),
-
- ('none is integer', False),
- ('false is integer', False),
- ('true is integer', False),
- ('42 is integer', True),
- ('3.14159 is integer', False),
- ('(10 ** 100) is integer', True),
-
- ('none is float', False),
- ('false is float', False),
- ('true is float', False),
- ('42 is float', False),
- ('4.2 is float', True),
- ('(10 ** 100) is float', False),
-
- ('none is number', False),
- ('false is number', True),
- ('true is number', True),
- ('42 is number', True),
- ('3.14159 is number', True),
- ('complex is number', True),
- ('(10 ** 100) is number', True),
-
- ('none is string', False),
- ('false is string', False),
- ('true is string', False),
- ('42 is string', False),
- ('"foo" is string', True),
-
- ('none is sequence', False),
- ('false is sequence', False),
- ('42 is sequence', False),
- ('"foo" is sequence', True),
- ('[] is sequence', True),
- ('[1, 2, 3] is sequence', True),
- ('{} is sequence', True),
-
- ('none is mapping', False),
- ('false is mapping', False),
- ('42 is mapping', False),
- ('"foo" is mapping', False),
- ('[] is mapping', False),
- ('{} is mapping', True),
- ('mydict is mapping', True),
-
- ('none is iterable', False),
- ('false is iterable', False),
- ('42 is iterable', False),
- ('"foo" is iterable', True),
- ('[] is iterable', True),
- ('{} is iterable', True),
- ('range(5) is iterable', True),
-
- ('none is callable', False),
- ('false is callable', False),
- ('42 is callable', False),
- ('"foo" is callable', False),
- ('[] is callable', False),
- ('{} is callable', False),
- ('range is callable', True),
- ))
+ @pytest.mark.parametrize(
+ "op,expect",
+ (
+ ("none is none", True),
+ ("false is none", False),
+ ("true is none", False),
+ ("42 is none", False),
+ ("none is true", False),
+ ("false is true", False),
+ ("true is true", True),
+ ("0 is true", False),
+ ("1 is true", False),
+ ("42 is true", False),
+ ("none is false", False),
+ ("false is false", True),
+ ("true is false", False),
+ ("0 is false", False),
+ ("1 is false", False),
+ ("42 is false", False),
+ ("none is boolean", False),
+ ("false is boolean", True),
+ ("true is boolean", True),
+ ("0 is boolean", False),
+ ("1 is boolean", False),
+ ("42 is boolean", False),
+ ("0.0 is boolean", False),
+ ("1.0 is boolean", False),
+ ("3.14159 is boolean", False),
+ ("none is integer", False),
+ ("false is integer", False),
+ ("true is integer", False),
+ ("42 is integer", True),
+ ("3.14159 is integer", False),
+ ("(10 ** 100) is integer", True),
+ ("none is float", False),
+ ("false is float", False),
+ ("true is float", False),
+ ("42 is float", False),
+ ("4.2 is float", True),
+ ("(10 ** 100) is float", False),
+ ("none is number", False),
+ ("false is number", True),
+ ("true is number", True),
+ ("42 is number", True),
+ ("3.14159 is number", True),
+ ("complex is number", True),
+ ("(10 ** 100) is number", True),
+ ("none is string", False),
+ ("false is string", False),
+ ("true is string", False),
+ ("42 is string", False),
+ ('"foo" is string', True),
+ ("none is sequence", False),
+ ("false is sequence", False),
+ ("42 is sequence", False),
+ ('"foo" is sequence', True),
+ ("[] is sequence", True),
+ ("[1, 2, 3] is sequence", True),
+ ("{} is sequence", True),
+ ("none is mapping", False),
+ ("false is mapping", False),
+ ("42 is mapping", False),
+ ('"foo" is mapping', False),
+ ("[] is mapping", False),
+ ("{} is mapping", True),
+ ("mydict is mapping", True),
+ ("none is iterable", False),
+ ("false is iterable", False),
+ ("42 is iterable", False),
+ ('"foo" is iterable', True),
+ ("[] is iterable", True),
+ ("{} is iterable", True),
+ ("range(5) is iterable", True),
+ ("none is callable", False),
+ ("false is callable", False),
+ ("42 is callable", False),
+ ('"foo" is callable', False),
+ ("[] is callable", False),
+ ("{} is callable", False),
+ ("range is callable", True),
+ ),
+ )
def test_types(self, env, op, expect):
- t = env.from_string('{{{{ {op} }}}}'.format(op=op))
+ t = env.from_string("{{{{ {op} }}}}".format(op=op))
assert t.render(mydict=MyDict(), complex=complex(1, 2)) == str(expect)
-
def test_upper(self, env):
tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}')
- assert tmpl.render() == 'True|False'
+ assert tmpl.render() == "True|False"
def test_equalto(self, env):
tmpl = env.from_string(
- '{{ foo is eq 12 }}|'
- '{{ foo is eq 0 }}|'
- '{{ foo is eq (3 * 4) }}|'
+ "{{ foo is eq 12 }}|"
+ "{{ foo is eq 0 }}|"
+ "{{ foo is eq (3 * 4) }}|"
'{{ bar is eq "baz" }}|'
'{{ bar is eq "zab" }}|'
'{{ bar is eq ("ba" + "z") }}|'
- '{{ bar is eq bar }}|'
- '{{ bar is eq foo }}'
+ "{{ bar is eq bar }}|"
+ "{{ bar is eq foo }}"
+ )
+ assert (
+ tmpl.render(foo=12, bar="baz")
+ == "True|False|True|True|False|True|True|False"
)
- assert tmpl.render(foo=12, bar="baz") \
- == 'True|False|True|True|False|True|True|False'
-
- @pytest.mark.parametrize('op,expect', (
- ('eq 2', True),
- ('eq 3', False),
- ('ne 3', True),
- ('ne 2', False),
- ('lt 3', True),
- ('lt 2', False),
- ('le 2', True),
- ('le 1', False),
- ('gt 1', True),
- ('gt 2', False),
- ('ge 2', True),
- ('ge 3', False),
- ))
+
+ @pytest.mark.parametrize(
+ "op,expect",
+ (
+ ("eq 2", True),
+ ("eq 3", False),
+ ("ne 3", True),
+ ("ne 2", False),
+ ("lt 3", True),
+ ("lt 2", False),
+ ("le 2", True),
+ ("le 1", False),
+ ("gt 1", True),
+ ("gt 2", False),
+ ("ge 2", True),
+ ("ge 3", False),
+ ),
+ )
def test_compare_aliases(self, env, op, expect):
- t = env.from_string('{{{{ 2 is {op} }}}}'.format(op=op))
+ t = env.from_string("{{{{ 2 is {op} }}}}".format(op=op))
assert t.render() == str(expect)
def test_sameas(self, env):
- tmpl = env.from_string('{{ foo is sameas false }}|'
- '{{ 0 is sameas false }}')
- assert tmpl.render(foo=False) == 'True|False'
+ tmpl = env.from_string("{{ foo is sameas false }}|{{ 0 is sameas false }}")
+ assert tmpl.render(foo=False) == "True|False"
def test_no_paren_for_arg1(self, env):
- tmpl = env.from_string('{{ foo is sameas none }}')
- assert tmpl.render(foo=None) == 'True'
+ tmpl = env.from_string("{{ foo is sameas none }}")
+ assert tmpl.render(foo=None) == "True"
def test_escaped(self, env):
env = Environment(autoescape=True)
- tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}')
- assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True'
+ tmpl = env.from_string("{{ x is escaped }}|{{ y is escaped }}")
+ assert tmpl.render(x="foo", y=Markup("foo")) == "False|True"
def test_greaterthan(self, env):
- tmpl = env.from_string('{{ 1 is greaterthan 0 }}|'
- '{{ 0 is greaterthan 1 }}')
- assert tmpl.render() == 'True|False'
+ tmpl = env.from_string("{{ 1 is greaterthan 0 }}|{{ 0 is greaterthan 1 }}")
+ assert tmpl.render() == "True|False"
def test_lessthan(self, env):
- tmpl = env.from_string('{{ 0 is lessthan 1 }}|'
- '{{ 1 is lessthan 0 }}')
- assert tmpl.render() == 'True|False'
+ tmpl = env.from_string("{{ 0 is lessthan 1 }}|{{ 1 is lessthan 0 }}")
+ assert tmpl.render() == "True|False"
def test_multiple_tests(self):
items = []
+
def matching(x, y):
items.append((x, y))
return False
+
env = Environment()
- env.tests['matching'] = matching
- tmpl = env.from_string("{{ 'us-west-1' is matching "
- "'(us-east-1|ap-northeast-1)' "
- "or 'stage' is matching '(dev|stage)' }}")
- assert tmpl.render() == 'False'
- assert items == [('us-west-1', '(us-east-1|ap-northeast-1)'),
- ('stage', '(dev|stage)')]
+ env.tests["matching"] = matching
+ tmpl = env.from_string(
+ "{{ 'us-west-1' is matching "
+ "'(us-east-1|ap-northeast-1)' "
+ "or 'stage' is matching '(dev|stage)' }}"
+ )
+ assert tmpl.render() == "False"
+ assert items == [
+ ("us-west-1", "(us-east-1|ap-northeast-1)"),
+ ("stage", "(dev|stage)"),
+ ]
def test_in(self, env):
- tmpl = env.from_string('{{ "o" is in "foo" }}|'
- '{{ "foo" is in "foo" }}|'
- '{{ "b" is in "foo" }}|'
- '{{ 1 is in ((1, 2)) }}|'
- '{{ 3 is in ((1, 2)) }}|'
- '{{ 1 is in [1, 2] }}|'
- '{{ 3 is in [1, 2] }}|'
- '{{ "foo" is in {"foo": 1}}}|'
- '{{ "baz" is in {"bar": 1}}}')
- assert tmpl.render() \
- == 'True|True|False|True|False|True|False|True|False'
+ tmpl = env.from_string(
+ '{{ "o" is in "foo" }}|'
+ '{{ "foo" is in "foo" }}|'
+ '{{ "b" is in "foo" }}|'
+ "{{ 1 is in ((1, 2)) }}|"
+ "{{ 3 is in ((1, 2)) }}|"
+ "{{ 1 is in [1, 2] }}|"
+ "{{ 3 is in [1, 2] }}|"
+ '{{ "foo" is in {"foo": 1}}}|'
+ '{{ "baz" is in {"bar": 1}}}'
+ )
+ assert tmpl.render() == "True|True|False|True|False|True|False|True|False"
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 15b6c55..8cecb1b 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -30,7 +30,6 @@ from jinja2.utils import urlize
@pytest.mark.utils
@pytest.mark.lrucache
class TestLRUCache(object):
-
def test_simple(self):
d = LRUCache(3)
d["a"] = 1
@@ -39,7 +38,7 @@ class TestLRUCache(object):
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
+ assert "a" in d and "c" in d and "d" in d and "b" not in d
def test_itervalue_deprecated(self):
cache = LRUCache(3)
@@ -77,13 +76,13 @@ class TestLRUCache(object):
@pytest.mark.parametrize("copy_func", [LRUCache.copy, shallow_copy])
def test_copy(self, copy_func):
cache = LRUCache(2)
- cache['a'] = 1
- cache['b'] = 2
+ cache["a"] = 1
+ cache["b"] = 2
copy = copy_func(cache)
assert copy._queue == cache._queue
- copy['c'] = 3
+ copy["c"] = 3
assert copy._queue != cache._queue
- assert 'a' not in copy and 'b' in copy and 'c' in copy
+ assert "a" not in copy and "b" in copy and "c" in copy
def test_clear(self):
d = LRUCache(3)
@@ -91,7 +90,7 @@ class TestLRUCache(object):
d["b"] = 2
d["c"] = 3
d.clear()
- assert d.__getstate__() == {'capacity': 3, '_mapping': {}, '_queue': deque([])}
+ assert d.__getstate__() == {"capacity": 3, "_mapping": {}, "_queue": deque([])}
def test_repr(self):
d = LRUCache(3)
@@ -107,18 +106,18 @@ class TestLRUCache(object):
d["a"] = 1
d["b"] = 2
d["c"] = 3
- assert d.items() == list(d.iteritems()) == [('c', 3), ('b', 2), ('a', 1)]
- assert d.keys() == list(d.iterkeys()) == ['c', 'b', 'a']
+ assert d.items() == list(d.iteritems()) == [("c", 3), ("b", 2), ("a", 1)]
+ assert d.keys() == list(d.iterkeys()) == ["c", "b", "a"]
assert d.values() == list(d.itervalues()) == [3, 2, 1]
- assert list(reversed(d)) == ['a', 'b', 'c']
+ assert list(reversed(d)) == ["a", "b", "c"]
# Change the cache a little
d["b"]
d["a"] = 4
- assert d.items() == list(d.iteritems()) == [('a', 4), ('b', 2), ('c', 3)]
- assert d.keys() == list(d.iterkeys()) == ['a', 'b', 'c']
+ assert d.items() == list(d.iteritems()) == [("a", 4), ("b", 2), ("c", 3)]
+ assert d.keys() == list(d.iterkeys()) == ["a", "b", "c"]
assert d.values() == list(d.itervalues()) == [4, 2, 3]
- assert list(reversed(d)) == ['c', 'b', 'a']
+ assert list(reversed(d)) == ["c", "b", "a"]
def test_setdefault(self):
d = LRUCache(3)
@@ -133,31 +132,31 @@ class TestLRUCache(object):
@pytest.mark.utils
@pytest.mark.helpers
class TestHelpers(object):
-
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'
+
+ 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"
def test_autoescape_select(self):
func = select_autoescape(
- enabled_extensions=('html', '.htm'),
- disabled_extensions=('txt',),
- default_for_string='STRING',
- default='NONE',
+ enabled_extensions=("html", ".htm"),
+ disabled_extensions=("txt",),
+ default_for_string="STRING",
+ default="NONE",
)
- assert func(None) == 'STRING'
- assert func('unknown.foo') == 'NONE'
- assert func('foo.html') == True
- assert func('foo.htm') == True
- assert func('foo.txt') == False
- assert func('FOO.HTML') == True
- assert func('FOO.TXT') == False
+ assert func(None) == "STRING"
+ assert func("unknown.foo") == "NONE"
+ assert func("foo.html") == True
+ assert func("foo.htm") == True
+ assert func("foo.txt") == False
+ assert func("FOO.HTML") == True
+ assert func("FOO.TXT") == False
@pytest.mark.utils
@@ -166,9 +165,11 @@ class TestEscapeUrlizeTarget(object):
def test_escape_urlize_target(self):
url = "http://example.org"
target = "<script>"
- assert urlize(url, target=target) == ('<a href="http://example.org"'
- ' target="&lt;script&gt;">'
- 'http://example.org</a>')
+ assert urlize(url, target=target) == (
+ '<a href="http://example.org"'
+ ' target="&lt;script&gt;">'
+ "http://example.org</a>"
+ )
@pytest.mark.utils
@@ -184,28 +185,29 @@ class TestLoremIpsum(object):
def test_lorem_ipsum_n(self):
"""Test that the n (number of lines) works as expected."""
- assert generate_lorem_ipsum(n=0, html=False) == u''
+ assert generate_lorem_ipsum(n=0, html=False) == u""
for n in range_type(1, 50):
- assert generate_lorem_ipsum(n=n, html=False).count('\n') == (n - 1) * 2
+ assert generate_lorem_ipsum(n=n, html=False).count("\n") == (n - 1) * 2
def test_lorem_ipsum_min(self):
"""Test that at least min words are in the output of each line"""
for _ in range_type(5):
- m = random.randrange(20, 99)
- for _ in range_type(10):
- assert generate_lorem_ipsum(n=1, min=m, html=False).count(' ') >= m - 1
+ m = random.randrange(20, 99)
+ for _ in range_type(10):
+ assert generate_lorem_ipsum(n=1, min=m, html=False).count(" ") >= m - 1
def test_lorem_ipsum_max(self):
"""Test that at least max words are in the output of each line"""
for _ in range_type(5):
- m = random.randrange(21, 100)
- for _ in range_type(10):
- assert generate_lorem_ipsum(n=1, max=m, html=False).count(' ') < m - 1
+ m = random.randrange(21, 100)
+ for _ in range_type(10):
+ assert generate_lorem_ipsum(n=1, max=m, html=False).count(" ") < m - 1
def test_missing():
"""Test the repr of missing."""
- assert repr(missing) == u'missing'
+ assert repr(missing) == u"missing"
+
def test_consume():
"""Test that consume consumes an iterator."""