fix type/lint/format findings

This commit is contained in:
Adam Englander 2025-05-28 10:06:54 -07:00 committed by David Lord
parent 51dbd8977e
commit a3dce7bb64
No known key found for this signature in database
GPG Key ID: 43368A7AA8CC5926
23 changed files with 262 additions and 283 deletions

View File

@ -95,5 +95,5 @@ def auto_aiter(
async def auto_to_list(
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
) -> t.List["V"]:
) -> list["V"]:
return [x async for x in auto_aiter(value)]

View File

@ -139,9 +139,7 @@ def has_safe_repr(value: t.Any) -> bool:
return False
def find_undeclared(
nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
) -> t.Set[str]:
def find_undeclared(nodes: t.Iterable[nodes.Node], names: t.Iterable[str]) -> set[str]:
"""Check if the names passed are accessed undeclared. The return value
is a set of all the undeclared names from the sequence of names found.
"""
@ -253,8 +251,8 @@ class DependencyFinderVisitor(NodeVisitor):
"""A visitor that collects filter and test calls."""
def __init__(self) -> None:
self.filters: t.Set[str] = set()
self.tests: t.Set[str] = set()
self.filters: set[str] = set()
self.tests: set[str] = set()
def visit_Filter(self, node: nodes.Filter) -> None:
self.generic_visit(node)
@ -276,7 +274,7 @@ class UndeclaredNameVisitor(NodeVisitor):
def __init__(self, names: t.Iterable[str]) -> None:
self.names = set(names)
self.undeclared: t.Set[str] = set()
self.undeclared: set[str] = set()
def visit_Name(self, node: nodes.Name) -> None:
if node.ctx == "load" and node.name in self.names:
@ -321,11 +319,11 @@ class CodeGenerator(NodeVisitor):
self.optimizer = Optimizer(environment)
# aliases for imports
self.import_aliases: t.Dict[str, str] = {}
self.import_aliases: dict[str, str] = {}
# a registry for all blocks. Because blocks are moved out
# into the global python scope they are registered here
self.blocks: t.Dict[str, nodes.Block] = {}
self.blocks: dict[str, nodes.Block] = {}
# the number of extends statements so far
self.extends_so_far = 0
@ -339,11 +337,11 @@ class CodeGenerator(NodeVisitor):
self.code_lineno = 1
# registry of all filters and tests (global, not block local)
self.tests: t.Dict[str, str] = {}
self.filters: t.Dict[str, str] = {}
self.tests: dict[str, str] = {}
self.filters: dict[str, str] = {}
# the debug information
self.debug_info: t.List[t.Tuple[int, int]] = []
self.debug_info: list[tuple[int, int]] = []
self._write_debug_info: t.Optional[int] = None
# the number of new lines before the next write()
@ -363,10 +361,10 @@ class CodeGenerator(NodeVisitor):
self._indentation = 0
# Tracks toplevel assignments
self._assign_stack: t.List[t.Set[str]] = []
self._assign_stack: list[set[str]] = []
# Tracks parameter definition blocks
self._param_def_block: t.List[t.Set[str]] = []
self._param_def_block: list[set[str]] = []
# Tracks the current context.
self._context_reference_stack = ["context"]
@ -613,7 +611,7 @@ class CodeGenerator(NodeVisitor):
def macro_body(
self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
) -> t.Tuple[Frame, MacroRef]:
) -> tuple[Frame, MacroRef]:
"""Dump the function def of a macro or call block."""
frame = frame.inner()
frame.symbols.analyze_node(node)
@ -1511,7 +1509,7 @@ class CodeGenerator(NodeVisitor):
self.indent()
finalize = self._make_finalize()
body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
body: list[t.Union[list[t.Any], nodes.Expr]] = []
# Evaluate constants at compile time if possible. Each item in
# body will be either a list of static data or a node to be
@ -1586,7 +1584,7 @@ class CodeGenerator(NodeVisitor):
# it is only valid if it references a Namespace object. Emit a check for
# that for each ref here, before assignment code is emitted. This can't
# be done in visit_NSRef as the ref could be in the middle of a tuple.
seen_refs: t.Set[str] = set()
seen_refs: set[str] = set()
for nsref in node.find_all(nodes.NSRef):
if nsref.name in seen_refs:

View File

@ -128,7 +128,7 @@ def fake_traceback( # type: ignore
return sys.exc_info()[2].tb_next # type: ignore
def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]:
def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> dict[str, t.Any]:
"""Based on the runtime locals, get the context that would be
available at that point in the template.
"""
@ -136,7 +136,7 @@ def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any
ctx: t.Optional[Context] = real_locals.get("context")
if ctx is not None:
data: t.Dict[str, t.Any] = ctx.get_all().copy()
data: dict[str, t.Any] = ctx.get_all().copy()
else:
data = {}
@ -144,7 +144,7 @@ def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any
# rather than pushing a context. Local variables follow the scheme
# l_depth_name. Find the highest-depth local that has a value for
# each name.
local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {}
local_overrides: dict[str, tuple[int, t.Any]] = {}
for name, value in real_locals.items():
if not name.startswith("l_") or value is missing:

View File

@ -36,7 +36,7 @@ DEFAULT_NAMESPACE = {
}
# default policies
DEFAULT_POLICIES: t.Dict[str, t.Any] = {
DEFAULT_POLICIES: dict[str, t.Any] = {
"compiler.ascii_str": True,
"urlize.rel": "noopener",
"urlize.target": None,

View File

@ -66,7 +66,7 @@ _env_bound = t.TypeVar("_env_bound", bound="Environment")
# for direct template usage we have up to ten living environments
@lru_cache(maxsize=10)
def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound:
def get_spontaneous_environment(cls: type[_env_bound], *args: t.Any) -> _env_bound:
"""Return a new spontaneous environment. A spontaneous environment
is used for templates created directly rather than through an
existing environment.
@ -81,7 +81,7 @@ def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_b
def create_cache(
size: int,
) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]]:
) -> t.Optional[t.MutableMapping[tuple["weakref.ref[BaseLoader]", str], "Template"]]:
"""Return the cache class for the given size."""
if size == 0:
return None
@ -94,9 +94,9 @@ def create_cache(
def copy_cache(
cache: t.Optional[
t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]
t.MutableMapping[tuple["weakref.ref[BaseLoader]", str], "Template"]
],
) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]]:
) -> t.Optional[t.MutableMapping[tuple["weakref.ref[BaseLoader]", str], "Template"]]:
"""Create an empty copy of the given cache."""
if cache is None:
return None
@ -109,8 +109,8 @@ def copy_cache(
def load_extensions(
environment: "Environment",
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]],
) -> t.Dict[str, "Extension"]:
extensions: t.Sequence[t.Union[str, type["Extension"]]],
) -> dict[str, "Extension"]:
"""Load the extensions from the list and bind it to the environment.
Returns a dict of instantiated extensions.
"""
@ -118,7 +118,7 @@ def load_extensions(
for extension in extensions:
if isinstance(extension, str):
extension = t.cast(t.Type["Extension"], import_string(extension))
extension = t.cast(type["Extension"], import_string(extension))
result[extension.identifier] = extension(environment)
@ -127,9 +127,9 @@ def load_extensions(
def _environment_config_check(environment: _env_bound) -> _env_bound:
"""Perform a sanity check on the environment."""
assert issubclass(
environment.undefined, Undefined
), "'undefined' must be a subclass of 'jinja2.Undefined'."
assert issubclass(environment.undefined, Undefined), (
"'undefined' must be a subclass of 'jinja2.Undefined'."
)
assert (
environment.block_start_string
!= environment.variable_start_string
@ -283,15 +283,15 @@ class Environment:
#: the class that is used for code generation. See
#: :class:`~jinja2.compiler.CodeGenerator` for more information.
code_generator_class: t.Type["CodeGenerator"] = CodeGenerator
code_generator_class: type["CodeGenerator"] = CodeGenerator
concat = "".join
#: the context class that is used for templates. See
#: :class:`~jinja2.runtime.Context` for more information.
context_class: t.Type[Context] = Context
context_class: type[Context] = Context
template_class: t.Type["Template"]
template_class: type["Template"]
def __init__(
self,
@ -307,9 +307,9 @@ class Environment:
lstrip_blocks: bool = LSTRIP_BLOCKS,
newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
extensions: t.Sequence[t.Union[str, type["Extension"]]] = (),
optimized: bool = True,
undefined: t.Type[Undefined] = Undefined,
undefined: type[Undefined] = Undefined,
finalize: t.Optional[t.Callable[..., t.Any]] = None,
autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
loader: t.Optional["BaseLoader"] = None,
@ -344,7 +344,7 @@ class Environment:
self.keep_trailing_newline = keep_trailing_newline
# runtime information
self.undefined: t.Type[Undefined] = undefined
self.undefined: type[Undefined] = undefined
self.optimized = optimized
self.finalize = finalize
self.autoescape = autoescape
@ -369,7 +369,7 @@ class Environment:
self.is_async = enable_async
_environment_config_check(self)
def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None:
def add_extension(self, extension: t.Union[str, type["Extension"]]) -> None:
"""Adds an extension after the environment was created.
.. versionadded:: 2.5
@ -399,9 +399,9 @@ class Environment:
lstrip_blocks: bool = missing,
newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing,
keep_trailing_newline: bool = missing,
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing,
extensions: t.Sequence[t.Union[str, type["Extension"]]] = missing,
optimized: bool = missing,
undefined: t.Type[Undefined] = missing,
undefined: type[Undefined] = missing,
finalize: t.Optional[t.Callable[..., t.Any]] = missing,
autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing,
loader: t.Optional["BaseLoader"] = missing,
@ -628,7 +628,7 @@ class Environment:
source: str,
name: t.Optional[str] = None,
filename: t.Optional[str] = None,
) -> t.Iterator[t.Tuple[int, str, str]]:
) -> t.Iterator[tuple[int, str, str]]:
"""Lex the given sourcecode and return a generator that yields
tokens as tuples in the form ``(lineno, token_type, value)``.
This can be useful for :ref:`extension development <writing-extensions>`
@ -677,7 +677,7 @@ class Environment:
stream = ext.filter_stream(stream) # type: ignore
if not isinstance(stream, TokenStream):
stream = TokenStream(stream, name, filename) # type: ignore[unreachable]
stream = TokenStream(stream, name, filename)
return stream
@ -902,7 +902,7 @@ class Environment:
self,
extensions: t.Optional[t.Collection[str]] = None,
filter_func: t.Optional[t.Callable[[str], bool]] = None,
) -> t.List[str]:
) -> list[str]:
"""Returns a list of templates for this environment. This requires
that the loader supports the loader's
:meth:`~BaseLoader.list_templates` method.
@ -1074,9 +1074,7 @@ class Environment:
@internalcode
def get_or_select_template(
self,
template_name_or_list: t.Union[
str, "Template", t.List[t.Union[str, "Template"]]
],
template_name_or_list: t.Union[str, "Template", list[t.Union[str, "Template"]]],
parent: t.Optional[str] = None,
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
) -> "Template":
@ -1095,7 +1093,7 @@ class Environment:
self,
source: t.Union[str, nodes.Template],
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
template_class: t.Optional[t.Type["Template"]] = None,
template_class: t.Optional[type["Template"]] = None,
) -> "Template":
"""Load a template from a source string without using
:attr:`loader`.
@ -1154,13 +1152,13 @@ class Template:
#: Type of environment to create when creating a template directly
#: rather than through an existing environment.
environment_class: t.Type[Environment] = Environment
environment_class: type[Environment] = Environment
environment: Environment
globals: t.MutableMapping[str, t.Any]
name: t.Optional[str]
filename: t.Optional[str]
blocks: t.Dict[str, t.Callable[[Context], t.Iterator[str]]]
blocks: dict[str, t.Callable[[Context], t.Iterator[str]]]
root_render_func: t.Callable[[Context], t.Iterator[str]]
_module: t.Optional["TemplateModule"]
_debug_info: str
@ -1181,9 +1179,9 @@ class Template:
lstrip_blocks: bool = LSTRIP_BLOCKS,
newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
extensions: t.Sequence[t.Union[str, type["Extension"]]] = (),
optimized: bool = True,
undefined: t.Type[Undefined] = Undefined,
undefined: type[Undefined] = Undefined,
finalize: t.Optional[t.Callable[..., t.Any]] = None,
autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
enable_async: bool = False,
@ -1336,7 +1334,7 @@ class Template:
if self.environment.is_async:
import asyncio
async def to_list() -> t.List[str]:
async def to_list() -> list[str]:
return [x async for x in self.generate_async(*args, **kwargs)]
yield from asyncio.run(to_list())
@ -1376,7 +1374,7 @@ class Template:
def new_context(
self,
vars: t.Optional[t.Dict[str, t.Any]] = None,
vars: t.Optional[dict[str, t.Any]] = None,
shared: bool = False,
locals: t.Optional[t.Mapping[str, t.Any]] = None,
) -> Context:
@ -1393,7 +1391,7 @@ class Template:
def make_module(
self,
vars: t.Optional[t.Dict[str, t.Any]] = None,
vars: t.Optional[dict[str, t.Any]] = None,
shared: bool = False,
locals: t.Optional[t.Mapping[str, t.Any]] = None,
) -> "TemplateModule":
@ -1408,7 +1406,7 @@ class Template:
async def make_module_async(
self,
vars: t.Optional[t.Dict[str, t.Any]] = None,
vars: t.Optional[dict[str, t.Any]] = None,
shared: bool = False,
locals: t.Optional[t.Mapping[str, t.Any]] = None,
) -> "TemplateModule":
@ -1498,7 +1496,7 @@ class Template:
return self._uptodate()
@property
def debug_info(self) -> t.List[t.Tuple[int, int]]:
def debug_info(self) -> list[tuple[int, int]]:
"""The debug info mapping."""
if self._debug_info:
return [
@ -1636,7 +1634,7 @@ class TemplateStream:
self.buffered = False
def _buffered_generator(self, size: int) -> t.Iterator[str]:
buf: t.List[str] = []
buf: list[str] = []
c_size = 0
push = buf.append

View File

@ -42,7 +42,7 @@ if t.TYPE_CHECKING:
# I18N functions available in Jinja templates. If the I18N library
# provides ugettext, it will be assigned to gettext.
GETTEXT_FUNCTIONS: t.Tuple[str, ...] = (
GETTEXT_FUNCTIONS: tuple[str, ...] = (
"_",
"gettext",
"ngettext",
@ -77,7 +77,7 @@ class Extension:
cls.identifier = f"{cls.__module__}.{cls.__name__}"
#: if this extension parses this is the list of tags it's listening to.
tags: t.Set[str] = set()
tags: set[str] = set()
#: the priority of that extension. This is especially useful for
#: extensions that preprocess values. A lower value means higher
@ -115,7 +115,7 @@ class Extension:
"""
return stream
def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
def parse(self, parser: "Parser") -> t.Union[nodes.Node, list[nodes.Node]]:
"""If any of the :attr:`tags` matched this method is called with the
parser as first argument. The token the parser stream is pointing at
is the name token that matched. This method has to return one or a
@ -138,8 +138,8 @@ class Extension:
def call_method(
self,
name: str,
args: t.Optional[t.List[nodes.Expr]] = None,
kwargs: t.Optional[t.List[nodes.Keyword]] = None,
args: t.Optional[list[nodes.Expr]] = None,
kwargs: t.Optional[list[nodes.Keyword]] = None,
dyn_args: t.Optional[nodes.Expr] = None,
dyn_kwargs: t.Optional[nodes.Expr] = None,
lineno: t.Optional[int] = None,
@ -330,13 +330,13 @@ class InternationalizationExtension(Extension):
source: t.Union[str, nodes.Template],
gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
) -> t.Iterator[
t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
tuple[int, str, t.Union[t.Optional[str], tuple[t.Optional[str], ...]]]
]:
if isinstance(source, str):
source = self.environment.parse(source)
return extract_from_ast(source, gettext_functions)
def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
def parse(self, parser: "Parser") -> t.Union[nodes.Node, list[nodes.Node]]:
"""Parse a translatable tag."""
lineno = next(parser.stream).lineno
@ -352,7 +352,7 @@ class InternationalizationExtension(Extension):
plural_expr: t.Optional[nodes.Expr] = None
plural_expr_assignment: t.Optional[nodes.Assign] = None
num_called_num = False
variables: t.Dict[str, nodes.Expr] = {}
variables: dict[str, nodes.Expr] = {}
trimmed = None
while parser.stream.current.type != "block_end":
if variables:
@ -463,7 +463,7 @@ class InternationalizationExtension(Extension):
def _parse_block(
self, parser: "Parser", allow_pluralize: bool
) -> t.Tuple[t.List[str], str]:
) -> tuple[list[str], str]:
"""Parse until the next block tag with a given name."""
referenced = []
buf = []
@ -513,7 +513,7 @@ class InternationalizationExtension(Extension):
singular: str,
plural: t.Optional[str],
context: t.Optional[str],
variables: t.Dict[str, nodes.Expr],
variables: dict[str, nodes.Expr],
plural_expr: t.Optional[nodes.Expr],
vars_referenced: bool,
num_called_num: bool,
@ -530,7 +530,7 @@ class InternationalizationExtension(Extension):
plural = plural.replace("%%", "%")
func_name = "gettext"
func_args: t.List[nodes.Expr] = [nodes.Const(singular)]
func_args: list[nodes.Expr] = [nodes.Const(singular)]
if context is not None:
func_args.insert(0, nodes.Const(context))
@ -640,9 +640,7 @@ def extract_from_ast(
ast: nodes.Template,
gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
babel_style: bool = True,
) -> t.Iterator[
t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
]:
) -> t.Iterator[tuple[int, str, t.Union[t.Optional[str], tuple[t.Optional[str], ...]]]]:
"""Extract localizable strings from the given template node. Per
default this function returns matches in babel style that means non string
parameters as well as keyword arguments are returned as `None`. This
@ -677,7 +675,7 @@ def extract_from_ast(
to extract any comments. For comment support you have to use the babel
extraction interface or extract comments yourself.
"""
out: t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]
out: t.Union[t.Optional[str], tuple[t.Optional[str], ...]]
for node in ast.find_all(nodes.Call):
if (
@ -686,7 +684,7 @@ def extract_from_ast(
):
continue
strings: t.List[t.Optional[str]] = []
strings: list[t.Optional[str]] = []
for arg in node.args:
if isinstance(arg, nodes.Const) and isinstance(arg.value, str):
@ -723,14 +721,14 @@ class _CommentFinder:
"""
def __init__(
self, tokens: t.Sequence[t.Tuple[int, str, str]], comment_tags: t.Sequence[str]
self, tokens: t.Sequence[tuple[int, str, str]], comment_tags: t.Sequence[str]
) -> None:
self.tokens = tokens
self.comment_tags = comment_tags
self.offset = 0
self.last_lineno = 0
def find_backwards(self, offset: int) -> t.List[str]:
def find_backwards(self, offset: int) -> list[str]:
try:
for _, token_type, token_value in reversed(
self.tokens[self.offset : offset]
@ -746,7 +744,7 @@ class _CommentFinder:
finally:
self.offset = offset
def find_comments(self, lineno: int) -> t.List[str]:
def find_comments(self, lineno: int) -> list[str]:
if not self.comment_tags or self.last_lineno > lineno:
return []
for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
@ -759,11 +757,9 @@ def babel_extract(
fileobj: t.BinaryIO,
keywords: t.Sequence[str],
comment_tags: t.Sequence[str],
options: t.Dict[str, t.Any],
options: dict[str, t.Any],
) -> t.Iterator[
t.Tuple[
int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]], t.List[str]
]
tuple[int, str, t.Union[t.Optional[str], tuple[t.Optional[str], ...]], list[str]]
]:
"""Babel extraction method for Jinja templates.
@ -792,7 +788,7 @@ def babel_extract(
:return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
(comments will be empty currently)
"""
extensions: t.Dict[t.Type[Extension], None] = {}
extensions: dict[type[Extension], None] = {}
for extension_name in options.get("extensions", "").split(","):
extension_name = extension_name.strip()

View File

@ -87,7 +87,7 @@ def make_multi_attrgetter(
environment: "Environment",
attribute: t.Optional[t.Union[str, int]],
postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
) -> t.Callable[[t.Any], t.List[t.Any]]:
) -> t.Callable[[t.Any], list[t.Any]]:
"""Returns a callable that looks up the given comma separated
attributes from a passed object with the rules of the environment.
Dots are allowed to access attributes of each attribute. Integer
@ -105,7 +105,7 @@ def make_multi_attrgetter(
parts = [_prepare_attribute_parts(item) for item in split]
def attrgetter(item: t.Any) -> t.List[t.Any]:
def attrgetter(item: t.Any) -> list[t.Any]:
items = [None] * len(parts)
for i, attribute_part in enumerate(parts):
@ -126,7 +126,7 @@ def make_multi_attrgetter(
def _prepare_attribute_parts(
attr: t.Optional[t.Union[str, int]],
) -> t.List[t.Union[str, int]]:
) -> list[t.Union[str, int]]:
if attr is None:
return []
@ -145,7 +145,7 @@ def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup:
def do_urlencode(
value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]],
value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[tuple[str, t.Any]]],
) -> str:
"""Quote data for use in a URL path or query using UTF-8.
@ -166,7 +166,7 @@ def do_urlencode(
return url_quote(value)
if isinstance(value, dict):
items: t.Iterable[t.Tuple[str, t.Any]] = value.items()
items: t.Iterable[tuple[str, t.Any]] = value.items()
else:
items = value # type: ignore
@ -221,7 +221,7 @@ def do_lower(s: str) -> str:
return soft_str(s).lower()
def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]:
def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[tuple[K, V]]:
"""Return an iterator over the ``(key, value)`` items of a mapping.
``x|items`` is the same as ``x.items()``, except if ``x`` is
@ -346,7 +346,7 @@ def do_dictsort(
case_sensitive: bool = False,
by: 'te.Literal["key", "value"]' = "key",
reverse: bool = False,
) -> t.List[t.Tuple[K, V]]:
) -> list[tuple[K, V]]:
"""Sort a dict and yield (key, value) pairs. Python dicts may not
be in the order you want to display them in, so sort them first.
@ -371,7 +371,7 @@ def do_dictsort(
else:
raise FilterArgumentError('You can only sort by either "key" or "value"')
def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any:
def sort_func(item: tuple[t.Any, t.Any]) -> t.Any:
value = item[pos]
if not case_sensitive:
@ -389,7 +389,7 @@ def do_sort(
reverse: bool = False,
case_sensitive: bool = False,
attribute: t.Optional[t.Union[str, int]] = None,
) -> "t.List[V]":
) -> "list[V]":
"""Sort an iterable using Python's :func:`sorted`.
.. sourcecode:: jinja
@ -1058,7 +1058,7 @@ def do_striptags(value: "t.Union[str, HasHTML]") -> str:
def sync_do_slice(
value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None
) -> "t.Iterator[t.List[V]]":
) -> "t.Iterator[list[V]]":
"""Slice an iterator and return a list of lists containing
those items. Useful if you want to create a div containing
three ul tags that represent columns:
@ -1104,13 +1104,13 @@ async def do_slice(
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
slices: int,
fill_with: t.Optional[t.Any] = None,
) -> "t.Iterator[t.List[V]]":
) -> "t.Iterator[list[V]]":
return sync_do_slice(await auto_to_list(value), slices, fill_with)
def do_batch(
value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None
) -> "t.Iterator[t.List[V]]":
) -> "t.Iterator[list[V]]":
"""
A filter that batches items. It works pretty much like `slice`
just the other way round. It returns a list of lists with the
@ -1129,7 +1129,7 @@ def do_batch(
{%- endfor %}
</table>
"""
tmp: t.List[V] = []
tmp: list[V] = []
for item in value:
if len(tmp) == linecount:
@ -1187,7 +1187,7 @@ def do_round(
class _GroupTuple(t.NamedTuple):
grouper: t.Any
list: t.List[t.Any]
list: list[t.Any]
# Use the regular tuple repr to hide this subclass if users print
# out the value during debugging.
@ -1205,7 +1205,7 @@ def sync_do_groupby(
attribute: t.Union[str, int],
default: t.Optional[t.Any] = None,
case_sensitive: bool = False,
) -> "t.List[_GroupTuple]":
) -> "list[_GroupTuple]":
"""Group a sequence of objects by an attribute using Python's
:func:`itertools.groupby`. The attribute can use dot notation for
nested access, like ``"address.city"``. Unlike Python's ``groupby``,
@ -1289,7 +1289,7 @@ async def do_groupby(
attribute: t.Union[str, int],
default: t.Optional[t.Any] = None,
case_sensitive: bool = False,
) -> "t.List[_GroupTuple]":
) -> "list[_GroupTuple]":
expr = make_attrgetter(
environment,
attribute,
@ -1358,7 +1358,7 @@ async def do_sum(
return rv
def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]":
def sync_do_list(value: "t.Iterable[V]") -> "list[V]":
"""Convert the value into a list. If it was a string the returned list
will be a list of characters.
"""
@ -1366,7 +1366,7 @@ def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]":
@async_variant(sync_do_list) # type: ignore
async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]":
async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "list[V]":
return await auto_to_list(value)
@ -1722,7 +1722,7 @@ def do_tojson(
def prepare_map(
context: "Context", args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any]
context: "Context", args: tuple[t.Any, ...], kwargs: dict[str, t.Any]
) -> t.Callable[[t.Any], t.Any]:
if not args and "attribute" in kwargs:
attribute = kwargs.pop("attribute")
@ -1751,8 +1751,8 @@ def prepare_map(
def prepare_select_or_reject(
context: "Context",
args: t.Tuple[t.Any, ...],
kwargs: t.Dict[str, t.Any],
args: tuple[t.Any, ...],
kwargs: dict[str, t.Any],
modfunc: t.Callable[[t.Any], t.Any],
lookup_attr: bool,
) -> t.Callable[[t.Any], t.Any]:
@ -1786,8 +1786,8 @@ def prepare_select_or_reject(
def select_or_reject(
context: "Context",
value: "t.Iterable[V]",
args: t.Tuple[t.Any, ...],
kwargs: t.Dict[str, t.Any],
args: tuple[t.Any, ...],
kwargs: dict[str, t.Any],
modfunc: t.Callable[[t.Any], t.Any],
lookup_attr: bool,
) -> "t.Iterator[V]":
@ -1802,8 +1802,8 @@ def select_or_reject(
async def async_select_or_reject(
context: "Context",
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
args: t.Tuple[t.Any, ...],
kwargs: t.Dict[str, t.Any],
args: tuple[t.Any, ...],
kwargs: dict[str, t.Any],
modfunc: t.Callable[[t.Any], t.Any],
lookup_attr: bool,
) -> "t.AsyncIterator[V]":

View File

@ -42,16 +42,16 @@ class Symbols:
self.level: int = level
self.parent = parent
self.refs: t.Dict[str, str] = {}
self.loads: t.Dict[str, t.Any] = {}
self.stores: t.Set[str] = set()
self.refs: dict[str, str] = {}
self.loads: dict[str, t.Any] = {}
self.stores: set[str] = set()
def analyze_node(self, node: nodes.Node, **kwargs: t.Any) -> None:
visitor = RootVisitor(self)
visitor.visit(node, **kwargs)
def _define_ref(
self, name: str, load: t.Optional[t.Tuple[str, t.Optional[str]]] = None
self, name: str, load: t.Optional[tuple[str, t.Optional[str]]] = None
) -> str:
ident = f"l_{self.level}_{name}"
self.refs[name] = ident
@ -121,7 +121,7 @@ class Symbols:
self._define_ref(name, load=(VAR_LOAD_RESOLVE, name))
def branch_update(self, branch_symbols: t.Sequence["Symbols"]) -> None:
stores: t.Set[str] = set()
stores: set[str] = set()
for branch in branch_symbols:
stores.update(branch.stores)
@ -144,8 +144,8 @@ class Symbols:
continue
self.loads[target] = (VAR_LOAD_RESOLVE, name)
def dump_stores(self) -> t.Dict[str, str]:
rv: t.Dict[str, str] = {}
def dump_stores(self) -> dict[str, str]:
rv: dict[str, str] = {}
node: t.Optional[Symbols] = self
while node is not None:
@ -157,7 +157,7 @@ class Symbols:
return rv
def dump_param_targets(self) -> t.Set[str]:
def dump_param_targets(self) -> set[str]:
rv = set()
node: t.Optional[Symbols] = self

View File

@ -21,7 +21,7 @@ if t.TYPE_CHECKING:
# cache for the lexers. Exists in order to be able to have multiple
# environments with the same lexer
_lexer_cache: t.MutableMapping[t.Tuple, "Lexer"] = LRUCache(50) # type: ignore
_lexer_cache: t.MutableMapping[tuple, "Lexer"] = LRUCache(50) # type: ignore
# static regular expressions
whitespace_re = re.compile(r"\s+")
@ -210,7 +210,7 @@ def count_newlines(value: str) -> int:
return len(newline_re.findall(value))
def compile_rules(environment: "Environment") -> t.List[t.Tuple[str, str]]:
def compile_rules(environment: "Environment") -> list[tuple[str, str]]:
"""Compiles all the rules from the environment into a list of rules."""
e = re.escape
rules = [
@ -257,7 +257,7 @@ class Failure:
"""
def __init__(
self, message: str, cls: t.Type[TemplateSyntaxError] = TemplateSyntaxError
self, message: str, cls: type[TemplateSyntaxError] = TemplateSyntaxError
) -> None:
self.message = message
self.error_class = cls
@ -329,7 +329,7 @@ class TokenStream:
filename: t.Optional[str],
):
self._iter = iter(generator)
self._pushed: te.Deque[Token] = deque()
self._pushed: deque[Token] = deque()
self.name = name
self.filename = filename
self.closed = False
@ -464,7 +464,7 @@ class OptionalLStrip(tuple): # type: ignore[type-arg]
class _Rule(t.NamedTuple):
pattern: t.Pattern[str]
tokens: t.Union[str, t.Tuple[str, ...], t.Tuple[Failure]]
tokens: t.Union[str, tuple[str, ...], tuple[Failure]]
command: t.Optional[str]
@ -484,7 +484,7 @@ class Lexer:
return re.compile(x, re.M | re.S)
# lexing rules for tags
tag_rules: t.List[_Rule] = [
tag_rules: list[_Rule] = [
_Rule(whitespace_re, TOKEN_WHITESPACE, None),
_Rule(float_re, TOKEN_FLOAT, None),
_Rule(integer_re, TOKEN_INTEGER, None),
@ -523,7 +523,7 @@ class Lexer:
)
# global lexing rules
self.rules: t.Dict[str, t.List[_Rule]] = {
self.rules: dict[str, list[_Rule]] = {
"root": [
# directives
_Rule(
@ -614,7 +614,7 @@ class Lexer:
def wrap(
self,
stream: t.Iterable[t.Tuple[int, str, str]],
stream: t.Iterable[tuple[int, str, str]],
name: t.Optional[str] = None,
filename: t.Optional[str] = None,
) -> t.Iterator[Token]:
@ -672,7 +672,7 @@ class Lexer:
name: t.Optional[str],
filename: t.Optional[str] = None,
state: t.Optional[str] = None,
) -> t.Iterator[t.Tuple[int, str, str]]:
) -> t.Iterator[tuple[int, str, str]]:
"""This method tokenizes the text and returns the tokens in a
generator. Use this method if you just want to tokenize a template.
@ -696,7 +696,7 @@ class Lexer:
statetokens = self.rules[stack[-1]]
source_length = len(source)
balancing_stack: t.List[str] = []
balancing_stack: list[str] = []
newlines_stripped = 0
line_starting = True

View File

@ -22,7 +22,7 @@ if t.TYPE_CHECKING:
from .environment import Template
def split_template_path(template: str) -> t.List[str]:
def split_template_path(template: str) -> list[str]:
"""Split a path into segments and perform a sanity check. If it detects
'..' in the path it will raise a `TemplateNotFound` error.
"""
@ -74,7 +74,7 @@ class BaseLoader:
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
"""Get the template source, filename and reload helper for a template.
It's passed the environment and template name and has to return a
tuple in the form ``(source, filename, uptodate)`` or raise a
@ -98,7 +98,7 @@ class BaseLoader:
)
raise TemplateNotFound(template)
def list_templates(self) -> t.List[str]:
def list_templates(self) -> list[str]:
"""Iterates over all templates. If the loader does not support that
it should raise a :exc:`TypeError` which is the default behavior.
"""
@ -193,7 +193,7 @@ class FileSystemLoader(BaseLoader):
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, str, t.Callable[[], bool]]:
) -> tuple[str, str, t.Callable[[], bool]]:
pieces = split_template_path(template)
for searchpath in self.searchpath:
@ -225,7 +225,7 @@ class FileSystemLoader(BaseLoader):
# Use normpath to convert Windows altsep to sep.
return contents, os.path.normpath(filename), uptodate
def list_templates(self) -> t.List[str]:
def list_templates(self) -> list[str]:
found = set()
for searchpath in self.searchpath:
walk_dir = os.walk(searchpath, followlinks=self.followlinks)
@ -245,24 +245,23 @@ class FileSystemLoader(BaseLoader):
if sys.version_info >= (3, 13):
def _get_zipimporter_files(z: t.Any) -> t.Dict[str, object]:
def _get_zipimporter_files(z: t.Any) -> dict[str, object]:
try:
get_files = z._get_files
except AttributeError as e:
raise TypeError(
"This zip import does not have the required"
" metadata to list templates."
"This zip import does not have the required metadata to list templates."
) from e
return get_files()
else:
def _get_zipimporter_files(z: t.Any) -> t.Dict[str, object]:
def _get_zipimporter_files(z: t.Any) -> dict[str, object]:
try:
files = z._files
except AttributeError as e:
raise TypeError(
"This zip import does not have the required"
" metadata to list templates."
"This zip import does not have the required metadata to list templates."
) from e
return files # type: ignore[no-any-return]
@ -333,7 +332,7 @@ class PackageLoader(BaseLoader):
pkgdir = next(iter(spec.submodule_search_locations)) # type: ignore
template_root = os.path.join(pkgdir, package_path).rstrip(os.sep)
else:
roots: t.List[str] = []
roots: list[str] = []
# One element for regular packages, multiple for namespace
# packages, or None for single module file.
@ -365,7 +364,7 @@ class PackageLoader(BaseLoader):
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, str, t.Optional[t.Callable[[], bool]]]:
) -> tuple[str, str, t.Optional[t.Callable[[], bool]]]:
# Use posixpath even on Windows to avoid "drive:" or UNC
# segments breaking out of the search directory. Use normpath to
# convert Windows altsep to sep.
@ -401,8 +400,8 @@ class PackageLoader(BaseLoader):
return source.decode(self.encoding), p, up_to_date
def list_templates(self) -> t.List[str]:
results: t.List[str] = []
def list_templates(self) -> list[str]:
results: list[str] = []
if self._archive is None:
# Package is a directory.
@ -444,13 +443,13 @@ class DictLoader(BaseLoader):
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, None, t.Callable[[], bool]]:
) -> tuple[str, None, t.Callable[[], bool]]:
if template in self.mapping:
source = self.mapping[template]
return source, None, lambda: source == self.mapping.get(template)
raise TemplateNotFound(template)
def list_templates(self) -> t.List[str]:
def list_templates(self) -> list[str]:
return sorted(self.mapping)
@ -478,7 +477,7 @@ class FunctionLoader(BaseLoader):
[str],
t.Optional[
t.Union[
str, t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
str, tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
]
],
],
@ -487,7 +486,7 @@ class FunctionLoader(BaseLoader):
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
rv = self.load_func(template)
if rv is None:
@ -520,7 +519,7 @@ class PrefixLoader(BaseLoader):
self.mapping = mapping
self.delimiter = delimiter
def get_loader(self, template: str) -> t.Tuple[BaseLoader, str]:
def get_loader(self, template: str) -> tuple[BaseLoader, str]:
try:
prefix, name = template.split(self.delimiter, 1)
loader = self.mapping[prefix]
@ -530,7 +529,7 @@ class PrefixLoader(BaseLoader):
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
loader, name = self.get_loader(template)
try:
return loader.get_source(environment, name)
@ -554,7 +553,7 @@ class PrefixLoader(BaseLoader):
# (the one that includes the prefix)
raise TemplateNotFound(name) from e
def list_templates(self) -> t.List[str]:
def list_templates(self) -> list[str]:
result = []
for prefix, loader in self.mapping.items():
for template in loader.list_templates():
@ -581,7 +580,7 @@ class ChoiceLoader(BaseLoader):
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
) -> tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]:
for loader in self.loaders:
try:
return loader.get_source(environment, template)
@ -603,7 +602,7 @@ class ChoiceLoader(BaseLoader):
pass
raise TemplateNotFound(name)
def list_templates(self) -> t.List[str]:
def list_templates(self) -> list[str]:
found = set()
for loader in self.loaders:
found.update(loader.list_templates())

View File

@ -17,7 +17,7 @@ class TrackingCodeGenerator(CodeGenerator):
def __init__(self, environment: "Environment") -> None:
super().__init__(environment, "<introspection>", "<introspection>")
self.undeclared_identifiers: t.Set[str] = set()
self.undeclared_identifiers: set[str] = set()
def write(self, x: str) -> None:
"""Don't write."""
@ -31,7 +31,7 @@ class TrackingCodeGenerator(CodeGenerator):
self.undeclared_identifiers.add(param)
def find_undeclared_variables(ast: nodes.Template) -> t.Set[str]:
def find_undeclared_variables(ast: nodes.Template) -> set[str]:
"""Returns a set of all variables in the AST that will be looked up from
the context at runtime. Because at compile time it's not known which
variables will be used depending on the path the execution takes at

View File

@ -19,7 +19,7 @@ if t.TYPE_CHECKING:
_NodeBound = t.TypeVar("_NodeBound", bound="Node")
_binop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
_binop_to_func: dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
"*": operator.mul,
"/": operator.truediv,
"//": operator.floordiv,
@ -29,13 +29,13 @@ _binop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
"-": operator.sub,
}
_uaop_to_func: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
_uaop_to_func: dict[str, t.Callable[[t.Any], t.Any]] = {
"not": operator.not_,
"+": operator.pos,
"-": operator.neg,
}
_cmpop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
_cmpop_to_func: dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
"eq": operator.eq,
"ne": operator.ne,
"gt": operator.gt,
@ -58,7 +58,7 @@ class NodeType(type):
def __new__(mcs, name, bases, d): # type: ignore
for attr in "fields", "attributes":
storage: t.List[t.Tuple[str, ...]] = []
storage: list[tuple[str, ...]] = []
storage.extend(getattr(bases[0] if bases else object, attr, ()))
storage.extend(d.get(attr, ()))
assert len(bases) <= 1, "multiple inheritance not allowed"
@ -119,8 +119,8 @@ class Node(metaclass=NodeType):
all nodes automatically.
"""
fields: t.Tuple[str, ...] = ()
attributes: t.Tuple[str, ...] = ("lineno", "environment")
fields: tuple[str, ...] = ()
attributes: tuple[str, ...] = ("lineno", "environment")
abstract = True
lineno: int
@ -148,7 +148,7 @@ class Node(metaclass=NodeType):
self,
exclude: t.Optional[t.Container[str]] = None,
only: t.Optional[t.Container[str]] = None,
) -> t.Iterator[t.Tuple[str, t.Any]]:
) -> t.Iterator[tuple[str, t.Any]]:
"""This method iterates over all fields that are defined and yields
``(key, value)`` tuples. Per default all fields are returned, but
it's possible to limit that to some fields by providing the `only`
@ -183,7 +183,7 @@ class Node(metaclass=NodeType):
elif isinstance(item, Node):
yield item
def find(self, node_type: t.Type[_NodeBound]) -> t.Optional[_NodeBound]:
def find(self, node_type: type[_NodeBound]) -> t.Optional[_NodeBound]:
"""Find the first node of a given type. If no such node exists the
return value is `None`.
"""
@ -193,7 +193,7 @@ class Node(metaclass=NodeType):
return None
def find_all(
self, node_type: t.Union[t.Type[_NodeBound], t.Tuple[t.Type[_NodeBound], ...]]
self, node_type: t.Union[type[_NodeBound], tuple[type[_NodeBound], ...]]
) -> t.Iterator[_NodeBound]:
"""Find all the nodes of a given type. If the type is a tuple,
the check is performed for any of the tuple items.
@ -274,7 +274,7 @@ class Node(metaclass=NodeType):
_dump(value)
buf.append(")")
buf: t.List[str] = []
buf: list[str] = []
_dump(self)
return "".join(buf)
@ -297,7 +297,7 @@ class Template(Node):
"""
fields = ("body",)
body: t.List[Node]
body: list[Node]
class Output(Stmt):
@ -306,7 +306,7 @@ class Output(Stmt):
"""
fields = ("nodes",)
nodes: t.List["Expr"]
nodes: list["Expr"]
class Extends(Stmt):
@ -328,8 +328,8 @@ class For(Stmt):
fields = ("target", "iter", "body", "else_", "test", "recursive")
target: Node
iter: Node
body: t.List[Node]
else_: t.List[Node]
body: list[Node]
else_: list[Node]
test: t.Optional[Node]
recursive: bool
@ -339,9 +339,9 @@ class If(Stmt):
fields = ("test", "body", "elif_", "else_")
test: Node
body: t.List[Node]
elif_: t.List["If"]
else_: t.List[Node]
body: list[Node]
elif_: list["If"]
else_: list[Node]
class Macro(Stmt):
@ -352,9 +352,9 @@ class Macro(Stmt):
fields = ("name", "args", "defaults", "body")
name: str
args: t.List["Name"]
defaults: t.List["Expr"]
body: t.List[Node]
args: list["Name"]
defaults: list["Expr"]
body: list[Node]
class CallBlock(Stmt):
@ -364,16 +364,16 @@ class CallBlock(Stmt):
fields = ("call", "args", "defaults", "body")
call: "Call"
args: t.List["Name"]
defaults: t.List["Expr"]
body: t.List[Node]
args: list["Name"]
defaults: list["Expr"]
body: list[Node]
class FilterBlock(Stmt):
"""Node for filter sections."""
fields = ("body", "filter")
body: t.List[Node]
body: list[Node]
filter: "Filter"
@ -385,9 +385,9 @@ class With(Stmt):
"""
fields = ("targets", "values", "body")
targets: t.List["Expr"]
values: t.List["Expr"]
body: t.List[Node]
targets: list["Expr"]
values: list["Expr"]
body: list[Node]
class Block(Stmt):
@ -399,7 +399,7 @@ class Block(Stmt):
fields = ("name", "body", "scoped", "required")
name: str
body: t.List[Node]
body: list[Node]
scoped: bool
required: bool
@ -436,7 +436,7 @@ class FromImport(Stmt):
fields = ("template", "names", "with_context")
template: "Expr"
names: t.List[t.Union[str, t.Tuple[str, str]]]
names: list[t.Union[str, tuple[str, str]]]
with_context: bool
@ -461,7 +461,7 @@ class AssignBlock(Stmt):
fields = ("target", "filter", "body")
target: "Expr"
filter: t.Optional["Filter"]
body: t.List[Node]
body: list[Node]
class Expr(Node):
@ -627,10 +627,10 @@ class Tuple(Literal):
"""
fields = ("items", "ctx")
items: t.List[Expr]
items: list[Expr]
ctx: str
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[t.Any, ...]:
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> tuple[t.Any, ...]:
eval_ctx = get_eval_context(self, eval_ctx)
return tuple(x.as_const(eval_ctx) for x in self.items)
@ -645,9 +645,9 @@ class List(Literal):
"""Any list literal such as ``[1, 2, 3]``"""
fields = ("items",)
items: t.List[Expr]
items: list[Expr]
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.List[t.Any]:
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> list[t.Any]:
eval_ctx = get_eval_context(self, eval_ctx)
return [x.as_const(eval_ctx) for x in self.items]
@ -658,11 +658,9 @@ class Dict(Literal):
"""
fields = ("items",)
items: t.List["Pair"]
items: list["Pair"]
def as_const(
self, eval_ctx: t.Optional[EvalContext] = None
) -> t.Dict[t.Any, t.Any]:
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> dict[t.Any, t.Any]:
eval_ctx = get_eval_context(self, eval_ctx)
return dict(x.as_const(eval_ctx) for x in self.items)
@ -674,9 +672,7 @@ class Pair(Helper):
key: Expr
value: Expr
def as_const(
self, eval_ctx: t.Optional[EvalContext] = None
) -> t.Tuple[t.Any, t.Any]:
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> tuple[t.Any, t.Any]:
eval_ctx = get_eval_context(self, eval_ctx)
return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
@ -688,7 +684,7 @@ class Keyword(Helper):
key: str
value: Expr
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[str, t.Any]:
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> tuple[str, t.Any]:
eval_ctx = get_eval_context(self, eval_ctx)
return self.key, self.value.as_const(eval_ctx)
@ -717,7 +713,7 @@ class CondExpr(Expr):
def args_as_const(
node: t.Union["_FilterTestCommon", "Call"], eval_ctx: t.Optional[EvalContext]
) -> t.Tuple[t.List[t.Any], t.Dict[t.Any, t.Any]]:
) -> tuple[list[t.Any], dict[t.Any, t.Any]]:
args = [x.as_const(eval_ctx) for x in node.args]
kwargs = dict(x.as_const(eval_ctx) for x in node.kwargs)
@ -740,8 +736,8 @@ class _FilterTestCommon(Expr):
fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
node: Expr
name: str
args: t.List[Expr]
kwargs: t.List[Pair]
args: list[Expr]
kwargs: list[Pair]
dyn_args: t.Optional[Expr]
dyn_kwargs: t.Optional[Expr]
abstract = True
@ -824,8 +820,8 @@ class Call(Expr):
fields = ("node", "args", "kwargs", "dyn_args", "dyn_kwargs")
node: Expr
args: t.List[Expr]
kwargs: t.List[Keyword]
args: list[Expr]
kwargs: list[Keyword]
dyn_args: t.Optional[Expr]
dyn_kwargs: t.Optional[Expr]
@ -901,7 +897,7 @@ class Concat(Expr):
"""
fields = ("nodes",)
nodes: t.List[Expr]
nodes: list[Expr]
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> str:
eval_ctx = get_eval_context(self, eval_ctx)
@ -915,7 +911,7 @@ class Compare(Expr):
fields = ("expr", "ops")
expr: Expr
ops: t.List["Operand"]
ops: list["Operand"]
def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
eval_ctx = get_eval_context(self, eval_ctx)
@ -1152,7 +1148,7 @@ class Scope(Stmt):
"""An artificial scope."""
fields = ("body",)
body: t.List[Node]
body: list[Node]
class OverlayScope(Stmt):
@ -1171,7 +1167,7 @@ class OverlayScope(Stmt):
fields = ("context", "body")
context: Expr
body: t.List[Node]
body: list[Node]
class EvalContextModifier(Stmt):
@ -1184,7 +1180,7 @@ class EvalContextModifier(Stmt):
"""
fields = ("options",)
options: t.List[Keyword]
options: list[Keyword]
class ScopedEvalContextModifier(EvalContextModifier):
@ -1194,7 +1190,7 @@ class ScopedEvalContextModifier(EvalContextModifier):
"""
fields = ("body",)
body: t.List[Node]
body: list[Node]
# make sure nobody creates custom nodes

View File

@ -35,7 +35,7 @@ _statement_keywords = frozenset(
)
_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
_math_nodes: t.Dict[str, t.Type[nodes.Expr]] = {
_math_nodes: dict[str, type[nodes.Expr]] = {
"add": nodes.Add,
"sub": nodes.Sub,
"mul": nodes.Mul,
@ -63,21 +63,21 @@ class Parser:
self.name = name
self.filename = filename
self.closed = False
self.extensions: t.Dict[
str, t.Callable[[Parser], t.Union[nodes.Node, t.List[nodes.Node]]]
self.extensions: dict[
str, t.Callable[[Parser], t.Union[nodes.Node, list[nodes.Node]]]
] = {}
for extension in environment.iter_extensions():
for tag in extension.tags:
self.extensions[tag] = extension.parse
self._last_identifier = 0
self._tag_stack: t.List[str] = []
self._end_token_stack: t.List[t.Tuple[str, ...]] = []
self._tag_stack: list[str] = []
self._end_token_stack: list[tuple[str, ...]] = []
def fail(
self,
msg: str,
lineno: t.Optional[int] = None,
exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError,
exc: type[TemplateSyntaxError] = TemplateSyntaxError,
) -> "te.NoReturn":
"""Convenience method that raises `exc` with the message, passed
line number or last line number as well as the current name and
@ -90,10 +90,10 @@ class Parser:
def _fail_ut_eof(
self,
name: t.Optional[str],
end_token_stack: t.List[t.Tuple[str, ...]],
end_token_stack: list[tuple[str, ...]],
lineno: t.Optional[int],
) -> "te.NoReturn":
expected: t.Set[str] = set()
expected: set[str] = set()
for exprs in end_token_stack:
expected.update(map(describe_token_expr, exprs))
if end_token_stack:
@ -138,7 +138,7 @@ class Parser:
def fail_eof(
self,
end_tokens: t.Optional[t.Tuple[str, ...]] = None,
end_tokens: t.Optional[tuple[str, ...]] = None,
lineno: t.Optional[int] = None,
) -> "te.NoReturn":
"""Like fail_unknown_tag but for end of template situations."""
@ -147,9 +147,7 @@ class Parser:
stack.append(end_tokens)
self._fail_ut_eof(None, stack, lineno)
def is_tuple_end(
self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None
) -> bool:
def is_tuple_end(self, extra_end_rules: t.Optional[tuple[str, ...]] = None) -> bool:
"""Are we at the end of a tuple?"""
if self.stream.current.type in ("variable_end", "block_end", "rparen"):
return True
@ -164,7 +162,7 @@ class Parser:
nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno)
return rv
def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]:
def parse_statement(self) -> t.Union[nodes.Node, list[nodes.Node]]:
"""Parse a single statement."""
token = self.stream.current
if token.type != "name":
@ -194,8 +192,8 @@ class Parser:
self._tag_stack.pop()
def parse_statements(
self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False
) -> t.List[nodes.Node]:
self, end_tokens: tuple[str, ...], drop_needle: bool = False
) -> list[nodes.Node]:
"""Parse multiple statements into a list until one of the end tokens
is reached. This is used to parse the body of statements as it also
parses template data if appropriate. The parser checks first if the
@ -272,8 +270,8 @@ class Parser:
def parse_with(self) -> nodes.With:
node = nodes.With(lineno=next(self.stream).lineno)
targets: t.List[nodes.Expr] = []
values: t.List[nodes.Expr] = []
targets: list[nodes.Expr] = []
values: list[nodes.Expr] = []
while self.stream.current.type != "block_end":
if targets:
self.stream.expect("comma")
@ -466,7 +464,7 @@ class Parser:
self,
with_tuple: bool = True,
name_only: bool = False,
extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
extra_end_rules: t.Optional[tuple[str, ...]] = None,
with_namespace: bool = False,
) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: ...
@ -474,7 +472,7 @@ class Parser:
self,
with_tuple: bool = True,
name_only: bool = False,
extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
extra_end_rules: t.Optional[tuple[str, ...]] = None,
with_namespace: bool = False,
) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]:
"""Parse an assignment target. As Jinja allows assignments to
@ -686,7 +684,7 @@ class Parser:
self,
simplified: bool = False,
with_condexpr: bool = True,
extra_end_rules: t.Optional[t.Tuple[str, ...]] = None,
extra_end_rules: t.Optional[tuple[str, ...]] = None,
explicit_parentheses: bool = False,
with_namespace: bool = False,
) -> t.Union[nodes.Tuple, nodes.Expr]:
@ -720,7 +718,7 @@ class Parser:
def parse() -> nodes.Expr:
return self.parse_expression(with_condexpr=with_condexpr)
args: t.List[nodes.Expr] = []
args: list[nodes.Expr] = []
is_tuple = False
while True:
@ -753,7 +751,7 @@ class Parser:
def parse_list(self) -> nodes.List:
token = self.stream.expect("lbracket")
items: t.List[nodes.Expr] = []
items: list[nodes.Expr] = []
while self.stream.current.type != "rbracket":
if items:
self.stream.expect("comma")
@ -765,7 +763,7 @@ class Parser:
def parse_dict(self) -> nodes.Dict:
token = self.stream.expect("lbrace")
items: t.List[nodes.Pair] = []
items: list[nodes.Pair] = []
while self.stream.current.type != "rbrace":
if items:
self.stream.expect("comma")
@ -824,7 +822,7 @@ class Parser:
arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
return nodes.Getitem(node, arg, "load", lineno=token.lineno)
if token.type == "lbracket":
args: t.List[nodes.Expr] = []
args: list[nodes.Expr] = []
while self.stream.current.type != "rbracket":
if args:
self.stream.expect("comma")
@ -839,7 +837,7 @@ class Parser:
def parse_subscribed(self) -> nodes.Expr:
lineno = self.stream.current.lineno
args: t.List[t.Optional[nodes.Expr]]
args: list[t.Optional[nodes.Expr]]
if self.stream.current.type == "colon":
next(self.stream)
@ -871,9 +869,9 @@ class Parser:
def parse_call_args(
self,
) -> t.Tuple[
t.List[nodes.Expr],
t.List[nodes.Keyword],
) -> tuple[
list[nodes.Expr],
list[nodes.Keyword],
t.Union[nodes.Expr, None],
t.Union[nodes.Expr, None],
]:
@ -967,7 +965,7 @@ class Parser:
next(self.stream)
name += "." + self.stream.expect("name").value
dyn_args = dyn_kwargs = None
kwargs: t.List[nodes.Keyword] = []
kwargs: list[nodes.Keyword] = []
if self.stream.current.type == "lparen":
args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
elif self.stream.current.type in {
@ -994,10 +992,10 @@ class Parser:
return node
def subparse(
self, end_tokens: t.Optional[t.Tuple[str, ...]] = None
) -> t.List[nodes.Node]:
body: t.List[nodes.Node] = []
data_buffer: t.List[nodes.Node] = []
self, end_tokens: t.Optional[tuple[str, ...]] = None
) -> list[nodes.Node]:
body: list[nodes.Node] = []
data_buffer: list[nodes.Node] = []
add_data = data_buffer.append
if end_tokens is not None:

View File

@ -93,8 +93,8 @@ def str_join(seq: t.Iterable[t.Any]) -> str:
def new_context(
environment: "Environment",
template_name: t.Optional[str],
blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
vars: t.Optional[t.Dict[str, t.Any]] = None,
blocks: dict[str, t.Callable[["Context"], t.Iterator[str]]],
vars: t.Optional[dict[str, t.Any]] = None,
shared: bool = False,
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
locals: t.Optional[t.Mapping[str, t.Any]] = None,
@ -165,16 +165,16 @@ class Context:
def __init__(
self,
environment: "Environment",
parent: t.Dict[str, t.Any],
parent: dict[str, t.Any],
name: t.Optional[str],
blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
blocks: dict[str, t.Callable[["Context"], t.Iterator[str]]],
globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
):
self.parent = parent
self.vars: t.Dict[str, t.Any] = {}
self.vars: dict[str, t.Any] = {}
self.environment: Environment = environment
self.eval_ctx = EvalContext(self.environment, name)
self.exported_vars: t.Set[str] = set()
self.exported_vars: set[str] = set()
self.name = name
self.globals_keys = set() if globals is None else set(globals)
@ -244,11 +244,11 @@ class Context:
return missing
def get_exported(self) -> t.Dict[str, t.Any]:
def get_exported(self) -> dict[str, t.Any]:
"""Get a new dict with the exported variables."""
return {k: self.vars[k] for k in self.exported_vars}
def get_all(self) -> t.Dict[str, t.Any]:
def get_all(self) -> dict[str, t.Any]:
"""Return the complete context as dict including the exported
variables. For optimizations reasons this might not return an
actual copy so be careful with using it.
@ -307,7 +307,7 @@ class Context:
" StopIteration exception"
)
def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context":
def derived(self, locals: t.Optional[dict[str, t.Any]] = None) -> "Context":
"""Internal helper function to create a derived context. This is
used in situations where the system needs a new context in the same
template that is independent.
@ -348,7 +348,7 @@ class BlockReference:
self,
name: str,
context: "Context",
stack: t.List[t.Callable[["Context"], t.Iterator[str]]],
stack: list[t.Callable[["Context"], t.Iterator[str]]],
depth: int,
) -> None:
self.name = name
@ -408,7 +408,7 @@ class LoopContext:
def __init__(
self,
iterable: t.Iterable[V],
undefined: t.Type["Undefined"],
undefined: type["Undefined"],
recurse: t.Optional["LoopRenderFunc"] = None,
depth0: int = 0,
) -> None:
@ -558,7 +558,7 @@ class LoopContext:
def __iter__(self) -> "LoopContext":
return self
def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:
def __next__(self) -> tuple[t.Any, "LoopContext"]:
if self._after is not missing:
rv = self._after
self._after = missing
@ -646,7 +646,7 @@ class AsyncLoopContext(LoopContext):
def __aiter__(self) -> "AsyncLoopContext":
return self
async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:
async def __anext__(self) -> tuple[t.Any, "AsyncLoopContext"]:
if self._after is not missing:
rv = self._after
self._after = missing
@ -667,7 +667,7 @@ class Macro:
environment: "Environment",
func: t.Callable[..., str],
name: str,
arguments: t.List[str],
arguments: list[str],
catch_kwargs: bool,
catch_varargs: bool,
caller: bool,
@ -769,7 +769,7 @@ class Macro:
return self._invoke(arguments, autoescape)
async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
async def _async_invoke(self, arguments: list[t.Any], autoescape: bool) -> str:
rv = await self._func(*arguments) # type: ignore
if autoescape:
@ -777,7 +777,7 @@ class Macro:
return rv # type: ignore
def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
def _invoke(self, arguments: list[t.Any], autoescape: bool) -> str:
if self._environment.is_async:
return self._async_invoke(arguments, autoescape) # type: ignore
@ -820,7 +820,7 @@ class Undefined:
hint: t.Optional[str] = None,
obj: t.Any = missing,
name: t.Optional[str] = None,
exc: t.Type[TemplateRuntimeError] = UndefinedError,
exc: type[TemplateRuntimeError] = UndefinedError,
) -> None:
self._undefined_hint = hint
self._undefined_obj = obj
@ -910,8 +910,8 @@ class Undefined:
def make_logging_undefined(
logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined
) -> t.Type[Undefined]:
logger: t.Optional["logging.Logger"] = None, base: type[Undefined] = Undefined
) -> type[Undefined]:
"""Given a logger object this returns a new undefined class that will
log certain failures. It will log iterations and printing. If no
logger is given a default logger is created.

View File

@ -25,10 +25,10 @@ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
MAX_RANGE = 100000
#: Unsafe function attributes.
UNSAFE_FUNCTION_ATTRIBUTES: t.Set[str] = set()
UNSAFE_FUNCTION_ATTRIBUTES: set[str] = set()
#: Unsafe method attributes. Function attributes are unsafe for methods too.
UNSAFE_METHOD_ATTRIBUTES: t.Set[str] = set()
UNSAFE_METHOD_ATTRIBUTES: set[str] = set()
#: unsafe generator attributes.
UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
@ -39,7 +39,7 @@ UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
#: unsafe attributes on async generators
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
_mutable_spec: t.Tuple[t.Tuple[t.Type[t.Any], t.FrozenSet[str]], ...] = (
_mutable_spec: tuple[tuple[type[t.Any], frozenset[str]], ...] = (
(
abc.MutableSet,
frozenset(
@ -190,7 +190,7 @@ class SandboxedEnvironment(Environment):
#: default callback table for the binary operators. A copy of this is
#: available on each instance of a sandboxed environment as
#: :attr:`binop_table`
default_binop_table: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
default_binop_table: dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
@ -203,7 +203,7 @@ class SandboxedEnvironment(Environment):
#: default callback table for the unary operators. A copy of this is
#: available on each instance of a sandboxed environment as
#: :attr:`unop_table`
default_unop_table: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
default_unop_table: dict[str, t.Callable[[t.Any], t.Any]] = {
"+": operator.pos,
"-": operator.neg,
}
@ -222,7 +222,7 @@ class SandboxedEnvironment(Environment):
#: interested in.
#:
#: .. versionadded:: 2.6
intercepted_binops: t.FrozenSet[str] = frozenset()
intercepted_binops: frozenset[str] = frozenset()
#: a set of unary operators that should be intercepted. Each operator
#: that is added to this set (empty by default) is delegated to the
@ -237,7 +237,7 @@ class SandboxedEnvironment(Environment):
#: interested in.
#:
#: .. versionadded:: 2.6
intercepted_unops: t.FrozenSet[str] = frozenset()
intercepted_unops: frozenset[str] = frozenset()
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
super().__init__(*args, **kwargs)
@ -357,7 +357,7 @@ class SandboxedEnvironment(Environment):
if not isinstance(f_self, str):
return None
str_type: t.Type[str] = type(f_self)
str_type: type[str] = type(f_self)
is_format_map = value.__name__ == "format_map"
formatter: SandboxedFormatter
@ -421,7 +421,7 @@ class SandboxedFormatter(Formatter):
def get_field(
self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
) -> t.Tuple[t.Any, str]:
) -> tuple[t.Any, str]:
first, rest = formatter_field_name_split(field_name)
obj = self.get_value(first, args, kwargs)
for is_attr, i in rest:

View File

@ -438,8 +438,8 @@ class LRUCache:
def __init__(self, capacity: int) -> None:
self.capacity = capacity
self._mapping: t.Dict[t.Any, t.Any] = {}
self._queue: te.Deque[t.Any] = deque()
self._mapping: dict[t.Any, t.Any] = {}
self._queue: deque[t.Any] = deque()
self._postinit()
def _postinit(self) -> None:
@ -461,7 +461,7 @@ class LRUCache:
self.__dict__.update(d)
self._postinit()
def __getnewargs__(self) -> t.Tuple[t.Any, ...]:
def __getnewargs__(self) -> tuple[t.Any, ...]:
return (self.capacity,)
def copy(self) -> "te.Self":
@ -552,7 +552,7 @@ class LRUCache:
except ValueError:
pass
def items(self) -> t.Iterable[t.Tuple[t.Any, t.Any]]:
def items(self) -> t.Iterable[tuple[t.Any, t.Any]]:
"""Return a list of items."""
result = [(key, self._mapping[key]) for key in list(self._queue)]
result.reverse()

View File

@ -80,7 +80,7 @@ class NodeTransformer(NodeVisitor):
setattr(node, field, new_node)
return node
def visit_list(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.List[Node]:
def visit_list(self, node: Node, *args: t.Any, **kwargs: t.Any) -> list[Node]:
"""As transformers may return lists in some places this method
can be used to enforce a list as return value.
"""

View File

@ -449,9 +449,7 @@ class TestAsyncForLoop:
def test_reversed_bug(self, test_env_async):
tmpl = test_env_async.from_string(
"{% for i in items %}{{ i }}"
"{% if not loop.last %}"
",{% endif %}{% endfor %}"
"{% for i in items %}{{ i }}{% if not loop.last %},{% endif %}{% endfor %}"
)
assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3"

View File

@ -191,9 +191,7 @@ class TestForLoop:
def test_reversed_bug(self, env):
tmpl = env.from_string(
"{% for i in items %}{{ i }}"
"{% if not loop.last %}"
",{% endif %}{% endfor %}"
"{% for i in items %}{{ i }}{% if not loop.last %},{% endif %}{% endfor %}"
)
assert tmpl.render(items=reversed([3, 2, 1])) == "1,2,3"

View File

@ -23,9 +23,9 @@ class TestDebug:
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
), f"Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}"
assert m is not None, (
f"Traceback did not match:\n\n{''.join(tb)}\nexpected:\n{expected_tb}"
)
def test_runtime_error(self, fs_env):
def test():

View File

@ -554,8 +554,7 @@ class TestNewstyleInternationalization:
newstyle=True,
)
t = env.from_string(
'{% autoescape ae %}{{ gettext("foo", name='
'"<test>") }}{% endautoescape %}'
'{% autoescape ae %}{{ gettext("foo", name="<test>") }}{% endautoescape %}'
)
assert t.render(ae=True) == "<strong>Wert: &lt;test&gt;</strong>"
assert t.render(ae=False) == "<strong>Wert: <test></strong>"

View File

@ -357,7 +357,7 @@ class TestFilter:
def test_urlize(self, env):
tmpl = env.from_string('{{ "foo example.org bar"|urlize }}')
assert tmpl.render() == (
'foo <a href="https://example.org" rel="noopener">' "example.org</a> bar"
'foo <a href="https://example.org" rel="noopener">example.org</a> bar'
)
tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}')
assert tmpl.render() == (

View File

@ -43,8 +43,7 @@ class TestTokenStream:
class TestLexer:
def test_raw1(self, env):
tmpl = env.from_string(
"{% raw %}foo{% endraw %}|"
"{%raw%}{{ bar }}|{% baz %}{% endraw %}"
"{% raw %}foo{% endraw %}|{%raw%}{{ bar }}|{% baz %}{% endraw %}"
)
assert tmpl.render() == "foo|{{ bar }}|{% baz %}"