From: David Lord Date: Fri, 10 Jan 2020 15:46:18 +0000 (-0800) Subject: apply black X-Git-Tag: 2.11.0~9^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=04c8787155137206d58d6ee147d06482c1a8b598;p=thirdparty%2Fjinja.git apply black --- diff --git a/docs/cache_extension.py b/docs/cache_extension.py index 992b5951..387cd465 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 63d8a425..25dcb0b0 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( + """\ \ -""").render()) +""" + ).render() +) diff --git a/examples/basic/debugger.py b/examples/basic/debugger.py index b74125b0..d3c1a60a 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 647f42cd..a3073a5e 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 04a6adc7..80b9d1f0 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"""\ {{ 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 c18731c1..673b67ed 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 d5b908b8..39be08d6 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( + """\ 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 bbe445a1..fda8f7a1 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 7d988cd9..473928b9 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( + """\ @@ -50,17 +51,21 @@ jinja_template = JinjaEnvironment( \ -""") +""" +) + 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( + """\ @@ -92,19 +97,23 @@ else: \ -""") +""" + ) 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( + """\ @@ -132,20 +141,26 @@ else: \ -""") +""" + ) 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( + """\ @@ -173,17 +188,20 @@ else: \ -""") +""" + ) 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( + """\ ${page_title} @@ -207,17 +225,20 @@ else: \ -""") +""" + ) 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 @@ -246,18 +267,22 @@ else: \ -""", 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( + """\ @@ -285,19 +310,23 @@ else: \ -""") +""" + ) 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( + """\ @@ -325,8 +354,12 @@ else: \ -""", '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( + """\ Page Title @@ -358,23 +392,27 @@ else: \ -""") +""" + ) 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( + """\ ${page_title} @@ -395,40 +433,63 @@ else: \ -""", 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' % test) + t = Timer(setup="from __main__ import test_%s as bench" % test, stmt="bench()") + sys.stdout.write(" >> %-20s" % 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 e6deb47f..b16d99f2 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 = """\ \ """ 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 06897e5f..0052aefc 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 af5d40bb..957216af 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' % 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" % 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 e9c19b29..25b116e5 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 = '' + name = "" 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 2d4ab9ad..9bd6b97d 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 3c0c4256..48c7cf6a 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 fd545b75..50a06711 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 7db5f4a4..96fedba6 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 4f04436c..96628ddd 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 0b158dd7..cd6ce23d 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 55d8e35c..02aed34d 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 2eac35d5..0a93e956 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 967dcb49..451d3f8b 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 f7da273b..b2fd6e47 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 6466df6e..c2b877e6 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 308042ac..324bc0ff 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 8d6a6a71..36da88bf 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 23e891d4..bb366094 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 135ef81f..8ee52bb2 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 252d7eb2..7c4d26a7 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 = '