]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
apply pyupgrade and f-strings
authorDavid Lord <davidism@gmail.com>
Wed, 29 Jan 2020 04:16:59 +0000 (20:16 -0800)
committerDavid Lord <davidism@gmail.com>
Wed, 5 Feb 2020 16:44:15 +0000 (08:44 -0800)
55 files changed:
docs/api.rst
docs/conf.py
docs/examples/cache_extension.py
docs/examples/inline_gettext_extension.py
examples/basic/cycle.py
examples/basic/debugger.py
examples/basic/inheritance.py
examples/basic/test.py
examples/basic/test_filter_and_linestatements.py
examples/basic/test_loop_filter.py
examples/basic/translate.py
scripts/generate_identifier_pattern.py
src/jinja2/asyncfilters.py
src/jinja2/asyncsupport.py
src/jinja2/bccache.py
src/jinja2/compiler.py
src/jinja2/constants.py
src/jinja2/debug.py
src/jinja2/defaults.py
src/jinja2/environment.py
src/jinja2/exceptions.py
src/jinja2/ext.py
src/jinja2/filters.py
src/jinja2/idtracking.py
src/jinja2/lexer.py
src/jinja2/loaders.py
src/jinja2/meta.py
src/jinja2/nativetypes.py
src/jinja2/nodes.py
src/jinja2/optimizer.py
src/jinja2/parser.py
src/jinja2/runtime.py
src/jinja2/sandbox.py
src/jinja2/tests.py
src/jinja2/utils.py
src/jinja2/visitor.py
tests/conftest.py
tests/test_api.py
tests/test_async.py
tests/test_bytecode_cache.py
tests/test_core_tags.py
tests/test_debug.py
tests/test_ext.py
tests/test_features.py
tests/test_filters.py
tests/test_idtracking.py
tests/test_imports.py
tests/test_inheritance.py
tests/test_lexnparse.py
tests/test_loader.py
tests/test_regression.py
tests/test_runtime.py
tests/test_security.py
tests/test_tests.py
tests/test_utils.py

index 501a2c6d10463198e20fa8fa9dfdce8f3ea86539..075bcc6a86dc7c511a82880d3da8dd2f6c5e1d96 100644 (file)
@@ -675,12 +675,14 @@ enabled::
     import re
     from jinja2 import evalcontextfilter, Markup, escape
 
-    _paragraph_re = re.compile(r'(?:\r\n|\r(?!\n)|\n){2,}')
+    _paragraph_re = re.compile(r"(?:\r\n|\r(?!\n)|\n){2,}")
 
     @evalcontextfilter
     def nl2br(eval_ctx, value):
-        result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', Markup('<br>\n'))
-                              for p in _paragraph_re.split(escape(value)))
+        result = "\n\n".join(
+            f"<p>{p.replace('\n', Markup('<br>\n'))}</p>"
+            for p in _paragraph_re.split(escape(value))
+        )
         if eval_ctx.autoescape:
             result = Markup(result)
         return result
index 01e530dc89432237bfda5bbb204e5ac6d358ff54..783bae20b07375c81160584913e2234e4f93365f 100644 (file)
@@ -42,11 +42,9 @@ singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]}
 html_static_path = ["_static"]
 html_favicon = "_static/jinja-logo-sidebar.png"
 html_logo = "_static/jinja-logo-sidebar.png"
-html_title = "Jinja Documentation ({})".format(version)
+html_title = f"Jinja Documentation ({version})"
 html_show_sourcelink = False
 
 # LaTeX ----------------------------------------------------------------
 
-latex_documents = [
-    (master_doc, "Jinja-{}.tex".format(version), html_title, author, "manual")
-]
+latex_documents = [(master_doc, f"Jinja-{version}.tex", html_title, author, "manual")]
index 387cd465790be78c258304c67b24962e949ef73f..46af67ce04d0f3fd1ff35b898cbe3ade175a0795 100644 (file)
@@ -7,7 +7,7 @@ class FragmentCacheExtension(Extension):
     tags = {"cache"}
 
     def __init__(self, environment):
-        super(FragmentCacheExtension, self).__init__(environment)
+        super().__init__(environment)
 
         # add the defaults to the environment
         environment.extend(fragment_cache_prefix="", fragment_cache=None)
index 47bc9cc1c27f78db5777f6a378748c23c8ebe377..d75119cfc038d414cfb76da4571f3e142e418b23 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import re
 
 from jinja2.exceptions import TemplateSyntaxError
@@ -54,7 +53,7 @@ class InlineGettext(Extension):
                 else:
                     if gtok == "(" or paren_stack > 1:
                         yield Token(lineno, "data", gtok)
-                    paren_stack += gtok == ")" and -1 or 1
+                    paren_stack += -1 if gtok == ")" else 1
                     if not paren_stack:
                         yield Token(lineno, "block_begin", None)
                         yield Token(lineno, "name", "endtrans")
index 25dcb0b090ec59ebaf6c2515c95617fc135b0ac2..1f97e37945ca3968644a6f4586836abd8aa0e7d8 100644 (file)
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 from jinja2 import Environment
 
 env = Environment(
index d3c1a60a7ae82a5e928408c314e28cc7504fd618..f6a9627084085144c61a002a57502891f08c4efb 100644 (file)
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 from jinja2 import Environment
 from jinja2.loaders import FileSystemLoader
 
index 4a881bf8a87c1a0d826aefc3793612a05c294bc6..6d928df9e9510b417153000155aa821436ad891e 100644 (file)
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 from jinja2 import Environment
 from jinja2.loaders import DictLoader
 
index 80b9d1f052b335629a850fc072f323eeaebcb845..d34b0ffff7ea1c95393d175f7bbe028e7467e6bf 100644 (file)
@@ -1,12 +1,10 @@
-from __future__ import print_function
-
 from jinja2 import Environment
 from jinja2.loaders import DictLoader
 
 env = Environment(
     loader=DictLoader(
         {
-            "child.html": u"""\
+            "child.html": """\
 {% extends master_layout or 'master.html' %}
 {% include helpers = 'helpers.html' %}
 {% macro get_the_answer() %}42{% endmacro %}
@@ -16,12 +14,12 @@ env = Environment(
     {{ helpers.conspirate() }}
 {% endblock %}
 """,
-            "master.html": u"""\
+            "master.html": """\
 <!doctype html>
 <title>{{ title }}</title>
 {% block body %}{% endblock %}
 """,
-            "helpers.html": u"""\
+            "helpers.html": """\
 {% macro conspirate() %}23{% endmacro %}
 """,
         }
index 673b67ed76aea84d1b49a4f1a3b0a41ccffec9b3..9bbcbcaffbb02956e408bfa7ad91e150f95a0809 100644 (file)
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 from jinja2 import Environment
 
 env = Environment(
index 39be08d61c2b8eb8cedb4ca109f97cd778d0b374..6bd89fde0045ef7f34f93f35f8521b01698f0cde 100644 (file)
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 from jinja2 import Environment
 
 tmpl = Environment().from_string(
index 71547f4649cad60083814a4a1718cdf0e448928e..e6596817c2160ec83fc2e44daf2489ad77b22580 100644 (file)
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 from jinja2 import Environment
 
 env = Environment(extensions=["jinja2.ext.i18n"])
@@ -7,7 +5,7 @@ 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]
+}[s if n == 1 else p]
 print(
     env.from_string(
         """\
index 5813199813c8af957167a7b12baf275b5f87b9b9..6b47953577e15b32543a7e3c06b47e16afb2ac0c 100755 (executable)
@@ -1,12 +1,8 @@
-#!/usr/bin/env python3
 import itertools
 import os
 import re
 import sys
 
-if sys.version_info[0] < 3:
-    raise RuntimeError("This needs to run on Python 3.")
-
 
 def get_characters():
     """Find every Unicode character that is valid in a Python `identifier`_ but
@@ -52,7 +48,7 @@ def build_pattern(ranges):
             out.append(a)
             out.append(b)
         else:
-            out.append("{}-{}".format(a, b))
+            out.append(f"{a}-{b}")
 
     return "".join(out)
 
@@ -70,7 +66,7 @@ def main():
         f.write("import re\n\n")
         f.write("# generated by scripts/generate_identifier_pattern.py\n")
         f.write("pattern = re.compile(\n")
-        f.write('    r"[\\w{}]+"  # noqa: B950\n'.format(pattern))
+        f.write(f'    r"[\\w{pattern}]+"  # noqa: B950\n')
         f.write(")\n")
 
 
index 3caee858090cbb7054d9cd5f130052ae683b9416..0aad12c81860e9fe73e876b84431ddd833cd24b6 100644 (file)
@@ -84,7 +84,7 @@ async def do_groupby(environment, value, attribute):
 
 
 @asyncfiltervariant(filters.do_join)
-async def do_join(eval_ctx, value, d=u"", attribute=None):
+async def do_join(eval_ctx, value, d="", attribute=None):
     return filters.do_join(eval_ctx, await auto_to_seq(value), d, attribute)
 
 
index d7d6259a31c5e9e29006bfcea5e9a987f0378e59..3aef7ad233cc7ad68b8530e4d21249f6a5ed3de7 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """The code for async support. Importing this patches Jinja."""
 import asyncio
 import inspect
index b328b3b08aba0e78cece8921add4b5a4ffda0dd7..70b99c53df5f315f2cf74ce8272f8f47fc96febd 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """The optional bytecode cache system. This is useful if you have very
 complex template situations and the compilation of all those templates
 slows down your application too much.
@@ -30,7 +29,7 @@ bc_magic = (
 )
 
 
-class Bucket(object):
+class Bucket:
     """Buckets are used to store the bytecode for one template.  It's created
     and initialized by the bytecode cache and passed to the loading functions.
 
@@ -87,7 +86,7 @@ class Bucket(object):
         return out.getvalue()
 
 
-class BytecodeCache(object):
+class BytecodeCache:
     """To implement your own bytecode cache you have to subclass this class
     and override :meth:`load_bytecode` and :meth:`dump_bytecode`.  Both of
     these methods are passed a :class:`~jinja2.bccache.Bucket`.
@@ -205,7 +204,7 @@ class FileSystemBytecodeCache(BytecodeCache):
         if not hasattr(os, "getuid"):
             _unsafe_dir()
 
-        dirname = "_jinja2-cache-%d" % os.getuid()
+        dirname = f"_jinja2-cache-{os.getuid()}"
         actual_dir = os.path.join(tmpdir, dirname)
 
         try:
@@ -237,7 +236,7 @@ class FileSystemBytecodeCache(BytecodeCache):
         return actual_dir
 
     def _get_cache_filename(self, bucket):
-        return os.path.join(self.directory, self.pattern % bucket.key)
+        return os.path.join(self.directory, self.pattern % (bucket.key,))
 
     def load_bytecode(self, bucket):
         f = open_if_exists(self._get_cache_filename(bucket), "rb")
@@ -260,7 +259,7 @@ class FileSystemBytecodeCache(BytecodeCache):
         # normally.
         from os import remove
 
-        files = fnmatch.filter(os.listdir(self.directory), self.pattern % "*")
+        files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",))
         for filename in files:
             try:
                 remove(os.path.join(self.directory, filename))
index 8b7203bb4d02329c81c73aa13f32e8429b0f8e79..045a3a88dca30be63aeb97042a3f2d8e09521786 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Compiles nodes from the parser into Python code."""
 from collections import namedtuple
 from functools import update_wrapper
@@ -89,7 +88,7 @@ def find_undeclared(nodes, names):
     return visitor.undeclared
 
 
-class MacroRef(object):
+class MacroRef:
     def __init__(self, node):
         self.node = node
         self.accesses_caller = False
@@ -97,12 +96,12 @@ class MacroRef(object):
         self.accesses_varargs = False
 
 
-class Frame(object):
+class Frame:
     """Holds compile time information for us."""
 
     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.symbols if parent else None, level=level)
 
         # a toplevel frame is the root + soft frames such as if conditions.
         self.toplevel = False
@@ -123,7 +122,7 @@ class Frame(object):
         self.buffer = None
 
         # the name of the block we're in, otherwise None.
-        self.block = parent and parent.block or None
+        self.block = parent.block if parent else None
 
         # the parent of this frame
         self.parent = parent
@@ -286,12 +285,12 @@ class CodeGenerator(NodeVisitor):
     def temporary_identifier(self):
         """Get a new unique identifier."""
         self._last_identifier += 1
-        return "t_%d" % self._last_identifier
+        return f"t_{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(f"{frame.buffer} = []")
 
     def return_buffer_contents(self, frame, force_unescaped=False):
         """Return the buffer contents of the frame."""
@@ -299,17 +298,17 @@ class CodeGenerator(NodeVisitor):
             if frame.eval_ctx.volatile:
                 self.writeline("if context.eval_ctx.autoescape:")
                 self.indent()
-                self.writeline("return Markup(concat(%s))" % frame.buffer)
+                self.writeline(f"return Markup(concat({frame.buffer}))")
                 self.outdent()
                 self.writeline("else:")
                 self.indent()
-                self.writeline("return concat(%s)" % frame.buffer)
+                self.writeline(f"return concat({frame.buffer})")
                 self.outdent()
                 return
             elif frame.eval_ctx.autoescape:
-                self.writeline("return Markup(concat(%s))" % frame.buffer)
+                self.writeline(f"return Markup(concat({frame.buffer}))")
                 return
-        self.writeline("return concat(%s)" % frame.buffer)
+        self.writeline(f"return concat({frame.buffer})")
 
     def indent(self):
         """Indent by one."""
@@ -324,7 +323,7 @@ class CodeGenerator(NodeVisitor):
         if frame.buffer is None:
             self.writeline("yield ", node)
         else:
-            self.writeline("%s.append(" % frame.buffer, node)
+            self.writeline(f"{frame.buffer}.append(", node)
 
     def end_write(self, frame):
         """End the writing process started by `start_write`."""
@@ -399,7 +398,7 @@ class CodeGenerator(NodeVisitor):
                 self.visit(kwarg, frame)
             if extra_kwargs is not None:
                 for key, value in extra_kwargs.items():
-                    self.write(", %s=%s" % (key, value))
+                    self.write(f", {key}={value}")
         if node.dyn_args:
             self.write(", *")
             self.visit(node.dyn_args, frame)
@@ -410,12 +409,12 @@ class CodeGenerator(NodeVisitor):
             else:
                 self.write(", **{")
             for kwarg in node.kwargs:
-                self.write("%r: " % kwarg.key)
+                self.write(f"{kwarg.key!r}: ")
                 self.visit(kwarg.value, frame)
                 self.write(", ")
             if extra_kwargs is not None:
                 for key, value in extra_kwargs.items():
-                    self.write("%r: %s, " % (key, value))
+                    self.write(f"{key!r}: {value}, ")
             if node.dyn_kwargs is not None:
                 self.write("}, **")
                 self.visit(node.dyn_kwargs, frame)
@@ -437,9 +436,7 @@ class CodeGenerator(NodeVisitor):
             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(f"{mapping[name]} = environment.{dependency}[{name!r}]")
 
     def enter_frame(self, frame):
         undefs = []
@@ -447,15 +444,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(f"{target} = {self.get_resolve_func()}({param!r})")
             elif action == VAR_LOAD_ALIAS:
-                self.writeline("%s = %s" % (target, param))
+                self.writeline(f"{target} = {param}")
             elif action == VAR_LOAD_UNDEFINED:
                 undefs.append(target)
             else:
                 raise NotImplementedError("unknown load instruction")
         if undefs:
-            self.writeline("%s = missing" % " = ".join(undefs))
+            self.writeline(f"{' = '.join(undefs)} = missing")
 
     def leave_frame(self, frame, with_python_scope=False):
         if not with_python_scope:
@@ -463,12 +460,12 @@ class CodeGenerator(NodeVisitor):
             for target in frame.symbols.loads:
                 undefs.append(target)
             if undefs:
-                self.writeline("%s = missing" % " = ".join(undefs))
+                self.writeline(f"{' = '.join(undefs)} = missing")
 
     def func(self, name):
         if self.environment.is_async:
-            return "async def %s" % name
-        return "def %s" % name
+            return f"async def {name}"
+        return f"def {name}"
 
     def macro_body(self, node, frame):
         """Dump the function def of a macro or call block."""
@@ -518,7 +515,7 @@ class CodeGenerator(NodeVisitor):
         # 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(f"{self.func('macro')}({', '.join(args)}):", node)
         self.indent()
 
         self.buffer(frame)
@@ -527,17 +524,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(f"if {ref} is missing:")
             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)
+                    f'{ref} = undefined("parameter {arg.name!r} was not provided",'
+                    f" name={arg.name!r})"
                 )
             else:
-                self.writeline("%s = " % ref)
+                self.writeline(f"{ref} = ")
                 self.visit(default, frame)
             self.mark_parameter_stored(ref)
             self.outdent()
@@ -557,29 +554,24 @@ class CodeGenerator(NodeVisitor):
         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,
-            )
+            f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
+            f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
+            f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
         )
 
     def position(self, node):
         """Return a human readable position for the node."""
-        rv = "line %d" % node.lineno
+        rv = f"line {node.lineno}"
         if self.name is not None:
-            rv += " in " + repr(self.name)
+            rv = f"{rv} in {self.name!r}"
         return rv
 
     def dump_local_context(self, frame):
-        return "{%s}" % ", ".join(
-            "%r: %s" % (name, target)
+        items_kv = ", ".join(
+            f"{name!r}: {target}"
             for name, target in frame.symbols.dump_stores().items()
         )
+        return f"{{{items_kv}}}"
 
     def write_commons(self):
         """Writes a common preamble that is used by root and block functions.
@@ -626,13 +618,10 @@ class CodeGenerator(NodeVisitor):
         target = self._context_reference_stack[-1]
         if target == "context":
             return "resolve"
-        return "%s.resolve" % target
+        return f"{target}.resolve"
 
     def derive_context(self, frame):
-        return "%s.derived(%s)" % (
-            self.get_context_ref(),
-            self.dump_local_context(frame),
-        )
+        return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
 
     def parameter_is_undeclared(self, target):
         """Checks if a given target is an undeclared parameter."""
@@ -655,23 +644,21 @@ class CodeGenerator(NodeVisitor):
         if len(vars) == 1:
             name = next(iter(vars))
             ref = frame.symbols.ref(name)
-            self.writeline("context.vars[%r] = %s" % (name, ref))
+            self.writeline(f"context.vars[{name!r}] = {ref}")
         else:
             self.writeline("context.vars.update({")
             for idx, name in enumerate(vars):
                 if idx:
                     self.write(", ")
                 ref = frame.symbols.ref(name)
-                self.write("%r: %s" % (name, ref))
+                self.write(f"{name!r}: {ref}")
             self.write("})")
         if public_names:
             if len(public_names) == 1:
-                self.writeline("context.exported_vars.add(%r)" % public_names[0])
+                self.writeline(f"context.exported_vars.add({public_names[0]!r})")
             else:
-                self.writeline(
-                    "context.exported_vars.update((%s))"
-                    % ", ".join(map(repr, public_names))
-                )
+                names_str = ", ".join(map(repr, public_names))
+                self.writeline(f"context.exported_vars.update(({names_str}))")
 
     # -- Statement Visitors
 
@@ -692,7 +679,7 @@ class CodeGenerator(NodeVisitor):
 
         # 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 = "" if self.defer_init else ", environment=environment"
 
         # do we have an extends tag at all?  If not, we can save some
         # overhead by just not processing any inheritance code.
@@ -701,7 +688,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(f"block {block.name!r} defined twice", block.lineno)
             self.blocks[block.name] = block
 
         # find all imports and import them
@@ -711,16 +698,16 @@ class CodeGenerator(NodeVisitor):
                 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))
+                    self.writeline(f"from {module} import {obj} as {alias}")
                 else:
-                    self.writeline("import %s as %s" % (imp, alias))
+                    self.writeline(f"import {imp} as {alias}")
 
         # add the load name
-        self.writeline("name = %r" % self.name)
+        self.writeline(f"name = {self.name!r}")
 
         # generate the root render function.
         self.writeline(
-            "%s(context, missing=missing%s):" % (self.func("root"), envenv), extra=1
+            f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
         )
         self.indent()
         self.write_commons()
@@ -729,7 +716,7 @@ class CodeGenerator(NodeVisitor):
         frame = Frame(eval_ctx)
         if "self" in find_undeclared(node.body, ("self",)):
             ref = frame.symbols.declare_parameter("self")
-            self.writeline("%s = TemplateReference(context)" % ref)
+            self.writeline(f"{ref} = TemplateReference(context)")
         frame.symbols.analyze_node(node)
         frame.toplevel = frame.rootlevel = True
         frame.require_output_check = have_extends and not self.has_known_extends
@@ -750,10 +737,9 @@ class CodeGenerator(NodeVisitor):
             if not self.environment.is_async:
                 self.writeline("yield from parent_template.root_render_func(context)")
             else:
+                loop = "async for" if self.environment.is_async else "for"
                 self.writeline(
-                    "%sfor event in parent_template."
-                    "root_render_func(context):"
-                    % (self.environment.is_async and "async " or "")
+                    f"{loop} event in parent_template.root_render_func(context):"
                 )
                 self.indent()
                 self.writeline("yield event")
@@ -763,8 +749,7 @@ class CodeGenerator(NodeVisitor):
         # at this point we now have the blocks collected and can visit them too.
         for name, block in self.blocks.items():
             self.writeline(
-                "%s(context, missing=missing%s):"
-                % (self.func("block_" + name), envenv),
+                f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
                 block,
                 1,
             )
@@ -777,10 +762,10 @@ class CodeGenerator(NodeVisitor):
             undeclared = find_undeclared(block.body, ("self", "super"))
             if "self" in undeclared:
                 ref = block_frame.symbols.declare_parameter("self")
-                self.writeline("%s = TemplateReference(context)" % ref)
+                self.writeline(f"{ref} = TemplateReference(context)")
             if "super" in undeclared:
                 ref = block_frame.symbols.declare_parameter("super")
-                self.writeline("%s = context.super(%r, block_%s)" % (ref, name, name))
+                self.writeline(f"{ref} = context.super({name!r}, block_{name})")
             block_frame.symbols.analyze_node(block)
             block_frame.block = name
             self.enter_frame(block_frame)
@@ -789,15 +774,10 @@ 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,
-        )
-
-        # add a function that returns the debug info
-        self.writeline(
-            "debug_info = %r" % "&".join("%s=%s" % x for x in self.debug_info)
-        )
+        blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
+        self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
+        debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
+        self.writeline(f"debug_info = {debug_kv_str!r}")
 
     def visit_Block(self, node, frame):
         """Call a block and register it for the template."""
@@ -819,13 +799,12 @@ class CodeGenerator(NodeVisitor):
 
         if not self.environment.is_async and frame.buffer is None:
             self.writeline(
-                "yield from context.blocks[%r][0](%s)" % (node.name, context), node
+                f"yield from context.blocks[{node.name!r}][0]({context})", node
             )
         else:
-            loop = self.environment.is_async and "async for" or "for"
+            loop = "async for" if self.environment.is_async else "for"
             self.writeline(
-                "%s event in context.blocks[%r][0](%s):" % (loop, node.name, context),
-                node,
+                f"{loop} event in context.blocks[{node.name!r}][0]({context}):", node
             )
             self.indent()
             self.simple_write("event", frame)
@@ -850,7 +829,7 @@ class CodeGenerator(NodeVisitor):
             if not self.has_known_extends:
                 self.writeline("if parent_template is not None:")
                 self.indent()
-            self.writeline("raise TemplateRuntimeError(%r)" % "extended multiple times")
+            self.writeline('raise TemplateRuntimeError("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.
@@ -861,7 +840,7 @@ class CodeGenerator(NodeVisitor):
 
         self.writeline("parent_template = environment.get_template(", node)
         self.visit(node.template, frame)
-        self.write(", %r)" % self.name)
+        self.write(f", {self.name!r})")
         self.writeline("for name, parent_block in parent_template.blocks.items():")
         self.indent()
         self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
@@ -891,9 +870,9 @@ class CodeGenerator(NodeVisitor):
         elif isinstance(node.template, (nodes.Tuple, nodes.List)):
             func_name = "select_template"
 
-        self.writeline("template = environment.%s(" % func_name, node)
+        self.writeline(f"template = environment.{func_name}(", node)
         self.visit(node.template, frame)
-        self.write(", %r)" % self.name)
+        self.write(f", {self.name!r})")
         if node.ignore_missing:
             self.outdent()
             self.writeline("except TemplateNotFound:")
@@ -905,16 +884,15 @@ class CodeGenerator(NodeVisitor):
 
         skip_event_yield = False
         if node.with_context:
-            loop = self.environment.is_async and "async for" or "for"
+            loop = "async for" if self.environment.is_async else "for"
             self.writeline(
-                "%s event in template.root_render_func("
-                "template.new_context(context.get_all(), True, "
-                "%s)):" % (loop, self.dump_local_context(frame))
+                f"{loop} event in template.root_render_func("
+                "template.new_context(context.get_all(), True,"
+                f" {self.dump_local_context(frame)})):"
             )
         elif self.environment.is_async:
             self.writeline(
-                "for event in (await "
-                "template._get_default_module_async())"
+                "for event in (await template._get_default_module_async())"
                 "._body_stream:"
             )
         else:
@@ -931,45 +909,37 @@ class CodeGenerator(NodeVisitor):
 
     def visit_Import(self, node, frame):
         """Visit regular imports."""
-        self.writeline("%s = " % frame.symbols.ref(node.target), node)
+        self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
         if frame.toplevel:
-            self.write("context.vars[%r] = " % node.target)
+            self.write(f"context.vars[{node.target!r}] = ")
         if self.environment.is_async:
             self.write("await ")
         self.write("environment.get_template(")
         self.visit(node.template, frame)
-        self.write(", %r)." % self.name)
+        self.write(f", {self.name!r}).")
         if node.with_context:
+            func = "make_module" + ("_async" if self.environment.is_async else "")
             self.write(
-                "make_module%s(context.get_all(), True, %s)"
-                % (
-                    self.environment.is_async and "_async" or "",
-                    self.dump_local_context(frame),
-                )
+                f"{func}(context.get_all(), True, {self.dump_local_context(frame)})"
             )
         elif self.environment.is_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.writeline(f"context.exported_vars.discard({node.target!r})")
 
     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 "")
-        )
+        prefix = "await " if self.environment.is_async else ""
+        self.write(f"included_template = {prefix}environment.get_template(")
         self.visit(node.template, frame)
-        self.write(", %r)." % self.name)
+        self.write(f", {self.name!r}).")
         if node.with_context:
+            func = "make_module" + ("_async" if self.environment.is_async else "")
             self.write(
-                "make_module%s(context.get_all(), True, %s)"
-                % (
-                    self.environment.is_async and "_async" or "",
-                    self.dump_local_context(frame),
-                )
+                f"{func}(context.get_all(), True, {self.dump_local_context(frame)})"
             )
         elif self.environment.is_async:
             self.write("_get_default_module_async()")
@@ -984,22 +954,18 @@ class CodeGenerator(NodeVisitor):
             else:
                 alias = name
             self.writeline(
-                "%s = getattr(included_template, "
-                "%r, missing)" % (frame.symbols.ref(alias), name)
+                f"{frame.symbols.ref(alias)} ="
+                f" getattr(included_template, {name!r}, missing)"
             )
-            self.writeline("if %s is missing:" % frame.symbols.ref(alias))
+            self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
             self.indent()
+            message = (
+                "the template {included_template.__name__!r}"
+                f" (imported on {self.position(node)})"
+                f" does not export the requested name {name!r}"
+            )
             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,
-                )
+                f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
             )
             self.outdent()
             if frame.toplevel:
@@ -1010,23 +976,19 @@ class CodeGenerator(NodeVisitor):
         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(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
             else:
-                self.writeline(
-                    "context.vars.update({%s})"
-                    % ", ".join(
-                        "%r: %s" % (name, frame.symbols.ref(name)) for name in var_names
-                    )
+                names_kv = ", ".join(
+                    f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
                 )
+                self.writeline(f"context.vars.update({{{names_kv}}})")
         if discarded_names:
             if len(discarded_names) == 1:
-                self.writeline("context.exported_vars.discard(%r)" % discarded_names[0])
+                self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
             else:
+                names_str = ", ".join(map(repr, discarded_names))
                 self.writeline(
-                    "context.exported_vars.difference_"
-                    "update((%s))" % ", ".join(map(repr, discarded_names))
+                    f"context.exported_vars.difference_update(({names_str}))"
                 )
 
     def visit_For(self, node, frame):
@@ -1052,13 +1014,13 @@ class CodeGenerator(NodeVisitor):
         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)
+            self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
             self.indent()
             self.enter_frame(test_frame)
-            self.writeline(self.environment.is_async and "async for " or "for ")
+            self.writeline("async for " if self.environment.is_async else "for ")
             self.visit(node.target, loop_frame)
             self.write(" in ")
-            self.write(self.environment.is_async and "auto_aiter(fiter)" or "fiter")
+            self.write("auto_aiter(fiter)" if self.environment.is_async else "fiter")
             self.write(":")
             self.indent()
             self.writeline("if ", node.test)
@@ -1075,7 +1037,7 @@ class CodeGenerator(NodeVisitor):
         # 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
+                f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
             )
             self.indent()
             self.buffer(loop_frame)
@@ -1086,7 +1048,7 @@ 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(f"{loop_ref} = missing")
 
         for name in node.find_all(nodes.Name):
             if name.ctx == "store" and name.name == "loop":
@@ -1097,20 +1059,18 @@ class CodeGenerator(NodeVisitor):
 
         if node.else_:
             iteration_indicator = self.temporary_identifier()
-            self.writeline("%s = 1" % iteration_indicator)
+            self.writeline(f"{iteration_indicator} = 1")
 
-        self.writeline(self.environment.is_async and "async for " or "for ", node)
+        self.writeline("async for " if self.environment.is_async else "for ", node)
         self.visit(node.target, loop_frame)
         if extended_loop:
-            if self.environment.is_async:
-                self.write(", %s in AsyncLoopContext(" % loop_ref)
-            else:
-                self.write(", %s in LoopContext(" % loop_ref)
+            prefix = "Async" if self.environment.is_async else ""
+            self.write(f", {loop_ref} in {prefix}LoopContext(")
         else:
             self.write(" in ")
 
         if node.test:
-            self.write("%s(" % loop_filter_func)
+            self.write(f"{loop_filter_func}(")
         if node.recursive:
             self.write("reciter")
         else:
@@ -1125,21 +1085,21 @@ class CodeGenerator(NodeVisitor):
         if node.recursive:
             self.write(", undefined, loop_render_func, depth):")
         else:
-            self.write(extended_loop and ", undefined):" or ":")
+            self.write(", undefined):" if extended_loop else ":")
 
         self.indent()
         self.enter_frame(loop_frame)
 
         self.blockvisit(node.body, loop_frame)
         if node.else_:
-            self.writeline("%s = 0" % iteration_indicator)
+            self.writeline(f"{iteration_indicator} = 0")
         self.outdent()
         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(f"if {iteration_indicator}:")
             self.indent()
             self.enter_frame(else_frame)
             self.blockvisit(node.else_, else_frame)
@@ -1189,9 +1149,9 @@ class CodeGenerator(NodeVisitor):
         self.newline()
         if frame.toplevel:
             if not node.name.startswith("_"):
-                self.write("context.exported_vars.add(%r)" % node.name)
-            self.writeline("context.vars[%r] = " % node.name)
-        self.write("%s = " % frame.symbols.ref(node.name))
+                self.write(f"context.exported_vars.add({node.name!r})")
+            self.writeline(f"context.vars[{node.name!r}] = ")
+        self.write(f"{frame.symbols.ref(node.name)} = ")
         self.macro_def(macro_ref, macro_frame)
 
     def visit_CallBlock(self, node, frame):
@@ -1369,9 +1329,9 @@ class CodeGenerator(NodeVisitor):
 
         if frame.buffer is not None:
             if len(body) == 1:
-                self.writeline("%s.append(" % frame.buffer)
+                self.writeline(f"{frame.buffer}.append(")
             else:
-                self.writeline("%s.extend((" % frame.buffer)
+                self.writeline(f"{frame.buffer}.extend((")
 
             self.indent()
 
@@ -1430,7 +1390,7 @@ class CodeGenerator(NodeVisitor):
         if node.filter is not None:
             self.visit_Filter(node.filter, block_frame)
         else:
-            self.write("concat(%s)" % block_frame.buffer)
+            self.write(f"concat({block_frame.buffer})")
         self.write(")")
         self.pop_assign_tracking(frame)
         self.leave_frame(block_frame)
@@ -1454,8 +1414,7 @@ class CodeGenerator(NodeVisitor):
                 and not self.parameter_is_undeclared(ref)
             ):
                 self.write(
-                    "(undefined(name=%r) if %s is missing else %s)"
-                    % (node.name, ref, ref)
+                    f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
                 )
                 return
 
@@ -1466,14 +1425,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(f"if not isinstance({ref}, Namespace):")
         self.indent()
         self.writeline(
-            "raise TemplateRuntimeError(%r)"
-            % "cannot assign attribute on non-namespace object"
+            "raise TemplateRuntimeError"
+            '("cannot assign attribute on non-namespace object")'
         )
         self.outdent()
-        self.writeline("%s[%r]" % (ref, node.attr))
+        self.writeline(f"{ref}[{node.attr!r}]")
 
     def visit_Const(self, node, frame):
         val = node.as_const(frame.eval_ctx)
@@ -1487,7 +1446,7 @@ class CodeGenerator(NodeVisitor):
             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
+                f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
             )
 
     def visit_Tuple(self, node, frame):
@@ -1497,7 +1456,7 @@ class CodeGenerator(NodeVisitor):
             if idx:
                 self.write(", ")
             self.visit(item, frame)
-        self.write(idx == 0 and ",)" or ")")
+        self.write(",)" if idx == 0 else ")")
 
     def visit_List(self, node, frame):
         self.write("[")
@@ -1524,14 +1483,14 @@ class CodeGenerator(NodeVisitor):
                 self.environment.sandboxed
                 and operator in self.environment.intercepted_binops
             ):
-                self.write("environment.call_binop(context, %r, " % operator)
+                self.write(f"environment.call_binop(context, {operator!r}, ")
                 self.visit(node.left, frame)
                 self.write(", ")
                 self.visit(node.right, frame)
             else:
                 self.write("(")
                 self.visit(node.left, frame)
-                self.write(" %s " % operator)
+                self.write(f" {operator} ")
                 self.visit(node.right, frame)
             self.write(")")
 
@@ -1544,7 +1503,7 @@ class CodeGenerator(NodeVisitor):
                 self.environment.sandboxed
                 and operator in self.environment.intercepted_unops
             ):
-                self.write("environment.call_unop(context, %r, " % operator)
+                self.write(f"environment.call_unop(context, {operator!r}, ")
                 self.visit(node.node, frame)
             else:
                 self.write("(" + operator)
@@ -1570,12 +1529,12 @@ class CodeGenerator(NodeVisitor):
     @optimizeconst
     def visit_Concat(self, node, frame):
         if frame.eval_ctx.volatile:
-            func_name = "(context.eval_ctx.volatile and markup_join or str_join)"
+            func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
         elif frame.eval_ctx.autoescape:
             func_name = "markup_join"
         else:
             func_name = "str_join"
-        self.write("%s((" % func_name)
+        self.write(f"{func_name}((")
         for arg in node.nodes:
             self.visit(arg, frame)
             self.write(", ")
@@ -1590,7 +1549,7 @@ class CodeGenerator(NodeVisitor):
         self.write(")")
 
     def visit_Operand(self, node, frame):
-        self.write(" %s " % operators[node.op])
+        self.write(f" {operators[node.op]} ")
         self.visit(node.expr, frame)
 
     @optimizeconst
@@ -1600,7 +1559,7 @@ class CodeGenerator(NodeVisitor):
 
         self.write("environment.getattr(")
         self.visit(node.node, frame)
-        self.write(", %r)" % node.attr)
+        self.write(f", {node.attr!r})")
 
         if self.environment.is_async:
             self.write("))")
@@ -1643,7 +1602,7 @@ class CodeGenerator(NodeVisitor):
         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)
+            self.fail(f"no filter named {node.name!r}", node.lineno)
         if getattr(func, "contextfilter", False) is True:
             self.write("context, ")
         elif getattr(func, "evalcontextfilter", False) is True:
@@ -1657,13 +1616,13 @@ class CodeGenerator(NodeVisitor):
             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)
+                f"(Markup(concat({frame.buffer}))"
+                f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
             )
         elif frame.eval_ctx.autoescape:
-            self.write("Markup(concat(%s))" % frame.buffer)
+            self.write(f"Markup(concat({frame.buffer}))")
         else:
-            self.write("concat(%s)" % frame.buffer)
+            self.write(f"concat({frame.buffer})")
         self.signature(node, frame)
         self.write(")")
         if self.environment.is_async:
@@ -1673,7 +1632,7 @@ class CodeGenerator(NodeVisitor):
     def visit_Test(self, node, frame):
         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(f"no test named {node.name!r}", node.lineno)
         self.visit(node.node, frame)
         self.signature(node, frame)
         self.write(")")
@@ -1684,12 +1643,9 @@ class CodeGenerator(NodeVisitor):
             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)
-                )
+                f'cond_expr_undefined("the inline if-expression on'
+                f" {self.position(node)} evaluated to false and no else"
+                f' section was defined.")'
             )
 
         self.write("(")
@@ -1709,7 +1665,7 @@ class CodeGenerator(NodeVisitor):
         else:
             self.write("context.call(")
         self.visit(node.node, frame)
-        extra_kwargs = forward_caller and {"caller": "caller"} or None
+        extra_kwargs = {"caller": "caller"} if forward_caller else None
         self.signature(node, frame, extra_kwargs)
         self.write(")")
         if self.environment.is_async:
@@ -1727,7 +1683,7 @@ class CodeGenerator(NodeVisitor):
         self.write(")")
 
     def visit_MarkSafeIfAutoescape(self, node, frame):
-        self.write("(context.eval_ctx.autoescape and Markup or identity)(")
+        self.write("(Markup if context.eval_ctx.autoescape else identity)(")
         self.visit(node.expr, frame)
         self.write(")")
 
@@ -1735,7 +1691,7 @@ class CodeGenerator(NodeVisitor):
         self.write("environment." + node.name)
 
     def visit_ExtensionAttribute(self, node, frame):
-        self.write("environment.extensions[%r].%s" % (node.identifier, node.name))
+        self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
 
     def visit_ImportedName(self, node, frame):
         self.write(self.import_aliases[node.importname])
@@ -1764,8 +1720,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(f"{ctx} = {self.derive_context(frame)}")
+        self.writeline(f"{ctx}.vars = ")
         self.visit(node.context, frame)
         self.push_context_reference(ctx)
 
@@ -1778,7 +1734,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(f"context.eval_ctx.{keyword.key} = ")
             self.visit(keyword.value, frame)
             try:
                 val = keyword.value.as_const(frame.eval_ctx)
@@ -1790,9 +1746,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(f"{old_ctx_name} = context.eval_ctx.save()")
         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(f"context.eval_ctx.revert({old_ctx_name})")
index bf7f2ca721789052f1e227c5e3432e7712134c55..41a1c23b0a7fe134b1f662545876eb65b31b071e 100644 (file)
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
 #: list of lorem ipsum words used by the lipsum() helper function
-LOREM_IPSUM_WORDS = u"""\
+LOREM_IPSUM_WORDS = """\
 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
index a0efa29ab0d1637ef740b4605567b20e48374b56..46c24eba5c38fa4834d023c70f10ebd92fc85f06 100644 (file)
@@ -106,7 +106,7 @@ def fake_traceback(exc_value, tb, filename, lineno):
             if function == "root":
                 location = "top-level template code"
             elif function.startswith("block_"):
-                location = 'block "%s"' % function[6:]
+                location = f"block {function[6:]!r}"
 
         # Collect arguments for the new code object. CodeType only
         # accepts positional arguments, and arguments were inserted in
index 72a935789093eb94111b201143c81e9be0a4bce6..1f0b0ab00db25f3822276c7304035de082fd9772 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 from .filters import FILTERS as DEFAULT_FILTERS  # noqa: F401
 from .tests import TESTS as DEFAULT_TESTS  # noqa: F401
 from .utils import Cycler
index 247f7bf158cb8e55f0767ce22d08228a58184351..3c93c4843f867f3155943a91f8b34356c8a62d31 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Classes for managing templates and their runtime and compile time
 options.
 """
@@ -101,13 +100,14 @@ def load_extensions(environment, extensions):
     return result
 
 
-def fail_for_missing_callable(string, name):
-    msg = string % name
+def fail_for_missing_callable(thing, name):
+    msg = f"no {thing} named {name!r}"
+
     if isinstance(name, Undefined):
         try:
             name._fail_with_undefined_error()
         except Exception as e:
-            msg = "%s (%s; did you forget to quote the callable name?)" % (msg, e)
+            msg = f"{msg} ({e}; did you forget to quote the callable name?)"
     raise TemplateRuntimeError(msg)
 
 
@@ -121,15 +121,15 @@ def _environment_sanity_check(environment):
         != environment.variable_start_string
         != environment.comment_start_string
     ), "block, variable and comment start strings must be different"
-    assert environment.newline_sequence in (
+    assert environment.newline_sequence in {
         "\r",
         "\r\n",
         "\n",
-    ), "newline_sequence set to unknown line ending string."
+    }, "newline_sequence set to unknown line ending string."
     return environment
 
 
-class Environment(object):
+class Environment:
     r"""The core component of Jinja is the `Environment`.  It contains
     important shared variables like configuration, filters, tests,
     globals and others.  Instances of this class may be modified if
@@ -479,7 +479,7 @@ 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("filter", name)
         args = [value] + list(args or ())
         if getattr(func, "contextfilter", False) is True:
             if context is None:
@@ -505,7 +505,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("test", name)
         return func(value, *(args or ()), **(kwargs or {}))
 
     @internalcode
@@ -719,11 +719,11 @@ class Environment(object):
             zip_file = ZipFile(
                 target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]
             )
-            log_function('Compiling into Zip archive "%s"' % target)
+            log_function(f"Compiling into Zip archive {target!r}")
         else:
             if not os.path.isdir(target):
                 os.makedirs(target)
-            log_function('Compiling into folder "%s"' % target)
+            log_function(f"Compiling into folder {target!r}")
 
         try:
             for name in self.list_templates(extensions, filter_func):
@@ -733,13 +733,13 @@ class Environment(object):
                 except TemplateSyntaxError as e:
                     if not ignore_errors:
                         raise
-                    log_function('Could not compile "%s": %s' % (name, e))
+                    log_function(f'Could not compile "{name}": {e}')
                     continue
 
                 filename = ModuleLoader.get_module_filename(name)
 
                 write_file(filename, code)
-                log_function('Compiled "%s" as %s' % (name, filename))
+                log_function(f'Compiled "{name}" as {filename}')
         finally:
             if zip:
                 zip_file.close()
@@ -859,7 +859,7 @@ class Environment(object):
 
         if not names:
             raise TemplatesNotFound(
-                message=u"Tried to select from an empty list " u"of templates."
+                message="Tried to select from an empty list of templates."
             )
         globals = self.make_globals(globals)
         for name in names:
@@ -902,7 +902,7 @@ class Environment(object):
         return dict(self.globals, **d)
 
 
-class Template(object):
+class Template:
     """The central template object.  This class represents a compiled template
     and is used to evaluate it.
 
@@ -1074,8 +1074,7 @@ class Template(object):
         """
         vars = dict(*args, **kwargs)
         try:
-            for event in self.root_render_func(self.new_context(vars)):
-                yield event
+            yield from self.root_render_func(self.new_context(vars))
         except Exception:
             yield self.environment.handle_exception()
 
@@ -1168,13 +1167,13 @@ class Template(object):
 
     def __repr__(self):
         if self.name is None:
-            name = "memory:%x" % id(self)
+            name = f"memory:{id(self):x}"
         else:
             name = repr(self.name)
-        return "<%s %s>" % (self.__class__.__name__, name)
+        return f"<{self.__class__.__name__} {name}>"
 
 
-class TemplateModule(object):
+class TemplateModule:
     """Represents an imported template.  All the exported names of the
     template are available as attributes on this object.  Additionally
     converting it into a string renders the contents.
@@ -1202,13 +1201,13 @@ class TemplateModule(object):
 
     def __repr__(self):
         if self.__name__ is None:
-            name = "memory:%x" % id(self)
+            name = f"memory:{id(self):x}"
         else:
             name = repr(self.__name__)
-        return "<%s %s>" % (self.__class__.__name__, name)
+        return f"<{self.__class__.__name__} {name}>"
 
 
-class TemplateExpression(object):
+class TemplateExpression:
     """The :meth:`jinja2.Environment.compile_expression` method returns an
     instance of this object.  It encapsulates the expression-like access
     to the template with an expression it wraps.
@@ -1227,7 +1226,7 @@ class TemplateExpression(object):
         return rv
 
 
-class TemplateStream(object):
+class TemplateStream:
     """A template stream works pretty much like an ordinary python generator
     but it can buffer multiple items to reduce the number of total iterations.
     Per default the output is unbuffered which means that for every unbuffered
index 8d5e89d7ccc39ebbded5047eca6ee8b6767a5e0c..07cfba26499e36864d833f78a867c869ac6c95ee 100644 (file)
@@ -65,10 +65,10 @@ class TemplatesNotFound(TemplateNotFound):
                 else:
                     parts.append(name)
 
-            message = u"none of the templates given were found: " + u", ".join(
+            message = "none of the templates given were found: " + ", ".join(
                 map(str, parts)
             )
-        TemplateNotFound.__init__(self, names and names[-1] or None, message)
+        TemplateNotFound.__init__(self, names[-1] if names else None, message)
         self.templates = list(names)
 
 
@@ -92,10 +92,10 @@ class TemplateSyntaxError(TemplateError):
             return self.message
 
         # otherwise attach some stuff
-        location = "line %d" % self.lineno
+        location = f"line {self.lineno}"
         name = self.filename or self.name
         if name:
-            location = 'File "%s", %s' % (name, location)
+            location = f'File "{name}", {location}'
         lines = [self.message, "  " + location]
 
         # if the source is set, add the line to the output
@@ -107,7 +107,7 @@ class TemplateSyntaxError(TemplateError):
             if line:
                 lines.append("    " + line.strip())
 
-        return u"\n".join(lines)
+        return "\n".join(lines)
 
     def __reduce__(self):
         # https://bugs.python.org/issue1692335 Exceptions that take
index fd36e2d4c84e5725cf9ce4144467ceb8fb396bf9..7c36bb4306b8187a645f119633a7c7200c39e741 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Extension API for adding custom tags and behavior."""
 import pprint
 import re
@@ -40,7 +39,7 @@ class ExtensionRegistry(type):
 
     def __new__(mcs, name, bases, d):
         rv = type.__new__(mcs, name, bases, d)
-        rv.identifier = rv.__module__ + "." + rv.__name__
+        rv.identifier = f"{rv.__module__}.{rv.__name__}"
         return rv
 
 
@@ -203,7 +202,7 @@ class InternationalizationExtension(Extension):
 
     def _install_null(self, newstyle=None):
         self._install_callables(
-            lambda x: x, lambda s, p, n: (n != 1 and (p,) or (s,))[0], newstyle
+            lambda x: x, lambda s, p, n: s if n == 1 else p, newstyle
         )
 
     def _install_callables(self, gettext, ngettext, newstyle=None):
@@ -246,7 +245,7 @@ class InternationalizationExtension(Extension):
             name = parser.stream.expect("name")
             if name.value in variables:
                 parser.fail(
-                    "translatable variable %r defined twice." % name.value,
+                    f"translatable variable {name.value!r} defined twice.",
                     name.lineno,
                     exc=TemplateAssertionError,
                 )
@@ -294,7 +293,7 @@ class InternationalizationExtension(Extension):
                 name = parser.stream.expect("name")
                 if name.value not in variables:
                     parser.fail(
-                        "unknown variable %r for pluralization" % name.value,
+                        f"unknown variable {name.value!r} for pluralization",
                         name.lineno,
                         exc=TemplateAssertionError,
                     )
@@ -353,7 +352,7 @@ class InternationalizationExtension(Extension):
                 next(parser.stream)
                 name = parser.stream.expect("name").value
                 referenced.append(name)
-                buf.append("%%(%s)s" % name)
+                buf.append(f"%({name})s")
                 parser.stream.expect("variable_end")
             elif parser.stream.current.type == "block_begin":
                 next(parser.stream)
@@ -436,7 +435,7 @@ class ExprStmtExtension(Extension):
     that it doesn't print the return value.
     """
 
-    tags = set(["do"])
+    tags = {"do"}
 
     def parse(self, parser):
         node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
@@ -447,7 +446,7 @@ class ExprStmtExtension(Extension):
 class LoopControlExtension(Extension):
     """Adds break and continue to the template engine."""
 
-    tags = set(["break", "continue"])
+    tags = {"break", "continue"}
 
     def parse(self, parser):
         token = next(parser.stream)
@@ -575,7 +574,7 @@ def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True
         yield node.lineno, node.node.name, strings
 
 
-class _CommentFinder(object):
+class _CommentFinder:
     """Helper class to find comments in a token stream.  Can only
     find comments for gettext calls forwards.  Once the comment
     from line 4 is found, a comment for line 1 will not return a
index 0675f54f86ef4d91e19d61accad1bc4426e9c329..f39d389fd723e8852280bfa6ee0fe2600cbbbfdd 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Built-in template filters used with the ``|`` operator."""
 import math
 import random
@@ -153,9 +152,8 @@ def do_urlencode(value):
     else:
         items = iter(value)
 
-    return u"&".join(
-        "%s=%s" % (url_quote(k, for_qs=True), url_quote(v, for_qs=True))
-        for k, v in items
+    return "&".join(
+        f"{url_quote(k, for_qs=True)}={url_quote(v, for_qs=True)}" for k, v in items
     )
 
 
@@ -224,13 +222,13 @@ def do_xmlattr(_eval_ctx, d, autospace=True):
     As you can see it automatically prepends a space in front of the item
     if the filter returned something unless the second parameter is false.
     """
-    rv = u" ".join(
-        u'%s="%s"' % (escape(key), escape(value))
+    rv = " ".join(
+        f'{escape(key)}="{escape(value)}"'
         for key, value in d.items()
         if value is not None and not isinstance(value, Undefined)
     )
     if autospace and rv:
-        rv = u" " + rv
+        rv = " " + rv
     if _eval_ctx.autoescape:
         rv = Markup(rv)
     return rv
@@ -415,7 +413,7 @@ def do_max(environment, value, case_sensitive=False, attribute=None):
     return _min_or_max(environment, value, max, case_sensitive, attribute)
 
 
-def do_default(value, default_value=u"", boolean=False):
+def do_default(value, default_value="", boolean=False):
     """If the value is undefined it will return the passed default value,
     otherwise the value of the variable:
 
@@ -444,7 +442,7 @@ def do_default(value, default_value=u"", boolean=False):
 
 
 @evalcontextfilter
-def do_join(eval_ctx, value, d=u"", attribute=None):
+def do_join(eval_ctx, value, d="", attribute=None):
     """Return a string which is the concatenation of the strings in the
     sequence. The separator between elements is an empty string per
     default, you can define it with the optional parameter:
@@ -541,27 +539,27 @@ def do_filesizeformat(value, binary=False):
     prefixes are used (Mebi, Gibi).
     """
     bytes = float(value)
-    base = binary and 1024 or 1000
+    base = 1024 if binary else 1000
     prefixes = [
-        (binary and "KiB" or "kB"),
-        (binary and "MiB" or "MB"),
-        (binary and "GiB" or "GB"),
-        (binary and "TiB" or "TB"),
-        (binary and "PiB" or "PB"),
-        (binary and "EiB" or "EB"),
-        (binary and "ZiB" or "ZB"),
-        (binary and "YiB" or "YB"),
+        ("KiB" if binary else "kB"),
+        ("MiB" if binary else "MB"),
+        ("GiB" if binary else "GB"),
+        ("TiB" if binary else "TB"),
+        ("PiB" if binary else "PB"),
+        ("EiB" if binary else "EB"),
+        ("ZiB" if binary else "ZB"),
+        ("YiB" if binary else "YB"),
     ]
     if bytes == 1:
         return "1 Byte"
     elif bytes < base:
-        return "%d Bytes" % bytes
+        return f"{int(bytes)} Bytes"
     else:
         for i, prefix in enumerate(prefixes):
             unit = base ** (i + 2)
             if bytes < unit:
-                return "%.1f %s" % ((base * bytes / unit), prefix)
-        return "%.1f %s" % ((base * bytes / unit), prefix)
+                return f"{base * bytes / unit:.1f} {prefix}"
+        return f"{base * bytes / unit:.1f} {prefix}"
 
 
 def do_pprint(value):
@@ -621,8 +619,8 @@ def do_indent(s, width=4, first=False, blank=False):
 
         Rename the ``indentfirst`` argument to ``first``.
     """
-    indention = u" " * width
-    newline = u"\n"
+    indention = " " * width
+    newline = "\n"
 
     if isinstance(s, Markup):
         indention = Markup(indention)
@@ -674,8 +672,8 @@ def do_truncate(env, s, length=255, killwords=False, end="...", leeway=None):
     """
     if leeway is None:
         leeway = env.policies["truncate.leeway"]
-    assert length >= len(end), "expected length >= %s, got %s" % (len(end), length)
-    assert leeway >= 0, "expected leeway >= 0, got %s" % leeway
+    assert length >= len(end), f"expected length >= {len(end)}, got {length}"
+    assert leeway >= 0, f"expected leeway >= 0, got {leeway}"
     if len(s) <= length + leeway:
         return s
     if killwords:
@@ -1245,14 +1243,13 @@ def do_tojson(eval_ctx, value, indent=None):
 def prepare_map(args, kwargs):
     context = args[0]
     seq = args[1]
-    default = None
 
     if len(args) == 2 and "attribute" in kwargs:
         attribute = kwargs.pop("attribute")
         default = kwargs.pop("default", None)
         if kwargs:
             raise FilterArgumentError(
-                "Unexpected keyword argument %r" % next(iter(kwargs))
+                f"Unexpected keyword argument {next(iter(kwargs))!r}"
             )
         func = make_attrgetter(context.environment, attribute, default=default)
     else:
index 7889a2b53dd54eb45d36b4084ea8c3266b863eae..78cad916a5e05b1eb1a15b0cf0d8d1ff4ea402e7 100644 (file)
@@ -20,7 +20,7 @@ def symbols_for_node(node, parent_symbols=None):
     return sym
 
 
-class Symbols(object):
+class Symbols:
     def __init__(self, parent=None, level=None):
         if level is None:
             if parent is None:
@@ -38,7 +38,7 @@ class Symbols(object):
         visitor.visit(node, **kwargs)
 
     def _define_ref(self, name, load=None):
-        ident = "l_%d_%s" % (self.level, name)
+        ident = f"l_{self.level}_{name}"
         self.refs[name] = ident
         if load is not None:
             self.loads[ident] = load
@@ -60,8 +60,8 @@ class Symbols(object):
         rv = self.find_ref(name)
         if rv is None:
             raise AssertionError(
-                "Tried to resolve a name to a reference that "
-                "was unknown to the frame (%r)" % name
+                "Tried to resolve a name to a reference that was"
+                f" unknown to the frame ({name!r})"
             )
         return rv
 
@@ -199,7 +199,7 @@ class RootVisitor(NodeVisitor):
 
     def generic_visit(self, node, *args, **kwargs):
         raise NotImplementedError(
-            "Cannot find symbols for %r" % node.__class__.__name__
+            f"Cannot find symbols for {node.__class__.__name__!r}"
         )
 
 
index 4988f7e35fb004b0df1e69fb51509f038c61f22f..e0b7a2e9a1d5fd99b9342e55304bd0f04842d707 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Implements a Jinja / Python combination lexer. The ``Lexer`` class
 is used to do some preprocessing. It filters out invalid operators like
 the bitshift operators we don't allow in templates. It separates
@@ -120,10 +119,10 @@ operators = {
     ";": TOKEN_SEMICOLON,
 }
 
-reverse_operators = dict([(v, k) for k, v in operators.items()])
+reverse_operators = {v: k for k, v in operators.items()}
 assert len(operators) == len(reverse_operators), "operators dropped"
 operator_re = re.compile(
-    "(%s)" % "|".join(re.escape(x) for x in sorted(operators, key=lambda x: -len(x)))
+    f"({'|'.join(re.escape(x) for x in sorted(operators, key=lambda x: -len(x)))})"
 )
 
 ignored_tokens = frozenset(
@@ -227,7 +226,7 @@ def compile_rules(environment):
     return [x[1:] for x in sorted(rules, reverse=True)]
 
 
-class Failure(object):
+class Failure:
     """Class that raises a `TemplateSyntaxError` if called.
     Used by the `Lexer` to specify known errors.
     """
@@ -277,10 +276,10 @@ class Token(tuple):
         return False
 
     def __repr__(self):
-        return "Token(%r, %r, %r)" % (self.lineno, self.type, self.value)
+        return f"Token({self.lineno!r}, {self.type!r}, {self.value!r})"
 
 
-class TokenStreamIterator(object):
+class TokenStreamIterator:
     """The iterator for tokenstreams.  Iterate over the stream
     until the eof token is reached.
     """
@@ -300,7 +299,7 @@ class TokenStreamIterator(object):
         return token
 
 
-class TokenStream(object):
+class TokenStream:
     """A token stream is an iterable that yields :class:`Token`\\s.  The
     parser however does not iterate over it but calls :meth:`next` to go
     one token ahead.  The current active token is stored as :attr:`current`.
@@ -385,13 +384,13 @@ class TokenStream(object):
             expr = describe_token_expr(expr)
             if self.current.type is TOKEN_EOF:
                 raise TemplateSyntaxError(
-                    "unexpected end of template, expected %r." % expr,
+                    f"unexpected end of template, expected {expr!r}.",
                     self.current.lineno,
                     self.name,
                     self.filename,
                 )
             raise TemplateSyntaxError(
-                "expected token %r, got %r" % (expr, describe_token(self.current)),
+                f"expected token {expr!r}, got {describe_token(self.current)!r}",
                 self.current.lineno,
                 self.name,
                 self.filename,
@@ -435,10 +434,10 @@ class OptionalLStrip(tuple):
     # Even though it looks like a no-op, creating instances fails
     # without this.
     def __new__(cls, *members, **kwargs):
-        return super(OptionalLStrip, cls).__new__(cls, members)
+        return super().__new__(cls, members)
 
 
-class Lexer(object):
+class Lexer:
     """Class that implements a lexer for a given environment. Automatically
     created by the environment class, usually you don't have to do that.
 
@@ -471,8 +470,13 @@ class Lexer(object):
         # is required.
         root_tag_rules = compile_rules(environment)
 
+        block_start_re = e(environment.block_start_string)
+        block_end_re = e(environment.block_end_string)
+        comment_end_re = e(environment.comment_end_string)
+        variable_end_re = e(environment.variable_end_string)
+
         # block suffix if trimming is enabled
-        block_suffix_re = environment.trim_blocks and "\\n?" or ""
+        block_suffix_re = "\\n?" if environment.trim_blocks else ""
 
         # If lstrip is enabled, it should not be applied if there is any
         # non-whitespace between the newline and block.
@@ -481,28 +485,20 @@ class Lexer(object):
         self.newline_sequence = environment.newline_sequence
         self.keep_trailing_newline = environment.keep_trailing_newline
 
+        root_raw_re = (
+            fr"(?P<raw_begin>{block_start_re}(\-|\+|)\s*raw\s*"
+            fr"(?:\-{block_end_re}\s*|{block_end_re}))"
+        )
+        root_parts_re = "|".join(
+            [root_raw_re] + [fr"(?P<{n}>{r}(\-|\+|))" for n, r in root_tag_rules]
+        )
+
         # global lexing rules
         self.rules = {
             "root": [
                 # directives
                 (
-                    c(
-                        "(.*?)(?:%s)"
-                        % "|".join(
-                            [
-                                r"(?P<raw_begin>%s(\-|\+|)\s*raw\s*(?:\-%s\s*|%s))"
-                                % (
-                                    e(environment.block_start_string),
-                                    e(environment.block_end_string),
-                                    e(environment.block_end_string),
-                                )
-                            ]
-                            + [
-                                r"(?P<%s>%s(\-|\+|))" % (n, r)
-                                for n, r in root_tag_rules
-                            ]
-                        )
-                    ),
+                    c(fr"(.*?)(?:{root_parts_re})"),
                     OptionalLStrip(TOKEN_DATA, "#bygroup"),
                     "#bygroup",
                 ),
@@ -513,29 +509,18 @@ class Lexer(object):
             TOKEN_COMMENT_BEGIN: [
                 (
                     c(
-                        r"(.*?)((?:\-%s\s*|%s)%s)"
-                        % (
-                            e(environment.comment_end_string),
-                            e(environment.comment_end_string),
-                            block_suffix_re,
-                        )
+                        fr"(.*?)((?:\-{comment_end_re}\s*"
+                        fr"|{comment_end_re}){block_suffix_re})"
                     ),
                     (TOKEN_COMMENT, TOKEN_COMMENT_END),
                     "#pop",
                 ),
-                (c("(.)"), (Failure("Missing end of comment tag"),), None),
+                (c(r"(.)"), (Failure("Missing end of comment tag"),), None),
             ],
             # blocks
             TOKEN_BLOCK_BEGIN: [
                 (
-                    c(
-                        r"(?:\-%s\s*|%s)%s"
-                        % (
-                            e(environment.block_end_string),
-                            e(environment.block_end_string),
-                            block_suffix_re,
-                        )
-                    ),
+                    c(fr"(?:\-{block_end_re}\s*|{block_end_re}){block_suffix_re}"),
                     TOKEN_BLOCK_END,
                     "#pop",
                 ),
@@ -544,13 +529,7 @@ class Lexer(object):
             # variables
             TOKEN_VARIABLE_BEGIN: [
                 (
-                    c(
-                        r"\-%s\s*|%s"
-                        % (
-                            e(environment.variable_end_string),
-                            e(environment.variable_end_string),
-                        )
-                    ),
+                    c(fr"\-{variable_end_re}\s*|{variable_end_re}"),
                     TOKEN_VARIABLE_END,
                     "#pop",
                 )
@@ -560,18 +539,13 @@ class Lexer(object):
             TOKEN_RAW_BEGIN: [
                 (
                     c(
-                        r"(.*?)((?:%s(\-|\+|))\s*endraw\s*(?:\-%s\s*|%s%s))"
-                        % (
-                            e(environment.block_start_string),
-                            e(environment.block_end_string),
-                            e(environment.block_end_string),
-                            block_suffix_re,
-                        )
+                        fr"(.*?)((?:{block_start_re}(\-|\+|))\s*endraw\s*"
+                        fr"(?:\-{block_end_re}\s*|{block_end_re}{block_suffix_re}))"
                     ),
                     OptionalLStrip(TOKEN_DATA, TOKEN_RAW_END),
                     "#pop",
                 ),
-                (c("(.)"), (Failure("Missing end of raw directive"),), None),
+                (c(r"(.)"), (Failure("Missing end of raw directive"),), None),
             ],
             # line statements
             TOKEN_LINESTATEMENT_BEGIN: [
@@ -649,10 +623,8 @@ class Lexer(object):
         """
         lines = source.splitlines()
         if self.keep_trailing_newline and source:
-            for newline in ("\r\n", "\r", "\n"):
-                if source.endswith(newline):
-                    lines.append("")
-                    break
+            if source.endswith(("\r\n", "\r", "\n")):
+                lines.append("")
         source = "\n".join(lines)
         pos = 0
         lineno = 1
@@ -732,9 +704,8 @@ class Lexer(object):
                                     break
                             else:
                                 raise RuntimeError(
-                                    "%r wanted to resolve "
-                                    "the token dynamically"
-                                    " but no group matched" % regex
+                                    f"{regex!r} wanted to resolve the token dynamically"
+                                    " but no group matched"
                                 )
                         # normal group
                         else:
@@ -757,13 +728,12 @@ class Lexer(object):
                         elif data in ("}", ")", "]"):
                             if not balancing_stack:
                                 raise TemplateSyntaxError(
-                                    "unexpected '%s'" % data, lineno, name, filename
+                                    f"unexpected '{data}'", lineno, name, filename
                                 )
                             expected_op = balancing_stack.pop()
                             if expected_op != data:
                                 raise TemplateSyntaxError(
-                                    "unexpected '%s', "
-                                    "expected '%s'" % (data, expected_op),
+                                    f"unexpected '{data}', expected '{expected_op}'",
                                     lineno,
                                     name,
                                     filename,
@@ -791,9 +761,8 @@ class Lexer(object):
                                 break
                         else:
                             raise RuntimeError(
-                                "%r wanted to resolve the "
-                                "new state dynamically but"
-                                " no group matched" % regex
+                                f"{regex!r} wanted to resolve the new state dynamically"
+                                f" but no group matched"
                             )
                     # direct state name given
                     else:
@@ -804,7 +773,7 @@ class Lexer(object):
                 # raise error
                 elif pos2 == pos:
                     raise RuntimeError(
-                        "%r yielded empty string without stack change" % regex
+                        f"{regex!r} yielded empty string without stack change"
                     )
                 # publish new function and start again
                 pos = pos2
@@ -817,8 +786,5 @@ class Lexer(object):
                     return
                 # something went wrong
                 raise TemplateSyntaxError(
-                    "unexpected char %r at %d" % (source[pos], pos),
-                    lineno,
-                    name,
-                    filename,
+                    f"unexpected char {source[pos]!r} at {pos}", lineno, name, filename
                 )
index e48155dc00654ca6c7e5014ed076f50f03ed8d5d..b02cc3575f7a44a8ad76015d754e90a24dd65cd3 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """API and implementations for loading templates from different data
 sources.
 """
@@ -33,7 +32,7 @@ def split_template_path(template):
     return pieces
 
 
-class BaseLoader(object):
+class BaseLoader:
     """Baseclass for all loaders.  Subclass this and override `get_source` to
     implement a custom loading mechanism.  The environment provides a
     `get_template` method that calls the loader's `load` method to get the
@@ -86,7 +85,7 @@ class BaseLoader(object):
         """
         if not self.has_source_access:
             raise RuntimeError(
-                "%s cannot provide access to the source" % self.__class__.__name__
+                f"{self.__class__.__name__} cannot provide access to the source"
             )
         raise TemplateNotFound(template)
 
@@ -277,8 +276,8 @@ class PackageLoader(BaseLoader):
 
         if self._template_root is None:
             raise ValueError(
-                "The %r package was not installed in a way that"
-                " PackageLoader understands." % package_name
+                f"The {package_name!r} package was not installed in a"
+                " way that PackageLoader understands."
             )
 
     def get_source(self, environment, template):
@@ -514,7 +513,7 @@ class ModuleLoader(BaseLoader):
     has_source_access = False
 
     def __init__(self, path):
-        package_name = "_jinja2_module_templates_%x" % id(self)
+        package_name = f"_jinja2_module_templates_{id(self):x}"
 
         # create a fake module that looks for the templates in the
         # path given.
@@ -546,7 +545,7 @@ class ModuleLoader(BaseLoader):
     @internalcode
     def load(self, environment, name, globals=None):
         key = self.get_template_key(name)
-        module = "%s.%s" % (self.package_name, key)
+        module = f"{self.package_name}.{key}"
         mod = getattr(self.module, module, None)
         if mod is None:
             try:
index d112cbed660079cae5a6c668ffddb1432a2daab8..899e179a97ad6757d53272b1b41c3f145d3cf1dd 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Functions that expose information about templates that might be
 interesting for introspection.
 """
index 4f8106bc79e26a0d41b73dfffa62e010836d9a4c..e0ad94d3248e5a5abc3d57648848c578fe61f4f9 100644 (file)
@@ -32,7 +32,7 @@ def native_concat(nodes, preserve_quotes=True):
     else:
         if isinstance(nodes, types.GeneratorType):
             nodes = chain(head, nodes)
-        raw = u"".join([str(v) for v in nodes])
+        raw = "".join([str(v) for v in nodes])
 
     try:
         literal = literal_eval(raw)
@@ -44,7 +44,8 @@ def native_concat(nodes, preserve_quotes=True):
     # Without this, "'{{ a }}', '{{ b }}'" results in "a, b", but should
     # be ('a', 'b').
     if preserve_quotes and isinstance(literal, str):
-        return "{quote}{}{quote}".format(literal, quote=raw[0])
+        quote = raw[0]
+        return f"{quote}{literal}{quote}"
 
     return literal
 
index 1989fcf059b8399f1e0fa32e0211675ac9346428..d5133f759570a54b6937759ff756a154b00f7d98 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """AST nodes generated by the parser for the compiler. Also provides
 some node tree helper functions used by the parser and compiler in order
 to normalize nodes.
@@ -18,7 +17,11 @@ _binop_to_func = {
     "-": operator.sub,
 }
 
-_uaop_to_func = {"not": operator.not_, "+": operator.pos, "-": operator.neg}
+_uaop_to_func = {
+    "not": operator.not_,
+    "+": operator.pos,
+    "-": operator.neg,
+}
 
 _cmpop_to_func = {
     "eq": operator.eq,
@@ -53,7 +56,7 @@ class NodeType(type):
         return type.__new__(mcs, name, bases, d)
 
 
-class EvalContext(object):
+class EvalContext:
     """Holds evaluation time information.  Custom attributes can be attached
     to it in extensions.
     """
@@ -78,9 +81,8 @@ def get_eval_context(node, ctx):
     if ctx is None:
         if node.environment is None:
             raise RuntimeError(
-                "if no eval context is passed, the "
-                "node must have an attached "
-                "environment."
+                "if no eval context is passed, the node must have an"
+                " attached environment."
             )
         return EvalContext(node.environment)
     return ctx
@@ -113,21 +115,17 @@ class Node(metaclass=NodeType):
         if fields:
             if len(fields) != len(self.fields):
                 if not self.fields:
-                    raise TypeError("%r takes 0 arguments" % self.__class__.__name__)
+                    raise TypeError(f"{self.__class__.__name__!r} takes 0 arguments")
                 raise TypeError(
-                    "%r takes 0 or %d argument%s"
-                    % (
-                        self.__class__.__name__,
-                        len(self.fields),
-                        len(self.fields) != 1 and "s" or "",
-                    )
+                    f"{self.__class__.__name__!r} takes 0 or {len(self.fields)}"
+                    f" argument{'s' if len(self.fields) != 1 else ''}"
                 )
             for name, arg in zip(self.fields, fields):
                 setattr(self, name, arg)
         for attr in self.attributes:
             setattr(self, attr, attributes.pop(attr, None))
         if attributes:
-            raise TypeError("unknown attribute %r" % next(iter(attributes)))
+            raise TypeError(f"unknown attribute {next(iter(attributes))!r}")
 
     def iter_fields(self, exclude=None, only=None):
         """This method iterates over all fields that are defined and yields
@@ -174,8 +172,7 @@ class Node(metaclass=NodeType):
         for child in self.iter_child_nodes():
             if isinstance(child, node_type):
                 yield child
-            for result in child.find_all(node_type):
-                yield result
+            yield from child.find_all(node_type)
 
     def set_ctx(self, ctx):
         """Reset the context of a node and all child nodes.  Per default the
@@ -221,10 +218,8 @@ class Node(metaclass=NodeType):
         return hash(tuple(self.iter_fields()))
 
     def __repr__(self):
-        return "%s(%s)" % (
-            self.__class__.__name__,
-            ", ".join("%s=%r" % (arg, getattr(self, arg, None)) for arg in self.fields),
-        )
+        args_str = ", ".join(f"{a}={getattr(self, a, None)!r}" for a in self.fields)
+        return f"{self.__class__.__name__}({args_str})"
 
     def dump(self):
         def _dump(node):
@@ -232,7 +227,7 @@ class Node(metaclass=NodeType):
                 buf.append(repr(node))
                 return
 
-            buf.append("nodes.%s(" % node.__class__.__name__)
+            buf.append(f"nodes.{node.__class__.__name__}(")
             if not node.fields:
                 buf.append(")")
                 return
@@ -809,15 +804,6 @@ class Operand(Helper):
     fields = ("op", "expr")
 
 
-if __debug__:
-    Operand.__doc__ += "\nThe following operators are available: " + ", ".join(
-        sorted(
-            "``%s``" % x
-            for x in set(_binop_to_func) | set(_uaop_to_func) | set(_cmpop_to_func)
-        )
-    )
-
-
 class Mul(BinExpr):
     """Multiplies the left with the right node."""
 
index 7bc78c4524ab59dd7218cfe5f23b5de29a4a170c..39d059f1b234a164a91e0606ac6847bf5a7bc413 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """The optimizer tries to constant fold expressions and modify the AST
 in place so that it should be faster to evaluate.
 
@@ -24,7 +23,7 @@ class Optimizer(NodeTransformer):
         self.environment = environment
 
     def generic_visit(self, node, *args, **kwargs):
-        node = super(Optimizer, self).generic_visit(node, *args, **kwargs)
+        node = super().generic_visit(node, *args, **kwargs)
 
         # Do constant folding. Some other nodes besides Expr have
         # as_const, but folding them causes errors later on.
index ec0778a7080ff99d3ce274215d653be6f2f4ac7a..eedea7a03a9c5ef2c22df6f227fe00c665ac18d5 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Parse tokens from the lexer into nodes for the compiler."""
 from . import nodes
 from .exceptions import TemplateAssertionError
@@ -34,7 +33,7 @@ _math_nodes = {
 }
 
 
-class Parser(object):
+class Parser:
     """This is the central parsing class Jinja uses.  It's passed to
     extensions and can be used to parse expressions or statements.
     """
@@ -68,7 +67,7 @@ class Parser(object):
             expected.extend(map(describe_token_expr, exprs))
         if end_token_stack:
             currently_looking = " or ".join(
-                "'%s'" % describe_token_expr(expr) for expr in end_token_stack[-1]
+                map(repr, map(describe_token_expr, end_token_stack[-1]))
             )
         else:
             currently_looking = None
@@ -76,25 +75,23 @@ class Parser(object):
         if name is None:
             message = ["Unexpected end of template."]
         else:
-            message = ["Encountered unknown tag '%s'." % name]
+            message = [f"Encountered unknown tag {name!r}."]
 
         if currently_looking:
             if name is not None and name in expected:
                 message.append(
-                    "You probably made a nesting mistake. Jinja "
-                    "is expecting this tag, but currently looking "
-                    "for %s." % currently_looking
+                    "You probably made a nesting mistake. Jinja is expecting this tag,"
+                    f" but currently looking for {currently_looking}."
                 )
             else:
                 message.append(
-                    "Jinja was looking for the following tags: "
-                    "%s." % currently_looking
+                    f"Jinja was looking for the following tags: {currently_looking}."
                 )
 
         if self._tag_stack:
             message.append(
-                "The innermost block that needs to be "
-                "closed is '%s'." % self._tag_stack[-1]
+                "The innermost block that needs to be closed is"
+                f" {self._tag_stack[-1]!r}."
             )
 
         self.fail(" ".join(message), lineno)
@@ -125,7 +122,7 @@ class Parser(object):
         """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
         self._last_identifier += 1
         rv = object.__new__(nodes.InternalName)
-        nodes.Node.__init__(rv, "fi%d" % self._last_identifier, lineno=lineno)
+        nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno)
         return rv
 
     def parse_statement(self):
@@ -264,9 +261,8 @@ class Parser(object):
         # raise a nicer error message in that case.
         if self.stream.current.type == "sub":
             self.fail(
-                "Block names in Jinja have to be valid Python "
-                "identifiers and may not contain hyphens, use an "
-                "underscore instead."
+                "Block names in Jinja have to be valid Python identifiers and may not"
+                " contain hyphens, use an underscore instead."
             )
 
         node.body = self.parse_statements(("name:endblock",), drop_needle=True)
@@ -434,7 +430,7 @@ class Parser(object):
             target.set_ctx("store")
         if not target.can_assign():
             self.fail(
-                "can't assign to %r" % target.__class__.__name__.lower(), target.lineno
+                f"can't assign to {target.__class__.__name__.lower()!r}", target.lineno
             )
         return target
 
@@ -595,7 +591,7 @@ class Parser(object):
         elif token.type == "lbrace":
             node = self.parse_dict()
         else:
-            self.fail("unexpected '%s'" % describe_token(token), token.lineno)
+            self.fail(f"unexpected {describe_token(token)!r}", token.lineno)
         return node
 
     def parse_tuple(
@@ -657,8 +653,8 @@ class Parser(object):
             # tuple.
             if not explicit_parentheses:
                 self.fail(
-                    "Expected an expression, got '%s'"
-                    % describe_token(self.stream.current)
+                    "Expected an expression,"
+                    f" got {describe_token(self.stream.current)!r}"
                 )
 
         return nodes.Tuple(args, "load", lineno=lineno)
index 462238b5493d03680417ebae95a816754c3f932d..00f1f59f645f22fd2b24ca11fb8cd5f2c2ae7356 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """The runtime functions and state used by compiled templates."""
 import sys
 from collections import abc
@@ -53,7 +52,7 @@ def markup_join(seq):
     for arg in iterator:
         buf.append(arg)
         if hasattr(arg, "__html__"):
-            return Markup(u"").join(chain(buf, iterator))
+            return Markup("").join(chain(buf, iterator))
     return concat(buf)
 
 
@@ -101,7 +100,7 @@ def new_context(
     return environment.context_class(environment, parent, template_name, blocks)
 
 
-class TemplateReference(object):
+class TemplateReference:
     """The `self` in templates."""
 
     def __init__(self, context):
@@ -112,7 +111,7 @@ class TemplateReference(object):
         return BlockReference(name, self.__context, blocks, 0)
 
     def __repr__(self):
-        return "<%s %r>" % (self.__class__.__name__, self.__context.name)
+        return f"<{self.__class__.__name__} {self.__context.name!r}>"
 
 
 def _get_func(x):
@@ -206,7 +205,7 @@ class Context(metaclass=ContextMeta):
             blocks[index]
         except LookupError:
             return self.environment.undefined(
-                "there is no parent block called %r." % name, name="super"
+                f"there is no parent block called {name!r}.", name="super"
             )
         return BlockReference(name, self, blocks, index)
 
@@ -244,7 +243,7 @@ class Context(metaclass=ContextMeta):
 
     def get_exported(self):
         """Get a new dict with the exported variables."""
-        return dict((k, self.vars[k]) for k in self.exported_vars)
+        return {k: self.vars[k] for k in self.exported_vars}
 
     def get_all(self):
         """Return the complete context as dict including the exported
@@ -290,9 +289,8 @@ class Context(metaclass=ContextMeta):
             return __obj(*args, **kwargs)
         except StopIteration:
             return __self.environment.undefined(
-                "value was undefined because "
-                "a callable raised a "
-                "StopIteration exception"
+                "value was undefined because a callable raised a"
+                " StopIteration exception"
             )
 
     def derived(self, locals=None):
@@ -333,14 +331,10 @@ class Context(metaclass=ContextMeta):
         return item
 
     def __repr__(self):
-        return "<%s %s of %r>" % (
-            self.__class__.__name__,
-            repr(self.get_all()),
-            self.name,
-        )
+        return f"<{self.__class__.__name__} {self.get_all()!r} of {self.name!r}>"
 
 
-class BlockReference(object):
+class BlockReference:
     """One block on a template reference."""
 
     def __init__(self, name, context, stack, depth):
@@ -354,7 +348,7 @@ class BlockReference(object):
         """Super the block."""
         if self._depth + 1 >= len(self._stack):
             return self._context.environment.undefined(
-                "there is no parent block called %r." % self.name, name="super"
+                f"there is no parent block called {self.name!r}.", name="super"
             )
         return BlockReference(self.name, self._context, self._stack, self._depth + 1)
 
@@ -554,10 +548,10 @@ class LoopContext:
         return self._recurse(iterable, self._recurse, depth=self.depth)
 
     def __repr__(self):
-        return "<%s %d/%d>" % (self.__class__.__name__, self.index, self.length)
+        return f"<{self.__class__.__name__} {self.index}/{self.length}>"
 
 
-class Macro(object):
+class Macro:
     """Wraps a macro function."""
 
     def __init__(
@@ -646,20 +640,18 @@ class Macro(object):
         elif kwargs:
             if "caller" in kwargs:
                 raise TypeError(
-                    "macro %r was invoked with two values for "
-                    "the special caller argument.  This is "
-                    "most likely a bug." % self.name
+                    f"macro {self.name!r} was invoked with two values for the special"
+                    " caller argument. This is most likely a bug."
                 )
             raise TypeError(
-                "macro %r takes no keyword argument %r"
-                % (self.name, next(iter(kwargs)))
+                f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}"
             )
         if self.catch_varargs:
             arguments.append(args[self._argument_count :])
         elif len(args) > self._argument_count:
             raise TypeError(
-                "macro %r takes not more than %d argument(s)"
-                % (self.name, len(self.arguments))
+                f"macro {self.name!r} takes not more than"
+                f" {len(self.arguments)} argument(s)"
             )
 
         return self._invoke(arguments, autoescape)
@@ -672,13 +664,11 @@ class Macro(object):
         return rv
 
     def __repr__(self):
-        return "<%s %s>" % (
-            self.__class__.__name__,
-            self.name is None and "anonymous" or repr(self.name),
-        )
+        name = "anonymous" if self.name is None else repr(self.name)
+        return f"<{self.__class__.__name__} {name}>"
 
 
-class Undefined(object):
+class Undefined:
     """The default undefined type.  This undefined type can be printed and
     iterated over, but every other access will raise an :exc:`UndefinedError`:
 
@@ -715,17 +705,17 @@ class Undefined(object):
             return self._undefined_hint
 
         if self._undefined_obj is missing:
-            return "%r is undefined" % self._undefined_name
+            return f"{self._undefined_name!r} is undefined"
 
         if not isinstance(self._undefined_name, str):
-            return "%s has no element %r" % (
-                object_type_repr(self._undefined_obj),
-                self._undefined_name,
+            return (
+                f"{object_type_repr(self._undefined_obj)} has no"
+                f" element {self._undefined_name!r}"
             )
 
-        return "%r has no attribute %r" % (
-            object_type_repr(self._undefined_obj),
-            self._undefined_name,
+        return (
+            f"{object_type_repr(self._undefined_obj)!r} has no"
+            f" attribute {self._undefined_name!r}"
         )
 
     @internalcode
@@ -762,7 +752,7 @@ class Undefined(object):
         return id(type(self))
 
     def __str__(self):
-        return u""
+        return ""
 
     def __len__(self):
         return 0
@@ -807,45 +797,27 @@ def make_logging_undefined(logger=None, base=None):
         base = Undefined
 
     def _log_message(undef):
-        if undef._undefined_hint is None:
-            if undef._undefined_obj is missing:
-                hint = "%s is undefined" % undef._undefined_name
-            elif not isinstance(undef._undefined_name, str):
-                hint = "%s has no element %s" % (
-                    object_type_repr(undef._undefined_obj),
-                    undef._undefined_name,
-                )
-            else:
-                hint = "%s has no attribute %s" % (
-                    object_type_repr(undef._undefined_obj),
-                    undef._undefined_name,
-                )
-        else:
-            hint = undef._undefined_hint
-        logger.warning("Template variable warning: %s", hint)
+        logger.warning("Template variable warning: %s", undef._undefined_message)
 
     class LoggingUndefined(base):
         def _fail_with_undefined_error(self, *args, **kwargs):
             try:
-                return base._fail_with_undefined_error(self, *args, **kwargs)
+                return super()._fail_with_undefined_error(*args, **kwargs)
             except self._undefined_exception as e:
-                logger.error("Template variable error: %s", str(e))
+                logger.error(f"Template variable error: %s", e)
                 raise e
 
         def __str__(self):
-            rv = base.__str__(self)
             _log_message(self)
-            return rv
+            return super().__str__()
 
         def __iter__(self):
-            rv = base.__iter__(self)
             _log_message(self)
-            return rv
+            return super().__iter__()
 
         def __bool__(self):
-            rv = base.__bool__(self)
             _log_message(self)
-            return rv
+            return super().__bool__()
 
     return LoggingUndefined
 
@@ -894,14 +866,19 @@ class DebugUndefined(Undefined):
     __slots__ = ()
 
     def __str__(self):
-        if self._undefined_hint is None:
-            if self._undefined_obj is missing:
-                return u"{{ %s }}" % self._undefined_name
-            return "{{ no such element: %s[%r] }}" % (
-                object_type_repr(self._undefined_obj),
-                self._undefined_name,
+        if self._undefined_hint:
+            message = f"undefined value printed: {self._undefined_hint}"
+
+        elif self._undefined_obj is missing:
+            message = self._undefined_name
+
+        else:
+            message = (
+                f"no such element: {object_type_repr(self._undefined_obj)}"
+                f"[{self._undefined_name!r}]"
             )
-        return u"{{ undefined value printed: %s }}" % self._undefined_hint
+
+        return f"{{{{ {message} }}}}"
 
 
 class StrictUndefined(Undefined):
index 1263c77fc3b94e5ed219e2dceb0560efe9e9b092..deecf61ce159eaed79e627d4a5e1110b4cca0a24 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """A sandbox layer that ensures unsafe operations cannot be performed.
 Useful when the template itself comes from an untrusted source.
 """
@@ -126,7 +125,7 @@ def safe_range(*args):
     if len(rng) > MAX_RANGE:
         raise OverflowError(
             "Range too big. The sandbox blocks ranges larger than"
-            " MAX_RANGE (%d)." % MAX_RANGE
+            f" MAX_RANGE ({MAX_RANGE})."
         )
 
     return rng
@@ -135,7 +134,7 @@ def safe_range(*args):
 def unsafe(f):
     """Marks a function or method as unsafe.
 
-    ::
+    .. code-block: python
 
         @unsafe
         def delete(self):
@@ -370,8 +369,8 @@ class SandboxedEnvironment(Environment):
     def unsafe_undefined(self, obj, attribute):
         """Return an undefined object for unsafe attributes."""
         return self.undefined(
-            "access to attribute %r of %r "
-            "object is unsafe." % (attribute, obj.__class__.__name__),
+            f"access to attribute {attribute!r} of"
+            f" {obj.__class__.__name__!r} object is unsafe.",
             name=attribute,
             obj=obj,
             exc=SecurityError,
@@ -389,8 +388,8 @@ class SandboxedEnvironment(Environment):
         if format_func is not None and format_func.__name__ == "format_map":
             if len(args) != 1 or kwargs:
                 raise TypeError(
-                    "format_map() takes exactly one argument %d given"
-                    % (len(args) + (kwargs is not None))
+                    "format_map() takes exactly one argument"
+                    f" {len(args) + (kwargs is not None)} given"
                 )
 
             kwargs = args[0]
@@ -409,7 +408,7 @@ class SandboxedEnvironment(Environment):
         # the double prefixes are to avoid double keyword argument
         # errors when proxying the call.
         if not __self.is_safe_callable(__obj):
-            raise SecurityError("%r is not safely callable" % (__obj,))
+            raise SecurityError(f"{__obj!r} is not safely callable")
         return __context.call(__obj, *args, **kwargs)
 
 
@@ -425,7 +424,7 @@ class ImmutableSandboxedEnvironment(SandboxedEnvironment):
         return not modifies_known_mutable(obj, attr)
 
 
-class SandboxedFormatterMixin(object):
+class SandboxedFormatterMixin:
     def __init__(self, env):
         self._env = env
 
index 6a24d9c0bac469a088d6ef1f27fec2d158981d12..bc7632686aa26f9eea734288c3dcc72845fefbce 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Built-in template tests used with the ``is`` operator."""
 import operator
 import re
index dfc518f2b128c898df07ac2e42c3ee43cf471bfd..99293cd69274e5721c16dd300ce47a2dd37aab05 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import json
 import os
 import re
@@ -13,12 +12,10 @@ from markupsafe import escape
 from markupsafe import Markup
 
 _word_split_re = re.compile(r"(\s+)")
+_lead_pattern = "|".join(map(re.escape, ("(", "<", "&lt;")))
+_trail_pattern = "|".join(map(re.escape, (".", ",", ")", ">", "\n", "&gt;")))
 _punctuation_re = re.compile(
-    "^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$"
-    % (
-        "|".join(map(re.escape, ("(", "<", "&lt;"))),
-        "|".join(map(re.escape, (".", ",", ")", ">", "\n", "&gt;"))),
-    )
+    fr"^(?P<lead>(?:{_lead_pattern})*)(?P<middle>.*?)(?P<trail>(?:{_trail_pattern})*)$"
 )
 _simple_email_re = re.compile(r"^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$")
 _striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
@@ -32,7 +29,7 @@ missing = type("MissingType", (), {"__repr__": lambda x: "missing"})()
 # internal code
 internal_code = set()
 
-concat = u"".join
+concat = "".join
 
 _slash_escape = "\\/" not in json.dumps("/")
 
@@ -165,8 +162,8 @@ def object_type_repr(obj):
     if obj.__class__.__module__ in ("__builtin__", "builtins"):
         name = obj.__class__.__name__
     else:
-        name = obj.__class__.__module__ + "." + obj.__class__.__name__
-    return "%s object" % name
+        name = f"{obj.__class__.__module__}.{obj.__class__.__name__}"
+    return f"{name} object"
 
 
 def pformat(obj):
@@ -191,14 +188,16 @@ def urlize(text, trim_url_limit=None, rel=None, target=None):
 
     If target is not None, a target attribute will be added to the link.
     """
-    trim_url = (
-        lambda x, limit=trim_url_limit: limit is not None
-        and (x[:limit] + (len(x) >= limit and "..." or ""))
-        or x
-    )
+
+    def trim_url(x, limit=trim_url_limit):
+        if limit is not None:
+            return x[:limit] + ("..." if len(x) >= limit else "")
+
+        return x
+
     words = _word_split_re.split(str(escape(text)))
-    rel_attr = rel and ' rel="%s"' % str(escape(rel)) or ""
-    target_attr = target and ' target="%s"' % escape(target) or ""
+    rel_attr = f' rel="{escape(rel)}"' if rel else ""
+    target_attr = f' target="{escape(target)}"' if target else ""
 
     for i, word in enumerate(words):
         match = _punctuation_re.match(word)
@@ -216,18 +215,13 @@ def urlize(text, trim_url_limit=None, rel=None, target=None):
                     or middle.endswith(".com")
                 )
             ):
-                middle = '<a href="http://%s"%s%s>%s</a>' % (
-                    middle,
-                    rel_attr,
-                    target_attr,
-                    trim_url(middle),
+                middle = (
+                    f'<a href="http://{middle}"{rel_attr}{target_attr}>'
+                    f"{trim_url(middle)}</a>"
                 )
             if middle.startswith("http://") or middle.startswith("https://"):
-                middle = '<a href="%s"%s%s>%s</a>' % (
-                    middle,
-                    rel_attr,
-                    target_attr,
-                    trim_url(middle),
+                middle = (
+                    f'<a href="{middle}"{rel_attr}{target_attr}>{trim_url(middle)}</a>'
                 )
             if (
                 "@" in middle
@@ -235,10 +229,10 @@ def urlize(text, trim_url_limit=None, rel=None, target=None):
                 and ":" not in middle
                 and _simple_email_re.match(middle)
             ):
-                middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
+                middle = f'<a href="mailto:{middle}">{middle}</a>'
             if lead + middle + trail != word:
                 words[i] = lead + middle + trail
-    return u"".join(words)
+    return "".join(words)
 
 
 def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
@@ -278,7 +272,7 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
             p.append(word)
 
         # ensure that the paragraph ends with a dot.
-        p = u" ".join(p)
+        p = " ".join(p)
         if p.endswith(","):
             p = p[:-1] + "."
         elif not p.endswith("."):
@@ -286,8 +280,8 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
         result.append(p)
 
     if not html:
-        return u"\n\n".join(result)
-    return Markup(u"\n".join(u"<p>%s</p>" % escape(x) for x in result))
+        return "\n\n".join(result)
+    return Markup("\n".join(f"<p>{escape(x)}</p>" for x in result))
 
 
 def url_quote(obj, charset="utf-8", for_qs=False):
@@ -329,7 +323,7 @@ def unicode_urlencode(obj, charset="utf-8", for_qs=False):
 
 
 @abc.MutableMapping.register
-class LRUCache(object):
+class LRUCache:
     """A simple LRU Cache implementation."""
 
     # this is fast for small capacities (something below 1000) but doesn't
@@ -406,7 +400,7 @@ class LRUCache(object):
         return len(self._mapping)
 
     def __repr__(self):
-        return "<%s %r>" % (self.__class__.__name__, self._mapping)
+        return f"<{self.__class__.__name__} {self._mapping!r}>"
 
     def __getitem__(self, key):
         """Get an item from the cache. Moves the item up so that it has the
@@ -525,8 +519,8 @@ def select_autoescape(
 
     .. versionadded:: 2.9
     """
-    enabled_patterns = tuple("." + x.lstrip(".").lower() for x in enabled_extensions)
-    disabled_patterns = tuple("." + x.lstrip(".").lower() for x in disabled_extensions)
+    enabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in enabled_extensions)
+    disabled_patterns = tuple(f".{x.lstrip('.').lower()}" for x in disabled_extensions)
 
     def autoescape(template_name):
         if template_name is None:
@@ -563,15 +557,15 @@ def htmlsafe_json_dumps(obj, dumper=None, **kwargs):
         dumper = json.dumps
     rv = (
         dumper(obj, **kwargs)
-        .replace(u"<", u"\\u003c")
-        .replace(u">", u"\\u003e")
-        .replace(u"&", u"\\u0026")
-        .replace(u"'", u"\\u0027")
+        .replace("<", "\\u003c")
+        .replace(">", "\\u003e")
+        .replace("&", "\\u0026")
+        .replace("'", "\\u0027")
     )
     return Markup(rv)
 
 
-class Cycler(object):
+class Cycler:
     """Cycle through values by yield them one at a time, then restarting
     once the end is reached. Available as ``cycler`` in templates.
 
@@ -625,21 +619,21 @@ class Cycler(object):
     __next__ = next
 
 
-class Joiner(object):
+class Joiner:
     """A joining helper for templates."""
 
-    def __init__(self, sep=u", "):
+    def __init__(self, sep=", "):
         self.sep = sep
         self.used = False
 
     def __call__(self):
         if not self.used:
             self.used = True
-            return u""
+            return ""
         return self.sep
 
 
-class Namespace(object):
+class Namespace:
     """A namespace object that can hold arbitrary attributes.  It may be
     initialized from a dictionary or with keyword arguments."""
 
@@ -659,7 +653,7 @@ class Namespace(object):
         self.__attrs[name] = value
 
     def __repr__(self):
-        return "<Namespace %r>" % self.__attrs
+        return f"<Namespace {self.__attrs!r}>"
 
 
 # does this python version support async for in and async generators?
index d1365bf10e5346a60df625faa5e118d4086b1395..590fa9eb2879c25258136a223dd8ddd0761f6961 100644 (file)
@@ -1,11 +1,10 @@
-# -*- coding: utf-8 -*-
 """API for traversing the AST nodes. Implemented by the compiler and
 meta introspection.
 """
 from .nodes import Node
 
 
-class NodeVisitor(object):
+class NodeVisitor:
     """Walks the abstract syntax tree and call visitor functions for every
     node found.  The visitor functions may return values which will be
     forwarded by the `visit` method.
@@ -22,8 +21,7 @@ class NodeVisitor(object):
         exists for this node.  In that case the generic visit function is
         used instead.
         """
-        method = "visit_" + node.__class__.__name__
-        return getattr(self, method, None)
+        return getattr(self, f"visit_{node.__class__.__name__}", None)
 
     def visit(self, node, *args, **kwargs):
         """Visit a node."""
index bb26409a5ca4195dfd16b9bc52a589b397057888..794e93339ffb2f236d34e2c6a6827dc0829f009a 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import os
 
 import pytest
index 76e0534271f307b26cb4211e9fded662862a794b..387fb3f16a6da7ebec854d05df393ebfc6add483 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import os
 import shutil
 import tempfile
@@ -27,7 +26,7 @@ from jinja2.utils import evalcontextfunction
 
 @pytest.mark.api
 @pytest.mark.extended
-class TestExtendedAPI(object):
+class TestExtendedAPI:
     def test_item_and_attribute(self, env):
         from jinja2.sandbox import SandboxedEnvironment
 
@@ -164,11 +163,11 @@ class TestExtendedAPI(object):
 
 @pytest.mark.api
 @pytest.mark.meta
-class TestMeta(object):
+class TestMeta:
     def test_find_undeclared_variables(self, env):
         ast = env.parse("{% set foo = 42 %}{{ bar + foo }}")
         x = meta.find_undeclared_variables(ast)
-        assert x == set(["bar"])
+        assert x == {"bar"}
 
         ast = env.parse(
             "{% set foo = 42 %}{{ bar + foo }}"
@@ -177,11 +176,11 @@ class TestMeta(object):
             "{% endfor %}"
         )
         x = meta.find_undeclared_variables(ast)
-        assert x == set(["bar", "seq", "muh"])
+        assert x == {"bar", "seq", "muh"}
 
         ast = env.parse("{% for x in range(5) %}{{ x }}{% endfor %}{{ foo }}")
         x = meta.find_undeclared_variables(ast)
-        assert x == set(["foo"])
+        assert x == {"foo"}
 
     def test_find_refererenced_templates(self, env):
         ast = env.parse('{% extends "layout.html" %}{% include helper %}')
@@ -219,7 +218,7 @@ class TestMeta(object):
 
 @pytest.mark.api
 @pytest.mark.streaming
-class TestStreaming(object):
+class TestStreaming:
     def test_basic_streaming(self, env):
         t = env.from_string(
             "<ul>{% for item in seq %}<li>{{ loop.index }} - {{ item }}</li>"
@@ -236,8 +235,8 @@ class TestStreaming(object):
         )
         stream = tmpl.stream(seq=list(range(3)))
         stream.enable_buffering(size=3)
-        assert next(stream) == u"<ul><li>1"
-        assert next(stream) == u" - 0</li>"
+        assert next(stream) == "<ul><li>1"
+        assert next(stream) == " - 0</li>"
 
     def test_streaming_behavior(self, env):
         tmpl = env.from_string("")
@@ -251,7 +250,7 @@ class TestStreaming(object):
     def test_dump_stream(self, env):
         tmp = tempfile.mkdtemp()
         try:
-            tmpl = env.from_string(u"\u2713")
+            tmpl = env.from_string("\u2713")
             stream = tmpl.stream()
             stream.dump(os.path.join(tmp, "dump.txt"), "utf-8")
             with open(os.path.join(tmp, "dump.txt"), "rb") as f:
@@ -262,7 +261,7 @@ class TestStreaming(object):
 
 @pytest.mark.api
 @pytest.mark.undefined
-class TestUndefined(object):
+class TestUndefined:
     def test_stopiteration_is_undefined(self):
         def test():
             raise StopIteration()
@@ -279,7 +278,7 @@ class TestUndefined(object):
     def test_logging_undefined(self):
         _messages = []
 
-        class DebugLogger(object):
+        class DebugLogger:
             def warning(self, msg, *args):
                 _messages.append("W:" + msg % args)
 
@@ -288,23 +287,23 @@ class TestUndefined(object):
 
         logging_undefined = make_logging_undefined(DebugLogger())
         env = Environment(undefined=logging_undefined)
-        assert env.from_string("{{ missing }}").render() == u""
+        assert env.from_string("{{ missing }}").render() == ""
         pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render)
         assert env.from_string("{{ missing|list }}").render() == "[]"
         assert env.from_string("{{ missing is not defined }}").render() == "True"
         assert env.from_string("{{ foo.missing }}").render(foo=42) == ""
         assert env.from_string("{{ not missing }}").render() == "True"
         assert _messages == [
-            "W:Template variable warning: missing is undefined",
+            "W:Template variable warning: 'missing' is undefined",
             "E:Template variable error: 'missing' is undefined",
-            "W:Template variable warning: missing is undefined",
-            "W:Template variable warning: int object has no attribute missing",
-            "W:Template variable warning: missing is undefined",
+            "W:Template variable warning: 'missing' is undefined",
+            "W:Template variable warning: 'int object' has no attribute 'missing'",
+            "W:Template variable warning: 'missing' is undefined",
         ]
 
     def test_default_undefined(self):
         env = Environment(undefined=Undefined)
-        assert env.from_string("{{ missing }}").render() == u""
+        assert env.from_string("{{ missing }}").render() == ""
         pytest.raises(UndefinedError, env.from_string("{{ missing.attribute }}").render)
         assert env.from_string("{{ missing|list }}").render() == "[]"
         assert env.from_string("{{ missing is not defined }}").render() == "True"
@@ -322,7 +321,7 @@ class TestUndefined(object):
     def test_chainable_undefined(self):
         env = Environment(undefined=ChainableUndefined)
         # The following tests are copied from test_default_undefined
-        assert env.from_string("{{ missing }}").render() == u""
+        assert env.from_string("{{ missing }}").render() == ""
         assert env.from_string("{{ missing|list }}").render() == "[]"
         assert env.from_string("{{ missing is not defined }}").render() == "True"
         assert env.from_string("{{ foo.missing }}").render(foo=42) == ""
@@ -332,19 +331,17 @@ class TestUndefined(object):
             getattr(ChainableUndefined, "__slots__")  # noqa: B009
 
         # The following tests ensure subclass functionality works as expected
-        assert env.from_string('{{ missing.bar["baz"] }}').render() == u""
-        assert (
-            env.from_string('{{ foo.bar["baz"]._undefined_name }}').render() == u"foo"
-        )
+        assert env.from_string('{{ missing.bar["baz"] }}').render() == ""
+        assert env.from_string('{{ foo.bar["baz"]._undefined_name }}').render() == "foo"
         assert (
             env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(foo=42)
-            == u"bar"
+            == "bar"
         )
         assert (
             env.from_string('{{ foo.bar["baz"]._undefined_name }}').render(
                 foo={"bar": 42}
             )
-            == u"baz"
+            == "baz"
         )
 
     def test_debug_undefined(self):
@@ -355,13 +352,13 @@ class TestUndefined(object):
         assert env.from_string("{{ missing is not defined }}").render() == "True"
         assert (
             env.from_string("{{ foo.missing }}").render(foo=42)
-            == u"{{ no such element: int object['missing'] }}"
+            == "{{ no such element: int object['missing'] }}"
         )
         assert env.from_string("{{ not missing }}").render() == "True"
         undefined_hint = "this is testing undefined hint of DebugUndefined"
         assert (
             str(DebugUndefined(hint=undefined_hint))
-            == u"{{ undefined value printed: %s }}" % undefined_hint
+            == f"{{{{ undefined value printed: {undefined_hint} }}}}"
         )
         with pytest.raises(AttributeError):
             getattr(DebugUndefined, "__slots__")  # noqa: B009
@@ -401,7 +398,7 @@ class TestUndefined(object):
 
 @pytest.mark.api
 @pytest.mark.lowlevel
-class TestLowLevel(object):
+class TestLowLevel:
     def test_custom_code_generator(self):
         class CustomCodeGenerator(CodeGenerator):
             def visit_Const(self, node, frame=None):
@@ -409,7 +406,7 @@ class TestLowLevel(object):
                 if node.value == "foo":
                     self.write(repr("bar"))
                 else:
-                    super(CustomCodeGenerator, self).visit_Const(node, frame)
+                    super().visit_Const(node, frame)
 
         class CustomEnvironment(Environment):
             code_generator_class = CustomCodeGenerator
index d6c4a239aa20a6d32d246c75e502f525d65d29fe..70f11bec4f5ed8db455f0e8eeec00f89460b94a6 100644 (file)
@@ -131,7 +131,7 @@ def test_env_async():
 
 
 @pytest.mark.imports
-class TestAsyncImports(object):
+class TestAsyncImports:
     def test_context_imports(self, test_env_async):
         t = test_env_async.from_string('{% import "module" as m %}{{ m.test() }}')
         assert t.render(foo=42) == "[|23]"
@@ -182,7 +182,7 @@ class TestAsyncImports(object):
 
 @pytest.mark.imports
 @pytest.mark.includes
-class TestAsyncIncludes(object):
+class TestAsyncIncludes:
     def test_context_include(self, test_env_async):
         t = test_env_async.from_string('{% include "header" %}')
         assert t.render(foo=42) == "[42|23]"
@@ -281,7 +281,7 @@ class TestAsyncIncludes(object):
 
 @pytest.mark.core_tags
 @pytest.mark.for_loop
-class TestAsyncForLoop(object):
+class TestAsyncForLoop:
     def test_simple(self, test_env_async):
         tmpl = test_env_async.from_string("{% for item in seq %}{{ item }}{% endfor %}")
         assert tmpl.render(seq=list(range(10))) == "0123456789"
@@ -345,8 +345,7 @@ class TestAsyncForLoop(object):
 
     def test_varlen(self, test_env_async):
         def inner():
-            for item in range(5):
-                yield item
+            yield from range(5)
 
         tmpl = test_env_async.from_string(
             "{% for item in iter %}{{ item }}{% endfor %}"
index 2970cb5c182a07b2695ece949e80c366b56c3eff..8979d159727d0b6b6431db4eb42cb1b643422ca1 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pytest
 
 from jinja2 import Environment
@@ -15,14 +14,14 @@ def env(package_loader, tmp_path):
 
 
 @pytest.mark.byte_code_cache
-class TestByteCodeCache(object):
+class TestByteCodeCache:
     def test_simple(self, env):
         tmpl = env.get_template("test.html")
         assert tmpl.render().strip() == "BAR"
         pytest.raises(TemplateNotFound, env.get_template, "missing.html")
 
 
-class MockMemcached(object):
+class MockMemcached:
     class Error(Exception):
         pass
 
@@ -45,7 +44,7 @@ class MockMemcached(object):
         raise self.Error()
 
 
-class TestMemcachedBytecodeCache(object):
+class TestMemcachedBytecodeCache:
     def test_dump_load(self):
         memcached = MockMemcached()
         m = MemcachedBytecodeCache(memcached)
index 4132c4f5ea25cac64e9646afa9ae1c008939a809..df41348006f33ea5cbaa62206328296fbe416aa5 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pytest
 
 from jinja2 import DictLoader
@@ -15,7 +14,7 @@ def env_trim():
 
 @pytest.mark.core_tags
 @pytest.mark.for_loop
-class TestForLoop(object):
+class TestForLoop:
     def test_simple(self, env):
         tmpl = env.from_string("{% for item in seq %}{{ item }}{% endfor %}")
         assert tmpl.render(seq=list(range(10))) == "0123456789"
@@ -103,12 +102,8 @@ class TestForLoop(object):
         assert not output
 
     def test_varlen(self, env):
-        def inner():
-            for item in range(5):
-                yield item
-
         tmpl = env.from_string("{% for item in iter %}{{ item }}{% endfor %}")
-        output = tmpl.render(iter=inner())
+        output = tmpl.render(iter=range(5))
         assert output == "01234"
 
     def test_noniter(self, env):
@@ -307,7 +302,7 @@ class TestForLoop(object):
 
 @pytest.mark.core_tags
 @pytest.mark.if_condition
-class TestIfCondition(object):
+class TestIfCondition:
     def test_simple(self, env):
         tmpl = env.from_string("""{% if true %}...{% endif %}""")
         assert tmpl.render() == "..."
@@ -320,10 +315,8 @@ class TestIfCondition(object):
         assert tmpl.render() == "..."
 
     def test_elif_deep(self, env):
-        elifs = "\n".join("{{% elif a == {0} %}}{0}".format(i) for i in range(1, 1000))
-        tmpl = env.from_string(
-            "{{% if a == 0 %}}0{0}{{% else %}}x{{% endif %}}".format(elifs)
-        )
+        elifs = "\n".join(f"{{% elif a == {i} %}}{i}" for i in range(1, 1000))
+        tmpl = env.from_string(f"{{% if a == 0 %}}0{elifs}{{% else %}}x{{% endif %}}")
         for x in (0, 10, 999):
             assert tmpl.render(a=x).strip() == str(x)
         assert tmpl.render(a=1000).strip() == "x"
@@ -351,7 +344,7 @@ class TestIfCondition(object):
 
 @pytest.mark.core_tags
 @pytest.mark.macros
-class TestMacros(object):
+class TestMacros:
     def test_simple(self, env_trim):
         tmpl = env_trim.from_string(
             """\
@@ -477,7 +470,7 @@ class TestMacros(object):
 
 @pytest.mark.core_tags
 @pytest.mark.set
-class TestSet(object):
+class TestSet:
     def test_normal(self, env_trim):
         tmpl = env_trim.from_string("{% set foo = 1 %}{{ foo }}")
         assert tmpl.render() == "1"
@@ -486,7 +479,7 @@ class TestSet(object):
     def test_block(self, env_trim):
         tmpl = env_trim.from_string("{% set foo %}42{% endset %}{{ foo }}")
         assert tmpl.render() == "42"
-        assert tmpl.module.foo == u"42"
+        assert tmpl.module.foo == "42"
 
     def test_block_escaping(self):
         env = Environment(autoescape=True)
@@ -565,7 +558,7 @@ class TestSet(object):
             "{% set foo | trim | length | string %} 42    {% endset %}{{ foo }}"
         )
         assert tmpl.render() == "2"
-        assert tmpl.module.foo == u"2"
+        assert tmpl.module.foo == "2"
 
     def test_block_filtered_set(self, env_trim):
         def _myfilter(val, arg):
@@ -581,12 +574,12 @@ class TestSet(object):
             "{{ foo }}"
         )
         assert tmpl.render() == "11"
-        assert tmpl.module.foo == u"11"
+        assert tmpl.module.foo == "11"
 
 
 @pytest.mark.core_tags
 @pytest.mark.with_
-class TestWith(object):
+class TestWith:
     def test_with(self, env):
         tmpl = env.from_string(
             """\
index 5ca92d92d098d86e78fa54e3a4720692ac171684..d3c3ac5c172b7d31d0b11d836f31fe2c3e672fd4 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pickle
 import re
 from traceback import format_exception
@@ -18,17 +17,16 @@ def fs_env(filesystem_loader):
 
 
 @pytest.mark.debug
-class TestDebug(object):
+class TestDebug:
     def assert_traceback_matches(self, callback, expected_tb):
         with pytest.raises(Exception) as exc_info:
             callback()
 
         tb = format_exception(exc_info.type, exc_info.value, exc_info.tb)
         m = re.search(expected_tb.strip(), "".join(tb))
-        assert m is not None, "Traceback did not match:\n\n%s\nexpected:\n%s" % (
-            "".join(tb),
-            expected_tb,
-        )
+        assert (
+            m is not None
+        ), "Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}"
 
     def test_runtime_error(self, fs_env):
         def test():
index 03e94e7628bfe52c770345b4d2cdbb893a08a9d2..00656b38d31972b7e8806e3f96bfab8f166e91c3 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import re
 from io import BytesIO
 
@@ -52,14 +51,14 @@ newstyle_i18n_templates = {
 
 languages = {
     "de": {
-        "missing": u"fehlend",
-        "watch out": u"pass auf",
-        "One user online": u"Ein Benutzer online",
-        "%(user_count)s users online": u"%(user_count)s Benutzer online",
-        "User: %(num)s": u"Benutzer: %(num)s",
-        "User: %(count)s": u"Benutzer: %(count)s",
-        "%(num)s apple": u"%(num)s Apfel",
-        "%(num)s apples": u"%(num)s Äpfel",
+        "missing": "fehlend",
+        "watch out": "pass auf",
+        "One user online": "Ein Benutzer online",
+        "%(user_count)s users online": "%(user_count)s Benutzer online",
+        "User: %(num)s": "Benutzer: %(num)s",
+        "User: %(count)s": "Benutzer: %(count)s",
+        "%(num)s apple": "%(num)s Apfel",
+        "%(num)s apples": "%(num)s Äpfel",
     }
 }
 
@@ -95,7 +94,7 @@ newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True)
 
 
 class ExampleExtension(Extension):
-    tags = set(["test"])
+    tags = {"test"}
     ext_attr = 42
     context_reference_node_cls = nodes.ContextReference
 
@@ -115,12 +114,9 @@ class ExampleExtension(Extension):
         ).set_lineno(next(parser.stream).lineno)
 
     def _dump(self, sandboxed, ext_attr, imported_object, context):
-        return "%s|%s|%s|%s|%s" % (
-            sandboxed,
-            ext_attr,
-            imported_object,
-            context.blocks,
-            context.get("test_var"),
+        return (
+            f"{sandboxed}|{ext_attr}|{imported_object}|{context.blocks}"
+            f"|{context.get('test_var')}"
         )
 
 
@@ -137,8 +133,7 @@ class StreamFilterExtension(Extension):
     def filter_stream(self, stream):
         for token in stream:
             if token.type == "data":
-                for t in self.interpolate(token):
-                    yield t
+                yield from self.interpolate(token)
             else:
                 yield token
 
@@ -166,7 +161,7 @@ class StreamFilterExtension(Extension):
 
 
 @pytest.mark.ext
-class TestExtensions(object):
+class TestExtensions:
     def test_extend_late(self):
         env = Environment()
         env.add_extension("jinja2.ext.autoescape")
@@ -262,11 +257,11 @@ class TestExtensions(object):
         out = t.render()
 
         for value in ("context", "cycler", "filters", "abs", "tests", "!="):
-            assert "'{}'".format(value) in out
+            assert f"'{value}'" in out
 
 
 @pytest.mark.ext
-class TestInternationalization(object):
+class TestInternationalization:
     def test_trans(self):
         tmpl = i18n_env.get_template("child.html")
         assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf"
@@ -347,9 +342,9 @@ class TestInternationalization(object):
             """
         )
         assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], {})) == [
-            (2, "gettext", u"Hello World", []),
-            (3, "gettext", u"Hello World", []),
-            (4, "ngettext", (u"%(users)s user", u"%(users)s users", None), []),
+            (2, "gettext", "Hello World", []),
+            (3, "gettext", "Hello World", []),
+            (4, "ngettext", ("%(users)s user", "%(users)s users", None), []),
         ]
 
     def test_extract_trimmed(self):
@@ -364,9 +359,9 @@ class TestInternationalization(object):
             """
         )
         assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], {})) == [
-            (2, "gettext", u" Hello  \n  World", []),
-            (4, "gettext", u"Hello World", []),
-            (6, "ngettext", (u"%(users)s user", u"%(users)s users", None), []),
+            (2, "gettext", " Hello  \n  World", []),
+            (4, "gettext", "Hello World", []),
+            (6, "ngettext", ("%(users)s user", "%(users)s users", None), []),
         ]
 
     def test_extract_trimmed_option(self):
@@ -382,9 +377,9 @@ class TestInternationalization(object):
         )
         opts = {"trimmed": "true"}
         assert list(babel_extract(source, ("gettext", "ngettext", "_"), [], opts)) == [
-            (2, "gettext", u" Hello  \n  World", []),
-            (4, "gettext", u"Hello World", []),
-            (6, "ngettext", (u"%(users)s user", u"%(users)s users", None), []),
+            (2, "gettext", " Hello  \n  World", []),
+            (4, "gettext", "Hello World", []),
+            (6, "ngettext", ("%(users)s user", "%(users)s users", None), []),
         ]
 
     def test_comment_extract(self):
@@ -402,18 +397,18 @@ class TestInternationalization(object):
         assert list(
             babel_extract(source, ("gettext", "ngettext", "_"), ["trans", ":"], {})
         ) == [
-            (3, "gettext", u"Hello World", ["first"]),
-            (4, "gettext", u"Hello World", ["second"]),
-            (6, "ngettext", (u"%(users)s user", u"%(users)s users", None), ["third"]),
+            (3, "gettext", "Hello World", ["first"]),
+            (4, "gettext", "Hello World", ["second"]),
+            (6, "ngettext", ("%(users)s user", "%(users)s users", None), ["third"]),
         ]
 
 
 @pytest.mark.ext
-class TestScope(object):
+class TestScope:
     def test_basic_scope_behavior(self):
         # This is what the old with statement compiled down to
         class ScopeExt(Extension):
-            tags = set(["scope"])
+            tags = {"scope"}
 
             def parse(self, parser):
                 node = nodes.Scope(lineno=next(parser.stream).lineno)
@@ -443,7 +438,7 @@ class TestScope(object):
 
 
 @pytest.mark.ext
-class TestNewstyleInternationalization(object):
+class TestNewstyleInternationalization:
     def test_trans(self):
         tmpl = newstyle_i18n_env.get_template("child.html")
         assert tmpl.render(LANGUAGE="de") == "<title>fehlend</title>pass auf"
@@ -472,12 +467,12 @@ class TestNewstyleInternationalization(object):
     def test_newstyle_plural(self):
         tmpl = newstyle_i18n_env.get_template("ngettext.html")
         assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apfel"
-        assert tmpl.render(LANGUAGE="de", apples=5) == u"5 Äpfel"
+        assert tmpl.render(LANGUAGE="de", apples=5) == "5 Äpfel"
 
     def test_autoescape_support(self):
         env = Environment(extensions=["jinja2.ext.autoescape", "jinja2.ext.i18n"])
         env.install_gettext_callables(
-            lambda x: u"<strong>Wert: %(name)s</strong>",
+            lambda x: "<strong>Wert: %(name)s</strong>",
             lambda s, p, n: s,
             newstyle=True,
         )
@@ -498,7 +493,7 @@ class TestNewstyleInternationalization(object):
 
     def test_num_used_twice(self):
         tmpl = newstyle_i18n_env.get_template("ngettext_long.html")
-        assert tmpl.render(apples=5, LANGUAGE="de") == u"5 Äpfel"
+        assert tmpl.render(apples=5, LANGUAGE="de") == "5 Äpfel"
 
     def test_num_called_num(self):
         source = newstyle_i18n_env.compile(
@@ -513,7 +508,7 @@ class TestNewstyleInternationalization(object):
         # would work) for better performance.  This only works on the
         # newstyle gettext of course
         assert (
-            re.search(r"u?'\%\(num\)s apple', u?'\%\(num\)s " r"apples', 3", source)
+            re.search(r"u?'%\(num\)s apple', u?'%\(num\)s apples', 3", source)
             is not None
         )
 
@@ -535,7 +530,7 @@ class TestNewstyleInternationalization(object):
 
 
 @pytest.mark.ext
-class TestAutoEscape(object):
+class TestAutoEscape:
     def test_scoped_setting(self):
         env = Environment(extensions=["jinja2.ext.autoescape"], autoescape=True)
         tmpl = env.from_string(
@@ -548,9 +543,9 @@ class TestAutoEscape(object):
         """
         )
         assert tmpl.render().split() == [
-            u"&lt;HelloWorld&gt;",
-            u"<HelloWorld>",
-            u"&lt;HelloWorld&gt;",
+            "&lt;HelloWorld&gt;",
+            "<HelloWorld>",
+            "&lt;HelloWorld&gt;",
         ]
 
         env = Environment(extensions=["jinja2.ext.autoescape"], autoescape=False)
@@ -564,9 +559,9 @@ class TestAutoEscape(object):
         """
         )
         assert tmpl.render().split() == [
-            u"<HelloWorld>",
-            u"&lt;HelloWorld&gt;",
-            u"<HelloWorld>",
+            "<HelloWorld>",
+            "&lt;HelloWorld&gt;",
+            "<HelloWorld>",
         ]
 
     def test_nonvolatile(self):
@@ -623,7 +618,7 @@ class TestAutoEscape(object):
 
     def test_overlay_scopes(self):
         class MagicScopeExtension(Extension):
-            tags = set(["overlay"])
+            tags = {"overlay"}
 
             def parse(self, parser):
                 node = nodes.OverlayScope(lineno=next(parser.stream).lineno)
index c7c43e8b8438327c6b204a06df8bc1b361eddd4d..4f36458a7f9b8e72cb0d8457214f6a754baf7d07 100644 (file)
@@ -3,8 +3,9 @@ import pytest
 from jinja2 import Template
 
 
+# Python < 3.7
 def test_generator_stop():
-    class X(object):
+    class X:
         def __getattr__(self, name):
             raise StopIteration()
 
index 03db0392893b3a04a14ccf85464ec73610a06168..bda5821fcaa85ca376f7a785e0dd753f0b81c505 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import random
 from collections import namedtuple
 
@@ -8,7 +7,7 @@ from jinja2 import Environment
 from jinja2 import Markup
 
 
-class Magic(object):
+class Magic:
     def __init__(self, value):
         self.value = value
 
@@ -16,17 +15,17 @@ class Magic(object):
         return str(self.value)
 
 
-class Magic2(object):
+class Magic2:
     def __init__(self, value1, value2):
         self.value1 = value1
         self.value2 = value2
 
     def __str__(self):
-        return u"(%s,%s)" % (str(self.value1), str(self.value2))
+        return f"({self.value1},{self.value2})"
 
 
 @pytest.mark.filter
-class TestFilter(object):
+class TestFilter:
     def test_filter_calling(self, env):
         rv = env.call_filter("sum", [1, 2, 3])
         assert rv == 6
@@ -56,7 +55,7 @@ class TestFilter(object):
         ),
     )
     def test_dictsort(self, env, args, expect):
-        t = env.from_string("{{{{ foo|dictsort({args}) }}}}".format(args=args))
+        t = env.from_string(f"{{{{ foo|dictsort({args}) }}}}")
         out = t.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3})
         assert out == expect
 
@@ -139,18 +138,18 @@ class TestFilter(object):
         assert out == "0"
 
     @pytest.mark.parametrize(
-        ("value", "expect"), (("42", "42.0"), ("abc", "0.0"), ("32.32", "32.32"),)
+        ("value", "expect"), (("42", "42.0"), ("abc", "0.0"), ("32.32", "32.32"))
     )
     def test_float(self, env, value, expect):
-        t = env.from_string("{{ '%s'|float }}" % value)
-        assert t.render() == expect
+        t = env.from_string("{{ value|float }}")
+        assert t.render(value=value) == expect
 
     def test_float_default(self, env):
         t = env.from_string("{{ value|float(default=1.0) }}")
         assert t.render(value="abc") == "1.0"
 
     def test_format(self, env):
-        tmpl = env.from_string("""{{ "%s|%s"|format("a", "b") }}""")
+        tmpl = env.from_string("{{ '%s|%s'|format('a', 'b') }}")
         out = tmpl.render()
         assert out == "a|b"
 
@@ -193,23 +192,23 @@ class TestFilter(object):
         ),
     )
     def test_int(self, env, value, expect):
-        t = env.from_string("{{ '%s'|int }}" % value)
-        assert t.render() == expect
+        t = env.from_string("{{ value|int }}")
+        assert t.render(value=value) == expect
 
     @pytest.mark.parametrize(
         ("value", "base", "expect"),
         (("0x4d32", 16, "19762"), ("011", 8, "9"), ("0x33Z", 16, "0"),),
     )
     def test_int_base(self, env, value, base, expect):
-        t = env.from_string("{{ '%s'|int(base=%d) }}" % (value, base))
-        assert t.render() == expect
+        t = env.from_string("{{ value|int(base=base) }}")
+        assert t.render(value=value, base=base) == expect
 
     def test_int_default(self, env):
         t = env.from_string("{{ value|int(default=1) }}")
         assert t.render(value="abc") == "1"
 
     def test_int_special_method(self, env):
-        class IntIsh(object):
+        class IntIsh:
             def __int__(self):
                 return 42
 
@@ -318,20 +317,19 @@ class TestFilter(object):
             "{{ smalldata|truncate(15) }}"
         )
         out = tmpl.render(data="foobar baz bar" * 1000, smalldata="foobar baz bar")
-        msg = "Current output: %s" % out
-        assert out == "foobar baz b>>>|foobar baz>>>|foobar baz bar", msg
+        assert out == "foobar baz b>>>|foobar baz>>>|foobar baz bar"
 
     def test_truncate_very_short(self, env):
         tmpl = env.from_string(
             '{{ "foo bar baz"|truncate(9) }}|{{ "foo bar baz"|truncate(9, true) }}'
         )
         out = tmpl.render()
-        assert out == "foo bar baz|foo bar baz", out
+        assert out == "foo bar baz|foo bar baz"
 
     def test_truncate_end_length(self, env):
         tmpl = env.from_string('{{ "Joel is a slug"|truncate(7, true) }}')
         out = tmpl.render()
-        assert out == "Joel...", "Current output: %s" % out
+        assert out == "Joel..."
 
     def test_upper(self, env):
         tmpl = env.from_string('{{ "foo"|upper }}')
@@ -580,7 +578,7 @@ class TestFilter(object):
 
     def test_forceescape(self, env):
         tmpl = env.from_string("{{ x|forceescape }}")
-        assert tmpl.render(x=Markup("<div />")) == u"&lt;div /&gt;"
+        assert tmpl.render(x=Markup("<div />")) == "&lt;div /&gt;"
 
     def test_safe(self, env):
         env = Environment(autoescape=True)
@@ -593,10 +591,10 @@ class TestFilter(object):
         ("value", "expect"),
         [
             ("Hello, world!", "Hello%2C%20world%21"),
-            (u"Hello, world\u203d", "Hello%2C%20world%E2%80%BD"),
+            ("Hello, world\u203d", "Hello%2C%20world%E2%80%BD"),
             ({"f": 1}, "f=1"),
             ([("f", 1), ("z", 2)], "f=1&amp;z=2"),
-            ({u"\u203d": 1}, "%E2%80%BD=1"),
+            ({"\u203d": 1}, "%E2%80%BD=1"),
             ({0: 1}, "0=1"),
             ([("a b/c", "a b/c")], "a+b%2Fc=a+b%2Fc"),
             ("a b/c", "a%20b/c"),
index 4c79ee6c4718d2989821081de55e643f7deb72c4..8a884671dfaa30f774e56f310e5311bef74c8840 100644 (file)
@@ -38,7 +38,7 @@ def test_basics():
 
 def test_complex():
     title_block = nodes.Block(
-        "title", [nodes.Output([nodes.TemplateData(u"Page Title")])], False
+        "title", [nodes.Output([nodes.TemplateData("Page Title")])], False
     )
 
     render_title_macro = nodes.Macro(
@@ -48,11 +48,11 @@ def test_complex():
         [
             nodes.Output(
                 [
-                    nodes.TemplateData(u'\n  <div class="title">\n    <h1>'),
+                    nodes.TemplateData('\n  <div class="title">\n    <h1>'),
                     nodes.Name("title", "load"),
-                    nodes.TemplateData(u"</h1>\n    <p>"),
+                    nodes.TemplateData("</h1>\n    <p>"),
                     nodes.Name("subtitle", "load"),
-                    nodes.TemplateData(u"</p>\n    "),
+                    nodes.TemplateData("</p>\n    "),
                 ]
             ),
             nodes.Assign(
@@ -60,9 +60,9 @@ def test_complex():
             ),
             nodes.Output(
                 [
-                    nodes.TemplateData(u"\n    <p>"),
+                    nodes.TemplateData("\n    <p>"),
                     nodes.Name("subtitle", "load"),
-                    nodes.TemplateData(u"</p>\n  </div>\n"),
+                    nodes.TemplateData("</p>\n  </div>\n"),
                     nodes.If(
                         nodes.Name("something", "load"),
                         [
@@ -104,13 +104,13 @@ def test_complex():
         [
             nodes.Output(
                 [
-                    nodes.TemplateData(u"\n    <li>"),
+                    nodes.TemplateData("\n    <li>"),
                     nodes.Name("item", "load"),
-                    nodes.TemplateData(u"</li>\n    <span>"),
+                    nodes.TemplateData("</li>\n    <span>"),
                 ]
             ),
             nodes.Include(nodes.Const("helper.html"), True, False),
-            nodes.Output([nodes.TemplateData(u"</span>\n  ")]),
+            nodes.Output([nodes.TemplateData("</span>\n  ")]),
         ],
         [],
         None,
@@ -122,7 +122,7 @@ def test_complex():
         [
             nodes.Output(
                 [
-                    nodes.TemplateData(u"\n  "),
+                    nodes.TemplateData("\n  "),
                     nodes.Call(
                         nodes.Name("render_title", "load"),
                         [nodes.Name("item", "load")],
@@ -130,11 +130,11 @@ def test_complex():
                         None,
                         None,
                     ),
-                    nodes.TemplateData(u"\n  <ul>\n  "),
+                    nodes.TemplateData("\n  <ul>\n  "),
                 ]
             ),
             for_loop,
-            nodes.Output([nodes.TemplateData(u"\n  </ul>\n")]),
+            nodes.Output([nodes.TemplateData("\n  </ul>\n")]),
         ],
         False,
     )
@@ -155,7 +155,7 @@ def test_complex():
     assert tmpl_sym.loads == {
         "l_0_render_title": ("undefined", None),
     }
-    assert tmpl_sym.stores == set(["render_title"])
+    assert tmpl_sym.stores == {"render_title"}
     assert tmpl_sym.dump_stores() == {
         "render_title": "l_0_render_title",
     }
@@ -173,7 +173,7 @@ def test_complex():
         "l_1_title": ("param", None),
         "l_1_title_upper": ("resolve", "title_upper"),
     }
-    assert macro_sym.stores == set(["title", "title_upper", "subtitle"])
+    assert macro_sym.stores == {"title", "title_upper", "subtitle"}
     assert macro_sym.find_ref("render_title") == "l_0_render_title"
     assert macro_sym.dump_stores() == {
         "title": "l_1_title",
@@ -193,7 +193,7 @@ def test_complex():
         "l_0_seq": ("resolve", "seq"),
         "l_0_render_title": ("resolve", "render_title"),
     }
-    assert body_sym.stores == set([])
+    assert body_sym.stores == set()
 
     for_sym = symbols_for_node(for_loop, body_sym)
     assert for_sym.refs == {
@@ -202,7 +202,7 @@ def test_complex():
     assert for_sym.loads == {
         "l_1_item": ("param", None),
     }
-    assert for_sym.stores == set(["item"])
+    assert for_sym.stores == {"item"}
     assert for_sym.dump_stores() == {
         "item": "l_1_item",
     }
@@ -222,7 +222,7 @@ def test_if_branching_stores():
 
     sym = symbols_for_node(tmpl)
     assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"}
-    assert sym.stores == set(["variable"])
+    assert sym.stores == {"variable"}
     assert sym.loads == {
         "l_0_variable": ("resolve", "variable"),
         "l_0_expression": ("resolve", "expression"),
@@ -247,7 +247,7 @@ def test_if_branching_stores_undefined():
 
     sym = symbols_for_node(tmpl)
     assert sym.refs == {"variable": "l_0_variable", "expression": "l_0_expression"}
-    assert sym.stores == set(["variable"])
+    assert sym.stores == {"variable"}
     assert sym.loads == {
         "l_0_variable": ("undefined", None),
         "l_0_expression": ("resolve", "expression"),
@@ -281,7 +281,7 @@ def test_if_branching_multi_scope():
 
     tmpl_sym = symbols_for_node(tmpl)
     for_sym = symbols_for_node(for_loop, tmpl_sym)
-    assert for_sym.stores == set(["item", "x"])
+    assert for_sym.stores == {"item", "x"}
     assert for_sym.loads == {
         "l_1_x": ("alias", "l_0_x"),
         "l_1_item": ("param", None),
index 0dae2173f22625a42d5443638377c90bb48d5717..ca4de705540ba14b0fa2c5b2a5a6635f0a8ab36d 100644 (file)
@@ -1,11 +1,11 @@
-# -*- coding: utf-8 -*-
 import pytest
 
-from jinja2 import DictLoader
-from jinja2 import Environment
+from jinja2.environment import Environment
 from jinja2.exceptions import TemplateNotFound
 from jinja2.exceptions import TemplatesNotFound
 from jinja2.exceptions import TemplateSyntaxError
+from jinja2.exceptions import UndefinedError
+from jinja2.loaders import DictLoader
 
 
 @pytest.fixture
@@ -24,7 +24,7 @@ def test_env():
 
 
 @pytest.mark.imports
-class TestImports(object):
+class TestImports:
     def test_context_imports(self, test_env):
         t = test_env.from_string('{% import "module" as m %}{{ m.test() }}')
         assert t.render(foo=42) == "[|23]"
@@ -93,10 +93,16 @@ class TestImports(object):
         assert m.variable == 42
         assert not hasattr(m, "notthere")
 
+    def test_not_exported(self, test_env):
+        t = test_env.from_string("{% from 'module' import nothing %}{{ nothing() }}")
+
+        with pytest.raises(UndefinedError, match="does not export the requested name"):
+            t.render()
+
 
 @pytest.mark.imports
 @pytest.mark.includes
-class TestIncludes(object):
+class TestIncludes:
     def test_context_include(self, test_env):
         t = test_env.from_string('{% include "header" %}')
         assert t.render(foo=42) == "[42|23]"
index 92f66e04fb9d18b4145480705649dc1b4c7306f9..331427e5285bb907be55540cf23588c850ae2954 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pytest
 
 from jinja2 import DictLoader
@@ -75,7 +74,7 @@ def env():
 
 
 @pytest.mark.inheritance
-class TestInheritance(object):
+class TestInheritance:
     def test_layout(self, env):
         tmpl = env.get_template("layout")
         assert tmpl.render() == (
@@ -158,7 +157,7 @@ class TestInheritance(object):
         )
         tmpl = env.get_template("child")
         for m in range(1, 3):
-            assert tmpl.render(master="master%d" % m) == "MASTER%dCHILD" % m
+            assert tmpl.render(master=f"master{m}") == f"MASTER{m}CHILD"
 
     def test_multi_inheritance(self, env):
         env = Environment(
@@ -234,7 +233,7 @@ class TestInheritance(object):
 
 
 @pytest.mark.inheritance
-class TestBugFix(object):
+class TestBugFix:
     def test_fixed_macro_scoping_bug(self, env):
         assert (
             Environment(
@@ -276,7 +275,7 @@ class TestBugFix(object):
             .get_template("test.html")
             .render()
             .split()
-            == [u"outer_box", u"my_macro"]
+            == ["outer_box", "my_macro"]
         )
 
     def test_double_extends(self, env):
index 75dd7552a247582d90d6edc04d398dc3a0f5419d..fabbd36ee9c51982420d256bd738576dfaef70cf 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pytest
 
 from jinja2 import Environment
@@ -15,7 +14,7 @@ from jinja2.lexer import TokenStream
 
 @pytest.mark.lexnparse
 @pytest.mark.tokenstream
-class TestTokenStream(object):
+class TestTokenStream:
     test_tokens = [
         Token(1, TOKEN_BLOCK_BEGIN, ""),
         Token(2, TOKEN_BLOCK_END, ""),
@@ -45,7 +44,7 @@ class TestTokenStream(object):
 
 @pytest.mark.lexnparse
 @pytest.mark.lexer
-class TestLexer(object):
+class TestLexer:
     def test_raw1(self, env):
         tmpl = env.from_string(
             "{% raw %}foo{% endraw %}|"
@@ -96,16 +95,16 @@ class TestLexer(object):
         )
 
     def test_string_escapes(self, env):
-        for char in u"\0", u"\u2668", u"\xe4", u"\t", u"\r", u"\n":
-            tmpl = env.from_string("{{ %s }}" % repr(char))
+        for char in "\0", "\u2668", "\xe4", "\t", "\r", "\n":
+            tmpl = env.from_string(f"{{{{ {char!r} }}}}")
             assert tmpl.render() == char
-        assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u"\u2668"
+        assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == "\u2668"
 
     def test_bytefallback(self, env):
         from pprint import pformat
 
-        tmpl = env.from_string(u"""{{ 'foo'|pprint }}|{{ 'bär'|pprint }}""")
-        assert tmpl.render() == pformat("foo") + "|" + pformat(u"bär")
+        tmpl = env.from_string("""{{ 'foo'|pprint }}|{{ 'bär'|pprint }}""")
+        assert tmpl.render() == pformat("foo") + "|" + pformat("bär")
 
     def test_operators(self, env):
         from jinja2.lexer import operators
@@ -113,7 +112,7 @@ class TestLexer(object):
         for test, expect in operators.items():
             if test in "([{}])":
                 continue
-            stream = env.lexer.tokenize("{{ %s }}" % test)
+            stream = env.lexer.tokenize(f"{{{{ {test} }}}}")
             next(stream)
             assert stream.current.type == expect
 
@@ -141,26 +140,26 @@ class TestLexer(object):
     @pytest.mark.parametrize(
         ("name", "valid"),
         [
-            (u"foo", True),
-            (u"föö", True),
-            (u"き", True),
-            (u"_", True),
-            (u"1a", False),  # invalid ascii start
-            (u"a-", False),  # invalid ascii continue
-            (u"🐍", False),  # invalid unicode start
-            (u"a🐍", False),  # invalid unicode continue
+            ("foo", True),
+            ("föö", True),
+            ("き", True),
+            ("_", True),
+            ("1a", False),  # invalid ascii start
+            ("a-", False),  # invalid ascii continue
+            ("\U0001f40da", False),  # invalid unicode start
+            ("a🐍\U0001f40d", False),  # invalid unicode continue
             # start characters not matched by \w
-            (u"\u1885", True),
-            (u"\u1886", True),
-            (u"\u2118", True),
-            (u"\u212e", True),
+            ("\u1885", True),
+            ("\u1886", True),
+            ("\u2118", True),
+            ("\u212e", True),
             # continue character not matched by \w
-            (u"\xb7", False),
-            (u"a\xb7", True),
+            ("\xb7", False),
+            ("a\xb7", True),
         ],
     )
     def test_name(self, env, name, valid):
-        t = u"{{ " + name + u" }}"
+        t = "{{ " + name + " }}"
 
         if valid:
             # valid for version being tested, shouldn't raise
@@ -171,7 +170,7 @@ class TestLexer(object):
 
 @pytest.mark.lexnparse
 @pytest.mark.parser
-class TestParser(object):
+class TestParser:
     def test_php_syntax(self, env):
         env = Environment("<?", "?>", "<?=", "?>", "<!--", "-->")
         tmpl = env.from_string(
@@ -306,7 +305,7 @@ and bar comment #}
 
 @pytest.mark.lexnparse
 @pytest.mark.syntax
-class TestSyntax(object):
+class TestSyntax:
     def test_call(self, env):
         env = Environment()
         env.globals["foo"] = lambda a, b, c, e, g: a + b + c + e + g
@@ -357,7 +356,7 @@ class TestSyntax(object):
         ],
     )
     def test_compare(self, env, a, op, b):
-        t = env.from_string("{{ %d %s %d }}" % (a, op, b))
+        t = env.from_string(f"{{{{ {a} {op} {b} }}}}")
         assert t.render() == "True"
 
     def test_compare_parens(self, env):
@@ -385,7 +384,7 @@ class TestSyntax(object):
 
     @pytest.mark.parametrize("value", ("[]", "{}", "()"))
     def test_collection_literal(self, env, value):
-        t = env.from_string("{{ %s }}" % value)
+        t = env.from_string(f"{{{{ {value} }}}}")
         assert t.render() == value
 
     @pytest.mark.parametrize(
@@ -406,7 +405,7 @@ class TestSyntax(object):
         ),
     )
     def test_numeric_literal(self, env, value, expect):
-        t = env.from_string("{{ %s }}" % value)
+        t = env.from_string(f"{{{{ {value} }}}}")
         assert t.render() == expect
 
     def test_bool(self, env):
@@ -460,11 +459,10 @@ class TestSyntax(object):
         ]
         for should_fail, sig in tests:
             if should_fail:
-                pytest.raises(
-                    TemplateSyntaxError, env.from_string, "{{ foo(%s) }}" % sig
-                )
+                with pytest.raises(TemplateSyntaxError):
+                    env.from_string(f"{{{{ foo({sig}) }}}}")
             else:
-                env.from_string("foo(%s)" % sig)
+                env.from_string(f"foo({sig})")
 
     def test_tuple_expr(self, env):
         for tmpl in [
@@ -491,11 +489,11 @@ class TestSyntax(object):
 
     def test_constant_casing(self, env):
         for const in True, False, None:
+            const = str(const)
             tmpl = env.from_string(
-                "{{ %s }}|{{ %s }}|{{ %s }}"
-                % (str(const), str(const).lower(), str(const).upper())
+                f"{{{{ {const} }}}}|{{{{ {const.lower()} }}}}|{{{{ {const.upper()} }}}}"
             )
-            assert tmpl.render() == "%s|%s|" % (const, const)
+            assert tmpl.render() == f"{const}|{const}|"
 
     def test_test_chaining(self, env):
         pytest.raises(
@@ -517,12 +515,12 @@ class TestSyntax(object):
         assert tmpl.render() == "5"
 
     def test_implicit_subscribed_tuple(self, env):
-        class Foo(object):
+        class Foo:
             def __getitem__(self, x):
                 return x
 
         t = env.from_string("{{ foo[1, 2] }}")
-        assert t.render(foo=Foo()) == u"(1, 2)"
+        assert t.render(foo=Foo()) == "(1, 2)"
 
     def test_raw2(self, env):
         tmpl = env.from_string("{% raw %}{{ FOO }} and {% BAR %}{% endraw %}")
@@ -563,7 +561,7 @@ class TestSyntax(object):
 
 @pytest.mark.lexnparse
 @pytest.mark.lstripblocks
-class TestLstripBlocks(object):
+class TestLstripBlocks:
     def test_lstrip(self, env):
         env = Environment(lstrip_blocks=True, trim_blocks=False)
         tmpl = env.from_string("""    {% if True %}\n    {% endif %}""")
@@ -683,7 +681,7 @@ hello
 ${item} ## the rest of the stuff
    <% endfor %>"""
         )
-        assert tmpl.render(seq=range(5)) == "".join("%s\n" % x for x in range(5))
+        assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5))
 
     def test_lstrip_angle_bracket_compact(self, env):
         env = Environment(
@@ -705,7 +703,7 @@ ${item} ## the rest of the stuff
 ${item} ## the rest of the stuff
    <%endfor%>"""
         )
-        assert tmpl.render(seq=range(5)) == "".join("%s\n" % x for x in range(5))
+        assert tmpl.render(seq=range(5)) == "".join(f"{x}\n" for x in range(5))
 
     def test_php_syntax_with_manual(self, env):
         env = Environment(
@@ -731,9 +729,7 @@ ${item} ## the rest of the stuff
         <?= item ?>
     <? endfor ?>"""
         )
-        assert tmpl.render(seq=range(5)) == "".join(
-            "        %s\n" % x for x in range(5)
-        )
+        assert tmpl.render(seq=range(5)) == "".join(f"        {x}\n" for x in range(5))
 
     def test_php_syntax_compact(self, env):
         env = Environment(
@@ -746,19 +742,12 @@ ${item} ## the rest of the stuff
         <?=item?>
     <?endfor?>"""
         )
-        assert tmpl.render(seq=range(5)) == "".join(
-            "        %s\n" % x for x in range(5)
-        )
+        assert tmpl.render(seq=range(5)) == "".join(f"        {x}\n" for x in range(5))
 
     def test_erb_syntax(self, env):
         env = Environment(
             "<%", "%>", "<%=", "%>", "<%#", "%>", lstrip_blocks=True, trim_blocks=True
         )
-        # env.from_string('')
-        # for n,r in env.lexer.rules.iteritems():
-        #    print n
-        # print env.lexer.rules['root'][0][0].pattern
-        # print "'%s'" % tmpl.render(seq=range(5))
         tmpl = env.from_string(
             """\
 <%# I'm a comment, I'm not interesting %>
@@ -767,7 +756,7 @@ ${item} ## the rest of the stuff
     <% endfor %>
 """
         )
-        assert tmpl.render(seq=range(5)) == "".join("    %s\n" % x for x in range(5))
+        assert tmpl.render(seq=range(5)) == "".join(f"    {x}\n" for x in range(5))
 
     def test_erb_syntax_with_manual(self, env):
         env = Environment(
index 84bd175d21957a7db47ce9f0954fa3b12b1788a2..0ad95383bfb7f21bf034224b0fd81d72e85280b8 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import os
 import platform
 import shutil
@@ -17,7 +16,7 @@ from jinja2.loaders import split_template_path
 
 
 @pytest.mark.loaders
-class TestLoaders(object):
+class TestLoaders:
     def test_dict_loader(self, dict_loader):
         env = Environment(loader=dict_loader)
         tmpl = env.get_template("justdict.html")
@@ -67,7 +66,7 @@ class TestLoaders(object):
 
         class TestLoader(loaders.BaseLoader):
             def get_source(self, environment, template):
-                return u"foo", None, lambda: not changed
+                return "foo", None, lambda: not changed
 
         env = Environment(loader=TestLoader(), cache_size=-1)
         tmpl = env.get_template("template")
@@ -118,7 +117,7 @@ class TestLoaders(object):
 
 @pytest.mark.loaders
 @pytest.mark.filesystemloader
-class TestFileSystemLoader(object):
+class TestFileSystemLoader:
     searchpath = os.path.join(
         os.path.dirname(os.path.abspath(__file__)), "res", "templates"
     )
@@ -168,8 +167,8 @@ class TestFileSystemLoader(object):
     @pytest.mark.parametrize(
         ("encoding", "expect"),
         [
-            ("utf-8", u"文字化け"),
-            ("iso-8859-1", u"æ\x96\x87\xe5\xad\x97\xe5\x8c\x96\xe3\x81\x91"),
+            ("utf-8", "文字化け"),
+            ("iso-8859-1", "æ\x96\x87\xe5\xad\x97\xe5\x8c\x96\xe3\x81\x91"),
         ],
     )
     def test_uses_specified_encoding(self, encoding, expect):
@@ -181,7 +180,7 @@ class TestFileSystemLoader(object):
 
 @pytest.mark.loaders
 @pytest.mark.moduleloader
-class TestModuleLoader(object):
+class TestModuleLoader:
     archive = None
 
     def compile_down(self, prefix_loader, zip="deflated"):
@@ -348,7 +347,7 @@ def test_package_zip_source(package_zip_loader, template, expect):
 
 @pytest.mark.xfail(
     platform.python_implementation() == "PyPy",
-    reason="PyPy's zipimporter doesn't have a _files attribute.",
+    reason="PyPy's zipimporter doesn't have a '_files' attribute.",
     raises=TypeError,
 )
 def test_package_zip_list(package_zip_loader):
index 993a922b7fdf989624a7081b90e926c82a8dd0d7..7205bde71a98ff53fdf74981968319dec221f8e5 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pytest
 
 from jinja2 import DictLoader
@@ -11,7 +10,7 @@ from jinja2 import TemplateSyntaxError
 
 
 @pytest.mark.regression
-class TestCorner(object):
+class TestCorner:
     def test_assigned_scoping(self, env):
         t = env.from_string(
             """
@@ -83,7 +82,7 @@ class TestCorner(object):
 
 
 @pytest.mark.regression
-class TestBug(object):
+class TestBug:
     def test_keyword_folding(self, env):
         env = Environment()
         env.filters["testing"] = lambda value, some: value + some
@@ -192,7 +191,7 @@ class TestBug(object):
         """
         )
         rv = t.render(foo=[1]).strip()
-        assert rv == u"1"
+        assert rv == "1"
 
     def test_call_with_args(self, env):
         t = Template(
@@ -226,13 +225,13 @@ class TestBug(object):
                 ]
             ).splitlines()
         ] == [
-            u"<ul><li><p>apo</p><dl>",
-            u"<dl>Realname</dl>",
-            u"<dd>something else</dd>",
-            u"<dl>Description</dl>",
-            u"<dd>test</dd>",
-            u"</dl>",
-            u"</li></ul>",
+            "<ul><li><p>apo</p><dl>",
+            "<dl>Realname</dl>",
+            "<dd>something else</dd>",
+            "<dl>Description</dl>",
+            "<dd>test</dd>",
+            "</dl>",
+            "</li></ul>",
         ]
 
     def test_empty_if_condition_fails(self, env):
@@ -294,7 +293,7 @@ class TestBug(object):
     def test_contextfunction_callable_classes(self, env):
         from jinja2.utils import contextfunction
 
-        class CallableClass(object):
+        class CallableClass:
             @contextfunction
             def __call__(self, ctx):
                 return ctx.resolve("hello")
@@ -595,7 +594,7 @@ class TestBug(object):
             def resolve(self, name):
                 if name == "foo":
                     return 42
-                return super(MyContext, self).resolve(name)
+                return super().resolve(name)
 
         x = MyContext(env, parent={"bar": 23}, name="foo", blocks={})
         assert x._legacy_resolve_mode
index 5e4686c083141d3ffa48d28136daa151ce7f8e52..db95899d9d331176e39e6d79279003438193aaf2 100644 (file)
@@ -62,7 +62,7 @@ def test_mock_not_contextfunction():
     as a ``contextfunction``.
     """
 
-    class Calc(object):
+    class Calc:
         def __getattr__(self, item):
             return object()
 
index 43057ec695e4afa02cdca117fa65255c03040a71..c3b3f9c15188b623b953ada117d9ecd0c9022321 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pytest
 
 from jinja2 import Environment
@@ -12,7 +11,7 @@ from jinja2.sandbox import SandboxedEnvironment
 from jinja2.sandbox import unsafe
 
 
-class PrivateStuff(object):
+class PrivateStuff:
     def bar(self):
         return 23
 
@@ -24,7 +23,7 @@ class PrivateStuff(object):
         return "PrivateStuff"
 
 
-class PublicStuff(object):
+class PublicStuff:
     def bar(self):
         return 23
 
@@ -36,7 +35,7 @@ class PublicStuff(object):
 
 
 @pytest.mark.sandbox
-class TestSandbox(object):
+class TestSandbox:
     def test_unsafe(self, env):
         env = SandboxedEnvironment()
         pytest.raises(
@@ -105,10 +104,10 @@ class TestSandbox(object):
         for expr, ctx, rv in ("1 + 2", {}, "3"), ("a + 2", {"a": 2}, "4"):
             env = SandboxedEnvironment()
             env.binop_table["+"] = disable_op
-            t = env.from_string("{{ %s }}" % expr)
+            t = env.from_string(f"{{{{ {expr} }}}}")
             assert t.render(ctx) == rv
             env.intercepted_binops = frozenset(["+"])
-            t = env.from_string("{{ %s }}" % expr)
+            t = env.from_string(f"{{{{ {expr} }}}}")
             with pytest.raises(TemplateRuntimeError):
                 t.render(ctx)
 
@@ -119,16 +118,16 @@ class TestSandbox(object):
         for expr, ctx, rv in ("-1", {}, "-1"), ("-a", {"a": 2}, "-2"):
             env = SandboxedEnvironment()
             env.unop_table["-"] = disable_op
-            t = env.from_string("{{ %s }}" % expr)
+            t = env.from_string(f"{{{{ {expr} }}}}")
             assert t.render(ctx) == rv
             env.intercepted_unops = frozenset(["-"])
-            t = env.from_string("{{ %s }}" % expr)
+            t = env.from_string(f"{{{{ {expr} }}}}")
             with pytest.raises(TemplateRuntimeError):
                 t.render(ctx)
 
 
 @pytest.mark.sandbox
-class TestStringFormat(object):
+class TestStringFormat:
     def test_basic_format_safety(self):
         env = SandboxedEnvironment()
         t = env.from_string('{{ "a{0.__class__}b".format(42) }}')
@@ -151,7 +150,7 @@ class TestStringFormat(object):
 
 
 @pytest.mark.sandbox
-class TestStringFormatMap(object):
+class TestStringFormatMap:
     def test_basic_format_safety(self):
         env = SandboxedEnvironment()
         t = env.from_string('{{ "a{x.__class__}b".format_map({"x":42}) }}')
index 42595e8fd6cee1e6e0058a9b0c62db21bfa5abd7..dbf6e6013ae221620cfbe1dccda7f4544bbf8107 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pytest
 
 from jinja2 import Environment
@@ -10,7 +9,7 @@ class MyDict(dict):
 
 
 @pytest.mark.test_tests
-class TestTestsCase(object):
+class TestTestsCase:
     def test_defined(self, env):
         tmpl = env.from_string("{{ missing is defined }}|{{ true is defined }}")
         assert tmpl.render() == "False|True"
@@ -111,7 +110,7 @@ class TestTestsCase(object):
         ),
     )
     def test_types(self, env, op, expect):
-        t = env.from_string("{{{{ {op} }}}}".format(op=op))
+        t = env.from_string(f"{{{{ {op} }}}}")
         assert t.render(mydict=MyDict(), complex=complex(1, 2)) == str(expect)
 
     def test_upper(self, env):
@@ -152,7 +151,7 @@ class TestTestsCase(object):
         ),
     )
     def test_compare_aliases(self, env, op, expect):
-        t = env.from_string("{{{{ 2 is {op} }}}}".format(op=op))
+        t = env.from_string(f"{{{{ 2 is {op} }}}}")
         assert t.render() == str(expect)
 
     def test_sameas(self, env):
index 9e75c6dcb3192eee8f5db4644df845922b037d40..750002f6c1ef15000b869c013bdf6fe5c0eda4b9 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import pickle
 import random
 from collections import deque
@@ -18,7 +17,7 @@ from jinja2.utils import urlize
 
 @pytest.mark.utils
 @pytest.mark.lrucache
-class TestLRUCache(object):
+class TestLRUCache:
     def test_simple(self):
         d = LRUCache(3)
         d["a"] = 1
@@ -80,7 +79,7 @@ class TestLRUCache(object):
         d["b"] = 2
         d["c"] = 3
         # Sort the strings - mapping is unordered
-        assert sorted(repr(d)) == sorted(u"<LRUCache {'a': 1, 'b': 2, 'c': 3}>")
+        assert sorted(repr(d)) == sorted("<LRUCache {'a': 1, 'b': 2, 'c': 3}>")
 
     def test_items(self):
         """Test various items, keys, values and iterators of LRUCache."""
@@ -113,9 +112,9 @@ class TestLRUCache(object):
 
 @pytest.mark.utils
 @pytest.mark.helpers
-class TestHelpers(object):
+class TestHelpers:
     def test_object_type_repr(self):
-        class X(object):
+        class X:
             pass
 
         assert object_type_repr(42) == "int object"
@@ -143,7 +142,7 @@ class TestHelpers(object):
 
 @pytest.mark.utils
 @pytest.mark.escapeUrlizeTarget
-class TestEscapeUrlizeTarget(object):
+class TestEscapeUrlizeTarget:
     def test_escape_urlize_target(self):
         url = "http://example.org"
         target = "<script>"
@@ -156,7 +155,7 @@ class TestEscapeUrlizeTarget(object):
 
 @pytest.mark.utils
 @pytest.mark.loremIpsum
-class TestLoremIpsum(object):
+class TestLoremIpsum:
     def test_lorem_ipsum_markup(self):
         """Test that output of lorem_ipsum is Markup by default."""
         assert isinstance(generate_lorem_ipsum(), Markup)
@@ -167,7 +166,7 @@ class TestLoremIpsum(object):
 
     def test_lorem_ipsum_n(self):
         """Test that the n (number of lines) works as expected."""
-        assert generate_lorem_ipsum(n=0, html=False) == u""
+        assert generate_lorem_ipsum(n=0, html=False) == ""
         for n in range(1, 50):
             assert generate_lorem_ipsum(n=n, html=False).count("\n") == (n - 1) * 2
 
@@ -188,7 +187,7 @@ class TestLoremIpsum(object):
 
 def test_missing():
     """Test the repr of missing."""
-    assert repr(missing) == u"missing"
+    assert repr(missing) == "missing"
 
 
 def test_consume():