env = Environment(
loader=DictLoader(
{
- "a": """[A[{% block body %}{% endblock %}]]""",
- "b": """{% extends 'a' %}{% block body %}[B]{% endblock %}""",
- "c": """{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}""",
+ "a": "[A[{% block body %}{% endblock %}]]",
+ "b": "{% extends 'a' %}{% block body %}[B]{% endblock %}",
+ "c": "{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}",
}
)
)
env.from_string(
"""\
{% trans %}Hello {{ user }}!{% endtrans %}
-{% trans count=users|count %}{{ count }} user{% pluralize %}{{ count }} users{% endtrans %}
+{% trans count=users|count -%}
+{{ count }} user{% pluralize %}{{ count }} users
+{% endtrans %}
"""
).render(user="someone", users=[1, 2, 3])
)
from timeit import Timer
from jinja2 import Environment as JinjaEnvironment
+from jinja2._compat import text_type
context = {
"page_title": "mitsuhiko's benchmark",
<h1>${page_title|h}</h1>
</div>
<ul class="navigation">
- % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
+ % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
+('products.html', 'Products')]:
<li><a href="${href|h}">${caption|h}</a></li>
% endfor
</ul>
<h1>$cgi.escape($page_title)</h1>
</div>
<ul class="navigation">
- #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
+ #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
+('products.html', 'Products')]:
<li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
#end for
</ul>
)
def test_cheetah():
- unicode(cheetah_template)
+ text_type(cheetah_template)
try:
<h1>${page_title}</h1>
</div>
<ul class="navigation">
-<?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: ?>
+<?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
+('products.html', 'Products')]: ?>
<li><a href="${href}">${caption}</a></li>
<?py #end ?>
</ul>
)
def test_tenjin():
- from tenjin.helpers import escape, to_str
-
tenjin_template.render(context, locals())
<h1>$cgi.escape($page_title)</h1>
</div>
<ul class="navigation">
- #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]
+ #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
+('products.html', 'Products')]
<li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
#end for
</ul>
<h1 tal:content="page_title">Page Title</h1>
</div>
<ul class="navigation">
- <li tal:repeat="item sections"><a tal:attributes="href item[0]" tal:content="item[1]">caption</a></li>
+ <li tal:repeat="item sections"><a tal:attributes="href item[0]" \
+tal:content="item[1]">caption</a></li>
</ul>
<div class="table">
<table>
# -*- coding: utf-8 -*-
"""
- Django to Jinja
- ~~~~~~~~~~~~~~~
+Django to Jinja
+~~~~~~~~~~~~~~~
- Helper module that can convert django templates into Jinja templates.
+Helper module that can convert django templates into Jinja templates.
- This file is not intended to be used as stand alone application but to
- be used as library. To convert templates you basically create your own
- writer, add extra conversion logic for your custom template tags,
- configure your django environment and run the `convert_templates`
- function.
+This file is not intended to be used as stand alone application but to
+be used as library. To convert templates you basically create your own
+writer, add extra conversion logic for your custom template tags,
+configure your django environment and run the `convert_templates`
+function.
- Here a simple example::
+Here a simple example::
- # configure django (or use settings.configure)
- import os
- os.environ['DJANGO_SETTINGS_MODULE'] = 'yourapplication.settings'
- from yourapplication.foo.templatetags.bar import MyNode
+ # configure django (or use settings.configure)
+ import os
- from django2jinja import Writer, convert_templates
+ os.environ["DJANGO_SETTINGS_MODULE"] = "yourapplication.settings"
+ from yourapplication.foo.templatetags.bar import MyNode
- def write_my_node(writer, node):
- writer.start_variable()
- writer.write('myfunc(')
- for idx, arg in enumerate(node.args):
- if idx:
- writer.write(', ')
- writer.node(arg)
- writer.write(')')
- writer.end_variable()
+ from django2jinja import Writer, convert_templates
- writer = Writer()
- writer.node_handlers[MyNode] = write_my_node
- convert_templates('/path/to/output/folder', writer=writer)
-
- Here is an example hos to automatically translate your django
- variables to jinja2::
-
- import re
- # List of tuple (Match pattern, Replace pattern, Exclusion pattern)
-
- var_re = ((re.compile(r"(u|user)\.is_authenticated"), r"\1.is_authenticated()", None),
- (re.compile(r"\.non_field_errors"), r".non_field_errors()", None),
- (re.compile(r"\.label_tag"), r".label_tag()", None),
- (re.compile(r"\.as_dl"), r".as_dl()", None),
- (re.compile(r"\.as_table"), r".as_table()", None),
- (re.compile(r"\.as_widget"), r".as_widget()", None),
- (re.compile(r"\.as_hidden"), r".as_hidden()", None),
-
- (re.compile(r"\.get_([0-9_\w]+)_url"), r".get_\1_url()", None),
- (re.compile(r"\.url"), r".url()", re.compile(r"(form|calendar).url")),
- (re.compile(r"\.get_([0-9_\w]+)_display"), r".get_\1_display()", None),
- (re.compile(r"loop\.counter"), r"loop.index", None),
- (re.compile(r"loop\.revcounter"), r"loop.revindex", None),
- (re.compile(r"request\.GET\.([0-9_\w]+)"), r"request.GET.get('\1', '')", None),
- (re.compile(r"request\.get_host"), r"request.get_host()", None),
-
- (re.compile(r"\.all(?!_)"), r".all()", None),
- (re.compile(r"\.all\.0"), r".all()[0]", None),
- (re.compile(r"\.([0-9])($|\s+)"), r"[\1]\2", None),
- (re.compile(r"\.items"), r".items()", None),
- )
- writer = Writer(var_re=var_re)
+ def write_my_node(writer, node):
+ writer.start_variable()
+ writer.write("myfunc(")
+ for idx, arg in enumerate(node.args):
+ if idx:
+ writer.write(", ")
+ writer.node(arg)
+ writer.write(")")
+ writer.end_variable()
- For details about the writing process have a look at the module code.
+ writer = Writer()
+ writer.node_handlers[MyNode] = write_my_node
+ convert_templates("/path/to/output/folder", writer=writer)
+
+Here is an example hos to automatically translate your django
+variables to jinja2::
+
+ import re
+ # List of tuple (Match pattern, Replace pattern, Exclusion pattern)
+ var_re = (
+ (re.compile("(u|user)\\.is_authenticated"), "\\1.is_authenticated()", None),
+ (re.compile("\\.non_field_errors"), ".non_field_errors()", None),
+ (re.compile("\\.label_tag"), ".label_tag()", None),
+ (re.compile("\\.as_dl"), ".as_dl()", None),
+ (re.compile("\\.as_table"), ".as_table()", None),
+ (re.compile("\\.as_widget"), ".as_widget()", None),
+ (re.compile("\\.as_hidden"), ".as_hidden()", None),
+ (re.compile("\\.get_([0-9_\\w]+)_url"), ".get_\\1_url()", None),
+ (re.compile("\\.url"), ".url()", re.compile("(form|calendar).url")),
+ (re.compile("\\.get_([0-9_\\w]+)_display"), ".get_\\1_display()", None),
+ (re.compile("loop\\.counte"), "loop.index", None),
+ (re.compile("loop\\.revcounte"), "loop.revindex", None),
+ (
+ re.compile("request\\.GET\\.([0-9_\\w]+)"),
+ "request.GET.get('\\1', '')",
+ None,
+ ),
+ (re.compile("request\\.get_host"), "request.get_host()", None),
+ (re.compile("\\.all(?!_)"), ".all()", None),
+ (re.compile("\\.all\\.0"), ".all()[0]", None),
+ (re.compile("\\.([0-9])($|\\s+)"), "[\\1]\\2", None),
+ (re.compile("\\.items"), ".items()", None),
+ )
+ writer = Writer(var_re=var_re)
+
+For details about the writing process have a look at the module code.
- :copyright: (c) 2009 by the Jinja Team.
- :license: BSD.
+:copyright: (c) 2009 by the Jinja Team.
+:license: BSD.
"""
from __future__ import print_function
from django.template.debug import DebugVariableNode as VariableNode
from django.templatetags import i18n as i18n_tags
-from jinja2.defaults import *
-
+from jinja2 import defaults
_node_handlers = {}
_resolved_filters = {}
_newline_re = re.compile(r"(?:\r\n|\r|\n)")
-
# Django stores an itertools object on the cycle node. Not only is this
# thread unsafe but also a problem for the converter which needs the raw
# string values passed to the constructor to create a jinja loop.cycle()
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.
+ translates them and writes the new templates into the output
+ directory.
"""
if writer is None:
writer = Writer()
if not os.path.exists(basetarget):
os.makedirs(basetarget)
callback(source)
- f = file(target, "w")
- try:
+ with open(target, "w") as f:
translate(f, source)
- finally:
- f.close()
class Writer(object):
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,
+ block_start_string=defaults.BLOCK_START_STRING,
+ block_end_string=defaults.BLOCK_END_STRING,
+ variable_start_string=defaults.VARIABLE_START_STRING,
+ variable_end_string=defaults.VARIABLE_END_STRING,
+ comment_start_string=defaults.COMMENT_START_STRING,
+ comment_end_string=defaults.COMMENT_END_STRING,
initial_autoescape=True,
use_jinja_autoescape=False,
custom_node_handlers=None,
- var_re=[],
+ var_re=None,
env=None,
):
if stream is None:
self.node_handlers = dict(_node_handlers, **(custom_node_handlers or {}))
self._loop_depth = 0
self._filters_warned = set()
- self.var_re = var_re
+ self.var_re = [] if var_re is None else var_re
self.env = env
def enter_loop(self):
- """Increments the loop depth so that write functions know if they
- are in a loop.
+ """Increments the loop depth so that write functions know if
+ they are in a loop.
"""
self._loop_depth += 1
self.stream.write(s.encode(settings.FILE_CHARSET))
def print_expr(self, expr):
- """Open a variable tag, write to the string to the stream and close."""
+ """Open a variable tag, write to the string to the stream and
+ close.
+ """
self.start_variable()
self.write(expr)
self.end_variable()
self.end_block()
def variable(self, name):
- """Prints a variable. This performs variable name transformation."""
+ """Prints a variable. This performs variable name
+ transformation.
+ """
self.write(self.translate_variable_name(name))
def literal(self, value):
if name is None:
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 defaults.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)
if not want_pipe:
"""
if filter not in _resolved_filters:
for library in libraries.values():
- for key, value in library.filters.iteritems():
+ for key, value in library.filters.items():
_resolved_filters[value] = key
return _resolved_filters.get(filter, None)
def node(self, node):
"""Invokes the node handler for a node."""
- for cls, handler in self.node_handlers.iteritems():
+ for cls, handler in self.node_handlers.items():
if type(node) is cls or type(node).__name__ == cls:
handler(self, node)
break
@node(core_tags.DebugNode)
-def comment_tag(writer, node):
+def debug_tag(writer, node):
writer.warn(
"Debug tag detected. Make sure to add a global function "
"called debug to the namespace.",
writer.write("set %s = " % node.asvar)
else:
writer.start_variable()
- autoescape = writer.autoescape
writer.write("url(")
writer.literal(node.view_name)
for arg in node.args:
touch_var(key)
writer.node(var.filter_expression)
- have_plural = False
- plural_var = None
if node.plural and node.countervar and node.counter:
- have_plural = True
plural_var = node.countervar
if plural_var not in variables:
if idx > -1:
from django.conf import settings
+from django2jinja import convert_templates
+from django2jinja import Writer
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)
def get_characters():
"""Find every Unicode character that is valid in a Python `identifier`_ but
- is not matched by the regex ``\w`` group.
+ is not matched by the regex ``\\w`` group.
- ``\w`` matches some characters that aren't valid in identifiers, but
+ ``\\w`` matches some characters that aren't valid in identifiers, but
:meth:`str.isidentifier` will catch that later in lexing.
All start characters are valid continue characters, so we only test for
Source: https://stackoverflow.com/a/4629241/400617
"""
- for a, b in itertools.groupby(enumerate(data), lambda x: ord(x[1]) - x[0]):
+ for _, b in itertools.groupby(enumerate(data), lambda x: ord(x[1]) - x[0]):
b = list(b)
yield b[0][1], b[-1][1]
)
with open(filename, "w", encoding="utf8") as f:
+ f.write("")
f.write("# generated by scripts/generate_identifier_pattern.py\n")
- f.write('pattern = "{}"\n'.format(pattern))
+ f.write('pattern = "{}" # noqa: B950\n'.format(pattern))
if __name__ == "__main__":
with open("CHANGES.rst") as f:
lineiter = iter(f)
for line in lineiter:
- match = re.search("^Version\s+(.*)", line.strip())
+ match = re.search(r"^Version\s+(.*)", line.strip())
if match is None:
continue
fail("You have uncommitted changes in git")
try:
- import wheel
+ __import__("wheel")
except ImportError:
fail("You need to install the wheel package.")
W503
# up to 88 allowed by bugbear B950
max-line-length = 80
+per-file-ignores =
+ # __init__ module exports names
+ src/jinja2/__init__.py: F401
# -*- coding: utf-8 -*-
+# flake8: noqa
"""
jinja2._compat
~~~~~~~~~~~~~~
:copyright: Copyright 2013 by the Jinja team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import marshal
import sys
PY2 = sys.version_info[0] == 2
implements_to_string = _identity
encode_filename = _identity
+ marshal_dump = marshal.dump
+ marshal_load = marshal.load
+
else:
unichr = unichr
text_type = unicode
return filename.encode("utf-8")
return filename
+ def marshal_dump(code, f):
+ if isinstance(f, file):
+ marshal.dump(code, f)
+ else:
+ f.write(marshal.dumps(code))
+
+ def marshal_load(f):
+ if isinstance(f, file):
+ return marshal.load(f)
+ return marshal.loads(f.read())
+
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
# generated by scripts/generate_identifier_pattern.py
-pattern = "·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯"
+pattern = "·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯" # noqa: B950
def dualfilter(normal_filter, async_filter):
wrap_evalctx = False
if getattr(normal_filter, "environmentfilter", False):
- is_async = lambda args: args[0].is_async
+
+ def is_async(args):
+ return args[0].is_async
+
wrap_evalctx = False
else:
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
+
+ def is_async(args):
+ return args[0].environment.is_async
@wraps(normal_filter)
def wrapper(*args, **kwargs):
if attribute is not None:
func = filters.make_attrgetter(environment, attribute)
else:
- func = lambda x: x
+
+ def func(x):
+ return x
+
async for item in auto_aiter(iterable):
rv += func(item)
return rv
"""
import errno
import fnmatch
-import marshal
import os
import stat
import sys
from os import path
from ._compat import BytesIO
+from ._compat import marshal_dump
+from ._compat import marshal_load
from ._compat import pickle
-from ._compat import PY2
from ._compat import text_type
from .utils import open_if_exists
-# marshal works better on 3.x, one hack less required
-if not PY2:
- marshal_dump = marshal.dump
- marshal_load = marshal.load
-else:
-
- def marshal_dump(code, f):
- if isinstance(f, file):
- marshal.dump(code, f)
- else:
- f.write(marshal.dumps(code))
-
- def marshal_load(f):
- if isinstance(f, file):
- return marshal.load(f)
- return marshal.loads(f.read())
-
-
bc_version = 3
# magic version used to only change with new jinja versions. With 2.6
else:
args.append(frame.symbols.declare_parameter("caller"))
macro_ref.accesses_caller = True
- if "kwargs" in undeclared and not "kwargs" in skip_special_params:
+ if "kwargs" in undeclared and "kwargs" not 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:
+ if "varargs" in undeclared and "varargs" not in skip_special_params:
args.append(frame.symbols.declare_parameter("varargs"))
macro_ref.accesses_varargs = True
if frame.toplevel:
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.macro_def(macro_ref, macro_frame)
with_frame = frame.inner()
with_frame.symbols.analyze_node(node)
self.enter_frame(with_frame)
- for idx, (target, expr) in enumerate(izip(node.targets, node.values)):
+ for target, expr in izip(node.targets, node.values):
self.newline()
self.visit(target, with_frame)
self.write(" = ")
if getattr(env_finalize, "contextfunction", False):
src += "context, "
- finalize = None
+ finalize = None # noqa: F811
elif getattr(env_finalize, "evalcontextfunction", False):
src += "context.eval_ctx, "
finalize = None
self.visit(item.value, frame)
self.write("}")
- def binop(operator, interceptable=True):
+ def binop(operator, interceptable=True): # noqa: B902
@optimizeconst
def visitor(self, node, frame):
if (
return visitor
- def uaop(operator, interceptable=True):
+ def uaop(operator, interceptable=True): # noqa: B902
@optimizeconst
def visitor(self, node, frame):
if (
# the new traceback without this frame.
try:
exec(code, globals, locals)
- except:
+ except BaseException:
return sys.exc_info()[2].tb_next
:license: BSD, see LICENSE for more details.
"""
from ._compat import range_type
+from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401
+from .tests import TESTS as DEFAULT_TESTS # noqa: F401
from .utils import Cycler
from .utils import generate_lorem_ipsum
from .utils import Joiner
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,
from . import nodes
from ._compat import encode_filename
-from ._compat import ifilter
-from ._compat import imap
from ._compat import implements_iterator
from ._compat import implements_to_string
from ._compat import iteritems
self.enable_async = enable_async
self.is_async = self.enable_async and have_async_gen
if self.is_async:
- import jinja2.asyncsupport # runs patch_all() once
+ # runs patch_all() to enable async support
+ import jinja2.asyncsupport # noqa: F401
_environment_sanity_check(self)
from jinja2.loaders import ModuleLoader
if log_function is None:
- log_function = lambda x: None
+
+ def log_function(x):
+ pass
if py_compile:
if not PY2 or PYPY:
.. versionadded:: 2.4
"""
- x = self.loader.list_templates()
+ names = 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
+
+ def filter_func(x):
+ return "." in x and x.rsplit(".", 1)[1] in extensions
+
if filter_func is not None:
- x = list(ifilter(filter_func, x))
- return x
+ names = [name for name in names if filter_func(name)]
+
+ return names
def handle_exception(self, source=None):
"""Exception handling helper. This is used internally to either raise
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(map(int, x.split("="))) for x in self._debug_info.split("&")]
return []
def __repr__(self):
# non unicode strings.
GETTEXT_FUNCTIONS = ("_", "gettext", "ngettext")
+_ws_re = re.compile(r"\s*\n\s*")
+
class ExtensionRegistry(type):
"""Gives the extension an unique identifier."""
- def __new__(cls, name, bases, d):
- rv = type.__new__(cls, name, bases, d)
+ def __new__(mcs, name, bases, d):
+ rv = type.__new__(mcs, name, bases, d)
rv.identifier = rv.__module__ + "." + rv.__name__
return rv
else:
return node
- def _trim_whitespace(self, string, _ws_re=re.compile(r"\s*\n\s*")):
+ def _trim_whitespace(self, string, _ws_re=_ws_re):
return _ws_re.sub(" ", string.strip())
def _parse_block(self, parser, allow_pluralize):
elif parser.stream.eos:
parser.fail("unclosed translation block")
else:
- assert False, "internal parser error"
+ raise RuntimeError("internal parser error")
return referenced, concat(buf)
else:
strings.append(None)
- for arg in node.kwargs:
+ for _ in node.kwargs:
strings.append(None)
if node.dyn_args is not None:
strings.append(None)
try:
node = environment.parse(source)
tokens = list(environment.lex(environment.preprocess(source)))
- except TemplateSyntaxError as e:
+ except TemplateSyntaxError:
if not silent:
raise
# skip templates with syntax errors
.. sourcecode:: jinja
- {% for user in users|sort(attribute="name")|sort(reverse=true, attribute="age") %}
+ {% for user in users|sort(attribute="name")
+ |sort(reverse=true, attribute="age") %}
...
{% endfor %}
"""
Return the last item of a sequence.
- Note: Does not work with generators. You may want to explicitly convert it to a list:
+ Note: Does not work with generators. You may want to explicitly
+ convert it to a list:
.. sourcecode:: jinja
{{ 42.55|round|int }}
-> 43
"""
- if not method in ("common", "ceil", "floor"):
+ if method not in {"common", "ceil", "floor"}:
raise FilterArgumentError("method must be common, ceil or floor")
if method == "common":
return round(value, precision)
args = args[3:]
except LookupError:
raise FilterArgumentError("map requires a filter argument")
- func = lambda item: context.environment.call_filter(
- name, item, args, kwargs, context=context
- )
+
+ def func(item):
+ return context.environment.call_filter(
+ name, item, args, kwargs, context=context
+ )
return seq, func
off = 1
else:
off = 0
- transfunc = lambda x: x
+
+ def transfunc(x):
+ return x
try:
name = args[2 + off]
args = args[3 + off :]
- func = lambda item: context.environment.call_test(name, item, args, kwargs)
+
+ def func(item):
+ return context.environment.call_test(name, item, args, kwargs)
+
except LookupError:
func = bool
def skip(self, n=1):
"""Got n tokens ahead."""
- for x in range(n):
+ for _ in range(n):
next(self)
def next_if(self, expr):
def __init__(self, environment):
# shortcuts
- c = lambda x: re.compile(x, re.M | re.S)
e = re.escape
+ def c(x):
+ return re.compile(x, re.M | re.S)
+
# lexing rules for tags
tag_rules = [
(whitespace_re, TOKEN_WHITESPACE, None),
found = set()
for searchpath in self.searchpath:
walk_dir = os.walk(searchpath, followlinks=self.followlinks)
- for dirpath, dirnames, filenames in walk_dir:
+ for dirpath, _, filenames in walk_dir:
for filename in filenames:
template = (
os.path.join(dirpath, filename)[len(searchpath) :]
inheritance. fields and attributes from the parent class are
automatically forwarded to the child."""
- def __new__(cls, name, bases, d):
+ def __new__(mcs, name, bases, d):
for attr in "fields", "attributes":
storage = []
storage.extend(getattr(bases[0], attr, ()))
assert len(storage) == len(set(storage)), "layout conflict"
d[attr] = tuple(storage)
d.setdefault("abstract", False)
- return type.__new__(cls, name, bases, d)
+ return type.__new__(mcs, name, bases, d)
class EvalContext(object):
over all fields and yields the values of they are nodes. If the value
of a field is a list all the nodes in that list are returned.
"""
- for field, item in self.iter_fields(exclude, only):
+ for _, item in self.iter_fields(exclude, only):
if isinstance(item, list):
for n in item:
if isinstance(n, Node):
targets = []
values = []
while self.stream.current.type != "block_end":
- lineno = self.stream.current.lineno
if targets:
self.stream.expect("comma")
target = self.parse_assign_target()
elif with_condexpr:
parse = self.parse_expression
else:
- parse = lambda: self.parse_expression(with_condexpr=False)
+
+ def parse():
+ return self.parse_expression(with_condexpr=False)
+
args = []
is_tuple = False
while 1:
from itertools import chain
from types import MethodType
+from markupsafe import escape
+from markupsafe import Markup
+from markupsafe import soft_unicode
+
from ._compat import abc
from ._compat import imap
from ._compat import implements_iterator
from .exceptions import UndefinedError
from .nodes import EvalContext
from .utils import concat
-from .utils import escape
from .utils import evalcontextfunction
from .utils import internalcode
-from .utils import Markup
from .utils import missing
from .utils import Namespace
from .utils import object_type_repr
-from .utils import soft_unicode
# these variables are exported to the template runtime
__all__ = [
#: a string. We can just use the text type here.
to_string = text_type
-#: the identity function. Useful for certain things in the environment
-identity = lambda x: x
-_first_iteration = object()
-_last_iteration = object()
+def identity(x):
+ """Returns its argument. Useful for certain things in the
+ environment.
+ """
+ return x
def markup_join(seq):
class ContextMeta(type):
- def __new__(cls, name, bases, d):
- rv = type.__new__(cls, name, bases, d)
+ def __new__(mcs, name, bases, d):
+ rv = type.__new__(mcs, name, bases, d)
if bases == ():
return rv
return dict(self.parent, **self.vars)
@internalcode
- def call(__self, __obj, *args, **kwargs):
+ def call(__self, __obj, *args, **kwargs): # noqa: B902
"""Call the callable with the arguments and keyword arguments
provided but inject the active context or environment as first
argument if the callable is a :func:`contextfunction` or
__traceback_hide__ = True # noqa
# Allow callable classes to take a context
- if hasattr(__obj, "__call__"):
+ if hasattr(__obj, "__call__"): # noqa: B004
fn = __obj.__call__
for fn_type in (
"contextfunction",
context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks))
return context
- def _all(meth):
- proxy = lambda self: getattr(self.get_all(), meth)()
+ def _all(meth): # noqa: B902
+ def proxy(self):
+ return getattr(self.get_all(), meth)()
+
proxy.__doc__ = getattr(dict, meth).__doc__
proxy.__name__ = meth
return proxy
# 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 name in self.arguments[len(arguments) :]:
try:
value = kwargs.pop(name)
except KeyError:
"""
import operator
import types
+import warnings
+from collections import deque
from string import Formatter
from markupsafe import EscapeFormatter
#: 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 = {
+ "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.
UNSAFE_FUNCTION_ATTRIBUTES = set()
-
#: unsafe method attributes. function attributes are unsafe for methods too
-UNSAFE_METHOD_ATTRIBUTES = set(["im_class", "im_func", "im_self"])
+UNSAFE_METHOD_ATTRIBUTES = {"im_class", "im_func", "im_self"}
#: unsafe generator attributes.
-UNSAFE_GENERATOR_ATTRIBUTES = set(["gi_frame", "gi_code"])
+UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
#: unsafe attributes on coroutines
-UNSAFE_COROUTINE_ATTRIBUTES = set(["cr_frame", "cr_code"])
+UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
#: unsafe attributes on async generators
-UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(["ag_code", "ag_frame"])
-
-import warnings
+UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
# 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"
)
-from collections import deque
-
_mutable_set_types = (set,)
_mutable_mapping_types = (dict,)
_mutable_sequence_types = (list,)
_mutable_mapping_types += (abc.MutableMapping,)
_mutable_sequence_types += (abc.MutableSequence,)
-
_mutable_spec = (
(
_mutable_set_types,
rv = formatter.vformat(s, args, kwargs)
return type(s)(rv)
- def call(__self, __context, __obj, *args, **kwargs):
+ def call(__self, __context, __obj, *args, **kwargs): # noqa: B902
"""Call an object from sandboxed code."""
fmt = inspect_format_method(__obj)
if fmt is not None:
try:
len(value)
value.__getitem__
- except:
+ except Exception:
return False
return True
from collections import deque
from threading import Lock
+from markupsafe import escape
+from markupsafe import Markup
+
from ._compat import abc
from ._compat import string_types
from ._compat import text_type
def consume(iterable):
"""Consumes an iterable without doing anything with it."""
- for event in iterable:
+ for _ in iterable:
pass
if (
"@" in middle
and not middle.startswith("www.")
- and not ":" in middle
+ and ":" not in middle
and _simple_email_re.match(middle)
):
middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
def iteritems(self):
"""Iterate over all items."""
+ warnings.warn(
+ "'iteritems()' will be removed in version 3.0. Use"
+ " 'iter(cache.items())' instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
return iter(self.items())
def values(self):
def itervalue(self):
"""Iterate over all values."""
warnings.warn(
- DeprecationWarning(
- '"itervalue()" is deprecated and will be removed in version 2.12.'
- + ' Use "itervalues()" instead.'
- ),
+ "'itervalue()' will be removed in version 3.0. Use"
+ " 'iter(cache.values())' instead.",
+ DeprecationWarning,
stacklevel=2,
)
- return self.itervalues()
+ return iter(self.values())
def itervalues(self):
"""Iterate over all values."""
+ warnings.warn(
+ "'itervalues()' will be removed in version 3.0. Use"
+ " 'iter(cache.values())' instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
return iter(self.values())
def keys(self):
"""Iterate over all keys in the cache dict, ordered by
the most recent usage.
"""
- return reversed(tuple(self._queue))
+ warnings.warn(
+ "'iterkeys()' will be removed in version 3.0. Use"
+ " 'iter(cache.keys())' instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return iter(self)
- __iter__ = iterkeys
+ def __iter__(self):
+ return reversed(tuple(self._queue))
def __reversed__(self):
"""Iterate over the keys in the cache dict, oldest items
"""A namespace object that can hold arbitrary attributes. It may be
initialized from a dictionary or with keyword arguments."""
- def __init__(*args, **kwargs):
+ def __init__(*args, **kwargs): # noqa: B902
self, args = args[0], args[1:]
self.__attrs = dict(*args, **kwargs)
have_async_gen = False
-# Imported here because that's where it was in the past
-from markupsafe import Markup, escape, soft_unicode
+def soft_unicode(s):
+ from markupsafe import soft_unicode
+
+ warnings.warn(
+ "'jinja2.utils.soft_unicode' will be removed in version 3.0."
+ " Use 'markupsafe.soft_unicode' instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return soft_unicode(s)
c.reset()
assert c.current == 1
- def test_cycler_nextmethod(self, env):
- items = 1, 2, 3
- c = Cycler(*items)
- for item in items + items:
- assert c.current == item
- assert c.next() == item
- c.next()
- assert c.current == 2
- c.reset()
- assert c.current == 1
-
def test_expressions(self, env):
expr = env.compile_expression("foo")
assert expr() is None
assert und1 != 42
assert hash(und1) == hash(und2) == hash(Undefined())
with pytest.raises(AttributeError):
- getattr(Undefined, "__slots__")
+ getattr(Undefined, "__slots__") # noqa: B009
def test_chainable_undefined(self):
env = Environment(undefined=ChainableUndefined)
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__") # noqa: B009
# The following tests ensure subclass functionality works as expected
assert env.from_string('{{ missing.bar["baz"] }}').render() == u""
== u"{{ undefined value printed: %s }}" % undefined_hint
)
with pytest.raises(AttributeError):
- getattr(DebugUndefined, "__slots__")
+ getattr(DebugUndefined, "__slots__") # noqa: B009
def test_strict_undefined(self):
env = Environment(undefined=StrictUndefined)
== "default"
)
with pytest.raises(AttributeError):
- getattr(StrictUndefined, "__slots__")
+ getattr(StrictUndefined, "__slots__") # noqa: B009
assert env.from_string('{{ "foo" if false }}').render() == ""
def test_indexing_gives_undefined(self):
def test_recursive_depth0(self, test_env_async):
tmpl = test_env_async.from_string(
- """{% for item in seq recursive -%}
- [{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}"""
+ "{% for item in seq recursive %}[{{ loop.depth0 }}:{{ item.a }}"
+ "{% if item.b %}<{{ loop(item.b) }}>{% endif %}]{% endfor %}"
)
assert (
tmpl.render(
def test_recursive_depth(self, test_env_async):
tmpl = test_env_async.from_string(
- """{% for item in seq recursive -%}
- [{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
- {%- endfor %}"""
+ "{% for item in seq recursive %}[{{ loop.depth }}:{{ item.a }}"
+ "{% if item.b %}<{{ loop(item.b) }}>{% endif %}]{% endfor %}"
)
assert (
tmpl.render(
)
sm = t.render(
this="/foo",
- site={
- "root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"},]}
- },
+ site={"root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"}]}},
)
lines = [x.strip() for x in sm.splitlines() if x.strip()]
assert lines == [
"""
)
sm = t.render(
- this="/foo", items=[{"url": "/"}, {"url": "/foo"}, {"url": "/bar"},]
+ this="/foo", items=[{"url": "/"}, {"url": "/foo"}, {"url": "/bar"}]
)
lines = [x.strip() for x in sm.splitlines() if x.strip()]
assert lines == [
+from collections import namedtuple
+
import pytest
from jinja2 import Environment
def mark_dualiter(parameter, factory):
def decorator(f):
return pytest.mark.parametrize(
- parameter, [lambda: factory(), lambda: make_aiter(factory()),]
+ parameter, [lambda: factory(), lambda: make_aiter(factory())]
)(f)
return decorator
def make_articles():
- class Date(object):
- def __init__(self, day, month, year):
- self.day = day
- self.month = month
- self.year = year
-
- class Article(object):
- def __init__(self, title, *date):
- self.date = Date(*date)
- self.title = title
-
+ Date = namedtuple("Date", "day,month,year")
+ Article = namedtuple("Article", "title,date")
return [
- Article("aha", 1, 1, 1970),
- Article("interesting", 2, 1, 1970),
- Article("really?", 3, 1, 1970),
- Article("totally not", 1, 1, 1971),
+ Article("aha", Date(1, 1, 1970)),
+ Article("interesting", Date(2, 1, 1970)),
+ Article("really?", Date(3, 1, 1970)),
+ Article("totally not", Date(1, 1, 1971)),
]
def make_users():
- class User(object):
- def __init__(self, username):
- self.username = username
-
+ User = namedtuple("User", "username")
return map(User, ["foo", "bar"])
def make_users():
- class User(object):
- def __init__(self, name, is_active):
- self.name = name
- self.is_active = is_active
-
+ User = namedtuple("User", "name,is_active")
return [
User("john", True),
User("jane", True),
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') }}""")
assert tmpl.render(items=items)
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"
+ assert tmpl.render(values={"foo": 23, "bar": 1, "baz": 18}) == "42"
@mark_dualiter("items", lambda: range(10))
def test_slice(env_async, items):
tmpl = env_async.from_string(
- "{{ items()|slice(3)|list }}|" '{{ items()|slice(3, "X")|list }}'
+ "{{ items()|slice(3)|list }}|{{ items()|slice(3, 'X')|list }}"
)
out = tmpl.render(items=items)
assert out == (
def test_recursive_depth0(self, env):
tmpl = env.from_string(
"""{% for item in seq recursive -%}
- [{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
+ [{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
{%- endfor %}"""
)
assert (
def test_recursive_depth(self, env):
tmpl = env.from_string(
"""{% for item in seq recursive -%}
- [{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
+ [{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
{%- endfor %}"""
)
assert (
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" %}'
- )
+ 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 }}"
+ "{% set ns = namespace() %}{% set ns.bar = '42' %}{{ ns.bar }}"
)
assert tmpl.render() == "42"
"""
import pickle
import re
-import sys
from traceback import format_exception
import pytest
@pytest.mark.debug
class TestDebug(object):
def assert_traceback_matches(self, callback, expected_tb):
- try:
+ with pytest.raises(Exception) as exc_info:
callback()
- 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,
- )
- else:
- assert False, "Expected exception"
+
+ tb = format_exception(exc_info.type, exc_info.value, exc_info.tb)
+ m = re.search(expected_tb.strip(), "".join(tb))
+ assert m is not None, "Traceback did not match:\n\n%s\nexpected:\n%s" % (
+ "".join(tb),
+ expected_tb,
+ )
def test_runtime_error(self, fs_env):
def test():
# error in the middle of other compiler frames.
self.assert_traceback_matches(
lambda: fs_env.get_template("syntaxerror.html"),
- r"""(?sm)
+ """(?sm)
File ".*?syntaxerror.html", line 4, in (template|<module>)
- \{% endif %\}.*?
-(jinja2\.exceptions\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'.
+ \\{% endif %\\}.*?
+(jinja2\\.exceptions\\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja \
+was looking for the following tags: 'endfor' or 'else'. The innermost block that needs \
+to be closed is 'for'.
""",
)
# 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 -%}"
+ "{%- trans trimmed = 'world' %} hello\n {{ trimmed }} {% endtrans -%}"
)
assert tmpl.render() == " hello\n world "
from jinja2 import contextfilter
from jinja2 import Environment
from jinja2 import Template
+from jinja2._compat import text_type
@pytest.mark.skipif(sys.version_info < (3, 5), reason="Requires 3.5 or later")
env.policies["compiler.ascii_str"] = False
t = env.from_string('{{ "foo"|assert }}')
- t.render(expected_type=unicode)
+ t.render(expected_type=text_type)
env.policies["compiler.ascii_str"] = True
t = env.from_string('{{ "foo"|assert }}')
for val in True, False:
env.policies["compiler.ascii_str"] = val
t = env.from_string(u'{{ "\N{SNOWMAN}"|assert }}')
- t.render(expected_type=unicode)
+ t.render(expected_type=text_type)
:license: BSD, see LICENSE for more details.
"""
import random
+from collections import namedtuple
import pytest
)
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]]|"
assert tmpl.render() == "<foo><span>foo</span>"
def test_join_attribute(self, env):
- class User(object):
- def __init__(self, username):
- self.username = username
-
+ User = namedtuple("User", "username")
tmpl = env.from_string("""{{ users|join(', ', 'username') }}""")
assert tmpl.render(users=map(User, ["foo", "bar"])) == "foo, bar"
def test_reverse(self, env):
tmpl = env.from_string(
- '{{ "foobar"|reverse|join }}|' "{{ [1, 2, 3]|reverse|list }}"
+ "{{ 'foobar'|reverse|join }}|{{ [1, 2, 3]|reverse|list }}"
)
assert tmpl.render() == "raboof|[3, 2, 1]"
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):
def test_sum_attributes(self, env):
tmpl = env.from_string("""{{ values|sum('value') }}""")
- assert tmpl.render(values=[{"value": 23}, {"value": 1}, {"value": 18},]) == "42"
+ 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') }}""")
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"
+ 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() == "a:1:2|b:1|"
def test_groupby_multidot(self, env):
- class Date(object):
- def __init__(self, day, month, year):
- self.day = day
- self.month = month
- self.year = year
-
- class Article(object):
- def __init__(self, title, *date):
- self.date = Date(*date)
- self.title = title
-
+ Date = namedtuple("Date", "day,month,year")
+ Article = namedtuple("Article", "title,date")
articles = [
- Article("aha", 1, 1, 1970),
- Article("interesting", 2, 1, 1970),
- Article("really?", 3, 1, 1970),
- Article("totally not", 1, 1, 1971),
+ Article("aha", Date(1, 1, 1970)),
+ Article("interesting", Date(2, 1, 1970)),
+ Article("really?", Date(3, 1, 1970)),
+ Article("totally not", Date(1, 1, 1971)),
]
tmpl = env.from_string(
"""
assert tmpl.render() == "[3, 3, 15]"
def test_attribute_map(self, env):
- class User(object):
- def __init__(self, name):
- self.name = name
-
+ User = namedtuple("User", "name")
env = Environment()
users = [
User("john"),
assert tmpl.render() == "[]"
def test_map_default(self, env):
- class Fullname(object):
- def __init__(self, firstname, lastname):
- self.firstname = firstname
- self.lastname = lastname
-
- class Firstname(object):
- def __init__(self, firstname):
- self.firstname = firstname
-
+ Fullname = namedtuple("Fullname", "firstname,lastname")
+ Firstname = namedtuple("Firstname", "firstname")
env = Environment()
tmpl = env.from_string(
'{{ users|map(attribute="lastname", default="smith")|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
-
+ User = namedtuple("User", "name,is_active")
env = Environment()
users = [
User("john", True),
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
-
+ User = namedtuple("User", "name,is_active")
env = Environment()
users = [
User("john", True),
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
-
+ User = namedtuple("User", "id,name")
env = Environment()
users = [
User(1, "john"),
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
-
+ User = namedtuple("User", "id,name")
env = Environment()
users = [
User(1, "john"),
def test_import_from_with_context(self):
env = Environment(
- loader=DictLoader({"a": "{% macro x() %}{{ foobar }}{% endmacro %}",})
+ loader=DictLoader({"a": "{% macro x() %}{{ foobar }}{% endmacro %}"})
)
t = env.from_string(
- '{% set foobar = 42 %}{% from "a" ' "import x with context %}{{ x() }}"
+ "{% set foobar = 42 %}{% from 'a' import x with context %}{{ x() }}"
)
assert t.render() == "42"
from jinja2 import DictLoader
from jinja2 import Environment
-from jinja2 import TemplateError
from jinja2 import TemplateRuntimeError
LAYOUTTEMPLATE = """\
assert tmpl.render() == "--INTRO--|BEFORE|[(INNER)]|AFTER"
def test_working(self, env):
- tmpl = env.get_template("working")
+ env.get_template("working")
def test_reuse_blocks(self, env):
tmpl = env.from_string(
)
)
t = env.from_string(
- '{% extends "master.html" %}{% block item %}' "{{ item }}{% endblock %}"
+ "{% extends 'master.html' %}{% block item %}{{ item }}{% endblock %}"
)
assert t.render(seq=list(range(5))) == "[0][1][2][3][4]"
assert tmpl.render() == "123"
def test_raw3(self, env):
- # The second newline after baz exists because it is AFTER the {% raw %} and is ignored.
+ # The second newline after baz exists because it is AFTER the
+ # {% raw %} and is ignored.
env = Environment(lstrip_blocks=True, trim_blocks=True)
tmpl = env.from_string("bar\n{% raw %}\n {{baz}}2 spaces\n{% endraw %}\nfoo")
assert tmpl.render(baz="test") == "bar\n\n {{baz}}2 spaces\nfoo"
def test_raw4(self, env):
- # The trailing dash of the {% raw -%} cleans both the spaces and newlines up to the first character of data.
+ # 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"
def test_notin(self, env):
bar = range(100)
tmpl = env.from_string("""{{ not 42 in bar }}""")
- assert tmpl.render(bar=bar) == text_type(not 42 in bar)
+ assert tmpl.render(bar=bar) == "False"
def test_operator_precedence(self, env):
tmpl = env.from_string("""{{ 2 * 3 + 4 % 2 + 1 - 2 }}""")
t2 = env.get_template("two")
assert t2 is env.get_template("two")
assert t1 is env.get_template("one")
- t3 = env.get_template("three")
+ env.get_template("three")
loader_ref = weakref.ref(loader)
assert (loader_ref, "one") in env.cache
assert (loader_ref, "two") not in env.cache
@pytest.mark.parametrize(
("encoding", "expect"),
- [("utf-8", u"文字化け"), ("iso-8859-1", u"æ\x96\x87å\xad\x97å\x8c\x96ã\x81\x91"),],
+ [
+ ("utf-8", u"文字化け"),
+ ("iso-8859-1", u"æ\x96\x87\xe5\xad\x97\xe5\x8c\x96\xe3\x81\x91"),
+ ],
)
def test_uses_specified_encoding(self, encoding, expect):
loader = loaders.FileSystemLoader(self.searchpath, encoding=encoding)
def test_weak_references(self, prefix_loader):
self.compile_down(prefix_loader)
- tmpl = self.mod_env.get_template("a/test.html")
+ self.mod_env.get_template("a/test.html")
key = loaders.ModuleLoader.get_template_key("a/test.html")
name = self.mod_env.loader.module.__name__
assert name in sys.modules
# unset all, ensure the module is gone from sys.modules
- self.mod_env = tmpl = None
+ self.mod_env = None
try:
import gc
gc.collect()
- except:
+ except BaseException:
pass
assert name not in sys.modules
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")
+ 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.compile_down(prefix_loader)
self.mod_env.loader = loaders.ChoiceLoader(
[self.mod_env.loader, loaders.DictLoader({"DICT_SOURCE": "DICT_TEMPLATE"})]
)
-
tmpl1 = self.mod_env.get_template("a/test.html")
assert tmpl1.render() == "BAR"
tmpl2 = self.mod_env.get_template("DICT_SOURCE")
assert tmpl2.render() == "DICT_TEMPLATE"
def test_prefix_loader(self, prefix_loader):
- log = self.compile_down(prefix_loader)
-
+ self.compile_down(prefix_loader)
self.mod_env.loader = loaders.PrefixLoader(
{
"MOD": self.mod_env.loader,
"DICT": loaders.DictLoader({"test.html": "DICT_TEMPLATE"}),
}
)
-
tmpl1 = self.mod_env.get_template("MOD/a/test.html")
assert tmpl1.render() == "BAR"
tmpl2 = self.mod_env.get_template("DICT/test.html")
)
pytest.raises(TemplateSyntaxError, Template, "{% for x in %}..{% endfor %}")
- def test_recursive_loop_bug(self, env):
- tpl1 = Template(
+ def test_recursive_loop_compile(self, env):
+ Template(
"""
- {% for p in foo recursive%}
- {{p.bar}}
- {% for f in p.fields recursive%}
- {{f.baz}}
+ {% for p in foo recursive%}
{{p.bar}}
- {% if f.rec %}
- {{ loop(f.sub) }}
- {% endif %}
+ {% for f in p.fields recursive%}
+ {{f.baz}}
+ {{p.bar}}
+ {% if f.rec %}
+ {{ loop(f.sub) }}
+ {% endif %}
+ {% endfor %}
{% endfor %}
- {% endfor %}
- """
+ """
)
-
- tpl2 = Template(
+ Template(
"""
- {% for p in foo%}
- {{p.bar}}
- {% for f in p.fields recursive%}
- {{f.baz}}
+ {% for p in foo%}
{{p.bar}}
- {% if f.rec %}
- {{ loop(f.sub) }}
- {% endif %}
+ {% for f in p.fields recursive%}
+ {{f.baz}}
+ {{p.bar}}
+ {% if f.rec %}
+ {{ loop(f.sub) }}
+ {% endif %}
+ {% endfor %}
{% endfor %}
- {% endfor %}
- """
+ """
)
def test_else_loop_bug(self, env):
def test_block_set_with_extends(self):
env = Environment(
- loader=DictLoader({"main": "{% block body %}[{{ x }}]{% endblock %}",})
+ loader=DictLoader({"main": "{% block body %}[{{ x }}]{% endblock %}"})
)
t = env.from_string('{% extends "main" %}{% set x %}42{% endset %}')
assert t.render() == "[42]"
)
sm = t.render(
this="/foo",
- site={
- "root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"},]}
- },
+ site={"root": {"url": "/", "children": [{"url": "/foo"}, {"url": "/bar"}]}},
)
lines = [x.strip() for x in sm.splitlines() if x.strip()]
assert lines == [
env = Environment(
loader=DictLoader(
{
- "inc": "{{ item }}",
- "main": '{% for item in [1, 2, 3] %}{% include "inc" %}{% endfor %}',
+ "inc": "{{ i }}",
+ "main": '{% for i in [1, 2, 3] %}{% include "inc" %}{% endfor %}',
}
)
)
assert env.get_template("test").render(foobar="test") == "test"
def test_legacy_custom_context(self, env):
- from jinja2.runtime import Context, Undefined, missing
+ from jinja2.runtime import Context, missing
class MyContext(Context):
def resolve(self, name):
def test_recursive_loop_bug(self, env):
tmpl = env.from_string(
- """
- {%- for value in values recursive %}1{% else %}0{% endfor -%}
- """
+ "{%- for value in values recursive %}1{% else %}0{% endfor -%}"
)
assert tmpl.render(values=[]) == "0"
from jinja2 import Template
from jinja2.runtime import LoopContext
-
TEST_IDX_TEMPLATE_STR_1 = (
- "[{% for i in lst|reverse %}"
- + "(len={{ loop.length }}, revindex={{ loop.revindex }}, index={{ loop.index }}, val={{ i }})"
- + "{% endfor %}]"
+ "[{% for i in lst|reverse %}(len={{ loop.length }},"
+ " revindex={{ loop.revindex }}, index={{ loop.index }}, val={{ i }}){% endfor %}]"
)
-
-
TEST_IDX0_TEMPLATE_STR_1 = (
- "[{% for i in lst|reverse %}"
- + "(len={{ loop.length }}, revindex0={{ loop.revindex0 }}, index0={{ loop.index0 }}, val={{ i }})"
- + "{% endfor %}]"
+ "[{% for i in lst|reverse %}(len={{ loop.length }},"
+ " revindex0={{ loop.revindex0 }}, index0={{ loop.index0 }}, val={{ i }})"
+ "{% endfor %}]"
)
def test_loopcontext0():
in_lst = []
- l = LoopContext(reversed(in_lst), None)
- assert l.length == len(in_lst)
+ lc = LoopContext(reversed(in_lst), None)
+ assert lc.length == len(in_lst)
def test_loopcontext1():
in_lst = [10]
- l = LoopContext(reversed(in_lst), None)
- assert l.length == len(in_lst)
+ lc = LoopContext(reversed(in_lst), None)
+ assert lc.length == len(in_lst)
def test_loopcontext2():
in_lst = [10, 11]
- l = LoopContext(reversed(in_lst), None)
- assert l.length == len(in_lst)
+ lc = LoopContext(reversed(in_lst), None)
+ assert lc.length == len(in_lst)
def test_iterator_not_advanced_early():
class PublicStuff(object):
- bar = lambda self: 23
- _foo = lambda self: 42
+ def bar(self):
+ return 23
+
+ def _foo(self):
+ return 42
def __repr__(self):
return "PublicStuff"
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)' }}"
+ "{{ 'us-west-1' is matching '(us-east-1|ap-northeast-1)'"
+ " or 'stage' is matching '(dev|stage)' }}"
)
assert tmpl.render() == "False"
assert items == [
cache = LRUCache(3)
cache["b"] = 1
cache["a"] = 2
- values = [v for v in cache.itervalues()]
+ values = [v for v in cache.values()]
assert len(values) == 2
assert 1 in values
assert 2 in values
def test_itervalues_empty(self):
cache = LRUCache(2)
- values = [v for v in cache.itervalues()]
+ values = [v for v in cache.values()]
assert len(values) == 0
def test_pickleable(self):
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.values() == list(d.itervalues()) == [3, 2, 1]
+ assert d.items() == [("c", 3), ("b", 2), ("a", 1)]
+ assert d.keys() == ["c", "b", "a"]
+ assert d.values() == [3, 2, 1]
assert list(reversed(d)) == ["a", "b", "c"]
# Change the cache a little
d["b"]
d["a"] = 4
- assert d.items() == 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 d.items() == [("a", 4), ("b", 2), ("c", 3)]
+ assert d.keys() == ["a", "b", "c"]
+ assert d.values() == [4, 2, 3]
assert list(reversed(d)) == ["c", "b", "a"]
def test_setdefault(self):
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("foo.html")
+ assert func("foo.htm")
+ assert not func("foo.txt")
+ assert func("FOO.HTML")
+ assert not func("FOO.TXT")
@pytest.mark.utils