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
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")]
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)
-# -*- coding: utf-8 -*-
import re
from jinja2.exceptions import TemplateSyntaxError
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")
-from __future__ import print_function
-
from jinja2 import Environment
env = Environment(
-from __future__ import print_function
-
from jinja2 import Environment
from jinja2.loaders import FileSystemLoader
-from __future__ import print_function
-
from jinja2 import Environment
from jinja2.loaders import DictLoader
-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 %}
{{ 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 %}
""",
}
-from __future__ import print_function
-
from jinja2 import Environment
env = Environment(
-from __future__ import print_function
-
from jinja2 import Environment
tmpl = Environment().from_string(
-from __future__ import print_function
-
from jinja2 import Environment
env = Environment(extensions=["jinja2.ext.i18n"])
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(
"""\
-#!/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
out.append(a)
out.append(b)
else:
- out.append("{}-{}".format(a, b))
+ out.append(f"{a}-{b}")
return "".join(out)
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")
@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)
-# -*- coding: utf-8 -*-
"""The code for async support. Importing this patches Jinja."""
import asyncio
import inspect
-# -*- 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.
)
-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.
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`.
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:
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")
# 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))
-# -*- coding: utf-8 -*-
"""Compiles nodes from the parser into Python code."""
from collections import namedtuple
from functools import update_wrapper
return visitor.undeclared
-class MacroRef(object):
+class MacroRef:
def __init__(self, node):
self.node = node
self.accesses_caller = False
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
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
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."""
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."""
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`."""
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)
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)
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 = []
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:
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."""
# 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)
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()
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.
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."""
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
# 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.
# 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
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()
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
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")
# 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,
)
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)
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."""
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)
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.
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)")
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:")
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:
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()")
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:
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):
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)
# 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)
# 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":
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:
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)
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):
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()
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)
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
# `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)
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):
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("[")
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(")")
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)
@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(", ")
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
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("))")
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:
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:
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(")")
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("(")
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:
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(")")
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])
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)
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)
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})")
-# -*- 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
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
-# -*- 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
-# -*- coding: utf-8 -*-
"""Classes for managing templates and their runtime and compile time
options.
"""
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)
!= 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
"""
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:
"""
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
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):
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()
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:
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.
"""
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()
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.
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.
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
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)
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
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
-# -*- coding: utf-8 -*-
"""Extension API for adding custom tags and behavior."""
import pprint
import re
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
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):
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,
)
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,
)
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)
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)
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)
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
-# -*- coding: utf-8 -*-
"""Built-in template filters used with the ``|`` operator."""
import math
import random
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
)
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
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:
@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:
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):
Rename the ``indentfirst`` argument to ``first``.
"""
- indention = u" " * width
- newline = u"\n"
+ indention = " " * width
+ newline = "\n"
if isinstance(s, Markup):
indention = Markup(indention)
"""
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:
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:
return sym
-class Symbols(object):
+class Symbols:
def __init__(self, parent=None, level=None):
if level is None:
if parent is None:
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
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
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}"
)
-# -*- 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
";": 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(
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.
"""
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.
"""
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`.
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,
# 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.
# 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.
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",
),
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",
),
# 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",
)
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: [
"""
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
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:
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,
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:
# 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
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
)
-# -*- coding: utf-8 -*-
"""API and implementations for loading templates from different data
sources.
"""
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
"""
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)
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):
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.
@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:
-# -*- coding: utf-8 -*-
"""Functions that expose information about templates that might be
interesting for introspection.
"""
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)
# 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
-# -*- 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.
"-": 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,
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.
"""
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
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
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
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):
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
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."""
-# -*- coding: utf-8 -*-
"""The optimizer tries to constant fold expressions and modify the AST
in place so that it should be faster to evaluate.
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.
-# -*- coding: utf-8 -*-
"""Parse tokens from the lexer into nodes for the compiler."""
from . import nodes
from .exceptions import TemplateAssertionError
}
-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.
"""
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
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)
"""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):
# 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)
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
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(
# 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)
-# -*- coding: utf-8 -*-
"""The runtime functions and state used by compiled templates."""
import sys
from collections import abc
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)
return environment.context_class(environment, parent, template_name, blocks)
-class TemplateReference(object):
+class TemplateReference:
"""The `self` in templates."""
def __init__(self, context):
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):
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)
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
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):
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):
"""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)
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__(
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)
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`:
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
return id(type(self))
def __str__(self):
- return u""
+ return ""
def __len__(self):
return 0
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
__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):
-# -*- coding: utf-8 -*-
"""A sandbox layer that ensures unsafe operations cannot be performed.
Useful when the template itself comes from an untrusted source.
"""
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
def unsafe(f):
"""Marks a function or method as unsafe.
- ::
+ .. code-block: python
@unsafe
def delete(self):
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,
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]
# 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)
return not modifies_known_mutable(obj, attr)
-class SandboxedFormatterMixin(object):
+class SandboxedFormatterMixin:
def __init__(self, env):
self._env = env
-# -*- coding: utf-8 -*-
"""Built-in template tests used with the ``is`` operator."""
import operator
import re
-# -*- coding: utf-8 -*-
import json
import os
import re
from markupsafe import Markup
_word_split_re = re.compile(r"(\s+)")
+_lead_pattern = "|".join(map(re.escape, ("(", "<", "<")))
+_trail_pattern = "|".join(map(re.escape, (".", ",", ")", ">", "\n", ">")))
_punctuation_re = re.compile(
- "^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$"
- % (
- "|".join(map(re.escape, ("(", "<", "<"))),
- "|".join(map(re.escape, (".", ",", ")", ">", "\n", ">"))),
- )
+ 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"(<!--.*?-->|<[^>]*>)")
# internal code
internal_code = set()
-concat = u"".join
+concat = "".join
_slash_escape = "\\/" not in json.dumps("/")
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):
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)
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
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):
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("."):
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):
@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
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
.. 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:
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.
__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."""
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?
-# -*- 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.
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."""
-# -*- coding: utf-8 -*-
import os
import pytest
-# -*- coding: utf-8 -*-
import os
import shutil
import tempfile
@pytest.mark.api
@pytest.mark.extended
-class TestExtendedAPI(object):
+class TestExtendedAPI:
def test_item_and_attribute(self, env):
from jinja2.sandbox import SandboxedEnvironment
@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 }}"
"{% 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 %}')
@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>"
)
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("")
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:
@pytest.mark.api
@pytest.mark.undefined
-class TestUndefined(object):
+class TestUndefined:
def test_stopiteration_is_undefined(self):
def test():
raise StopIteration()
def test_logging_undefined(self):
_messages = []
- class DebugLogger(object):
+ class DebugLogger:
def warning(self, msg, *args):
_messages.append("W:" + msg % args)
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"
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) == ""
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):
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
@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):
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
@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]"
@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]"
@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"
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 %}"
-# -*- coding: utf-8 -*-
import pytest
from jinja2 import Environment
@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
raise self.Error()
-class TestMemcachedBytecodeCache(object):
+class TestMemcachedBytecodeCache:
def test_dump_load(self):
memcached = MockMemcached()
m = MemcachedBytecodeCache(memcached)
-# -*- coding: utf-8 -*-
import pytest
from jinja2 import DictLoader
@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"
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):
@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() == "..."
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"
@pytest.mark.core_tags
@pytest.mark.macros
-class TestMacros(object):
+class TestMacros:
def test_simple(self, env_trim):
tmpl = env_trim.from_string(
"""\
@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"
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)
"{% 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):
"{{ 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(
"""\
-# -*- coding: utf-8 -*-
import pickle
import re
from traceback import format_exception
@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():
-# -*- coding: utf-8 -*-
import re
from io import BytesIO
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",
}
}
class ExampleExtension(Extension):
- tags = set(["test"])
+ tags = {"test"}
ext_attr = 42
context_reference_node_cls = nodes.ContextReference
).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')}"
)
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
@pytest.mark.ext
-class TestExtensions(object):
+class TestExtensions:
def test_extend_late(self):
env = Environment()
env.add_extension("jinja2.ext.autoescape")
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"
"""
)
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):
"""
)
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):
)
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):
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)
@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"
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,
)
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(
# 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
)
@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(
"""
)
assert tmpl.render().split() == [
- u"<HelloWorld>",
- u"<HelloWorld>",
- u"<HelloWorld>",
+ "<HelloWorld>",
+ "<HelloWorld>",
+ "<HelloWorld>",
]
env = Environment(extensions=["jinja2.ext.autoescape"], autoescape=False)
"""
)
assert tmpl.render().split() == [
- u"<HelloWorld>",
- u"<HelloWorld>",
- u"<HelloWorld>",
+ "<HelloWorld>",
+ "<HelloWorld>",
+ "<HelloWorld>",
]
def test_nonvolatile(self):
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)
from jinja2 import Template
+# Python < 3.7
def test_generator_stop():
- class X(object):
+ class X:
def __getattr__(self, name):
raise StopIteration()
-# -*- coding: utf-8 -*-
import random
from collections import namedtuple
from jinja2 import Markup
-class Magic(object):
+class Magic:
def __init__(self, value):
self.value = value
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
),
)
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
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"
),
)
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
"{{ 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 }}')
def test_forceescape(self, env):
tmpl = env.from_string("{{ x|forceescape }}")
- assert tmpl.render(x=Markup("<div />")) == u"<div />"
+ assert tmpl.render(x=Markup("<div />")) == "<div />"
def test_safe(self, env):
env = Environment(autoescape=True)
("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&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"),
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(
[
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(
),
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"),
[
[
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,
[
nodes.Output(
[
- nodes.TemplateData(u"\n "),
+ nodes.TemplateData("\n "),
nodes.Call(
nodes.Name("render_title", "load"),
[nodes.Name("item", "load")],
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,
)
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",
}
"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",
"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 == {
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",
}
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"),
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"),
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),
-# -*- 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
@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]"
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]"
-# -*- coding: utf-8 -*-
import pytest
from jinja2 import DictLoader
@pytest.mark.inheritance
-class TestInheritance(object):
+class TestInheritance:
def test_layout(self, env):
tmpl = env.get_template("layout")
assert tmpl.render() == (
)
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(
@pytest.mark.inheritance
-class TestBugFix(object):
+class TestBugFix:
def test_fixed_macro_scoping_bug(self, env):
assert (
Environment(
.get_template("test.html")
.render()
.split()
- == [u"outer_box", u"my_macro"]
+ == ["outer_box", "my_macro"]
)
def test_double_extends(self, env):
-# -*- coding: utf-8 -*-
import pytest
from jinja2 import Environment
@pytest.mark.lexnparse
@pytest.mark.tokenstream
-class TestTokenStream(object):
+class TestTokenStream:
test_tokens = [
Token(1, TOKEN_BLOCK_BEGIN, ""),
Token(2, TOKEN_BLOCK_END, ""),
@pytest.mark.lexnparse
@pytest.mark.lexer
-class TestLexer(object):
+class TestLexer:
def test_raw1(self, env):
tmpl = env.from_string(
"{% raw %}foo{% endraw %}|"
)
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
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
@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
@pytest.mark.lexnparse
@pytest.mark.parser
-class TestParser(object):
+class TestParser:
def test_php_syntax(self, env):
env = Environment("<?", "?>", "<?=", "?>", "<!--", "-->")
tmpl = env.from_string(
@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
],
)
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):
@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(
),
)
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):
]
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 [
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(
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 %}")
@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 %}""")
${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(
${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(
<?= 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(
<?=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 %>
<% 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(
-# -*- coding: utf-8 -*-
import os
import platform
import shutil
@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")
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")
@pytest.mark.loaders
@pytest.mark.filesystemloader
-class TestFileSystemLoader(object):
+class TestFileSystemLoader:
searchpath = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "res", "templates"
)
@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):
@pytest.mark.loaders
@pytest.mark.moduleloader
-class TestModuleLoader(object):
+class TestModuleLoader:
archive = None
def compile_down(self, prefix_loader, zip="deflated"):
@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):
-# -*- coding: utf-8 -*-
import pytest
from jinja2 import DictLoader
@pytest.mark.regression
-class TestCorner(object):
+class TestCorner:
def test_assigned_scoping(self, env):
t = env.from_string(
"""
@pytest.mark.regression
-class TestBug(object):
+class TestBug:
def test_keyword_folding(self, env):
env = Environment()
env.filters["testing"] = lambda value, some: value + some
"""
)
rv = t.render(foo=[1]).strip()
- assert rv == u"1"
+ assert rv == "1"
def test_call_with_args(self, env):
t = Template(
]
).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):
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")
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
as a ``contextfunction``.
"""
- class Calc(object):
+ class Calc:
def __getattr__(self, item):
return object()
-# -*- coding: utf-8 -*-
import pytest
from jinja2 import Environment
from jinja2.sandbox import unsafe
-class PrivateStuff(object):
+class PrivateStuff:
def bar(self):
return 23
return "PrivateStuff"
-class PublicStuff(object):
+class PublicStuff:
def bar(self):
return 23
@pytest.mark.sandbox
-class TestSandbox(object):
+class TestSandbox:
def test_unsafe(self, env):
env = SandboxedEnvironment()
pytest.raises(
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)
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) }}')
@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}) }}')
-# -*- coding: utf-8 -*-
import pytest
from jinja2 import Environment
@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"
),
)
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):
),
)
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):
-# -*- coding: utf-8 -*-
import pickle
import random
from collections import deque
@pytest.mark.utils
@pytest.mark.lrucache
-class TestLRUCache(object):
+class TestLRUCache:
def test_simple(self):
d = LRUCache(3)
d["a"] = 1
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."""
@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"
@pytest.mark.utils
@pytest.mark.escapeUrlizeTarget
-class TestEscapeUrlizeTarget(object):
+class TestEscapeUrlizeTarget:
def test_escape_urlize_target(self):
url = "http://example.org"
target = "<script>"
@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)
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
def test_missing():
"""Test the repr of missing."""
- assert repr(missing) == u"missing"
+ assert repr(missing) == "missing"
def test_consume():