Загрузить файлы в «venv/Lib/site-packages/jinja2»
This commit is contained in:
112
venv/Lib/site-packages/jinja2/meta.py
Normal file
112
venv/Lib/site-packages/jinja2/meta.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
"""Functions that expose information about templates that might be
|
||||||
|
interesting for introspection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import nodes
|
||||||
|
from .compiler import CodeGenerator
|
||||||
|
from .compiler import Frame
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .environment import Environment
|
||||||
|
|
||||||
|
|
||||||
|
class TrackingCodeGenerator(CodeGenerator):
|
||||||
|
"""We abuse the code generator for introspection."""
|
||||||
|
|
||||||
|
def __init__(self, environment: "Environment") -> None:
|
||||||
|
super().__init__(environment, "<introspection>", "<introspection>")
|
||||||
|
self.undeclared_identifiers: t.Set[str] = set()
|
||||||
|
|
||||||
|
def write(self, x: str) -> None:
|
||||||
|
"""Don't write."""
|
||||||
|
|
||||||
|
def enter_frame(self, frame: Frame) -> None:
|
||||||
|
"""Remember all undeclared identifiers."""
|
||||||
|
super().enter_frame(frame)
|
||||||
|
|
||||||
|
for _, (action, param) in frame.symbols.loads.items():
|
||||||
|
if action == "resolve" and param not in self.environment.globals:
|
||||||
|
self.undeclared_identifiers.add(param)
|
||||||
|
|
||||||
|
|
||||||
|
def find_undeclared_variables(ast: nodes.Template) -> t.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
|
||||||
|
runtime, all variables are returned.
|
||||||
|
|
||||||
|
>>> from jinja2 import Environment, meta
|
||||||
|
>>> env = Environment()
|
||||||
|
>>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
|
||||||
|
>>> meta.find_undeclared_variables(ast) == {'bar'}
|
||||||
|
True
|
||||||
|
|
||||||
|
.. admonition:: Implementation
|
||||||
|
|
||||||
|
Internally the code generator is used for finding undeclared variables.
|
||||||
|
This is good to know because the code generator might raise a
|
||||||
|
:exc:`TemplateAssertionError` during compilation and as a matter of
|
||||||
|
fact this function can currently raise that exception as well.
|
||||||
|
"""
|
||||||
|
codegen = TrackingCodeGenerator(ast.environment) # type: ignore
|
||||||
|
codegen.visit(ast)
|
||||||
|
return codegen.undeclared_identifiers
|
||||||
|
|
||||||
|
|
||||||
|
_ref_types = (nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include)
|
||||||
|
_RefType = t.Union[nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include]
|
||||||
|
|
||||||
|
|
||||||
|
def find_referenced_templates(ast: nodes.Template) -> t.Iterator[t.Optional[str]]:
|
||||||
|
"""Finds all the referenced templates from the AST. This will return an
|
||||||
|
iterator over all the hardcoded template extensions, inclusions and
|
||||||
|
imports. If dynamic inheritance or inclusion is used, `None` will be
|
||||||
|
yielded.
|
||||||
|
|
||||||
|
>>> from jinja2 import Environment, meta
|
||||||
|
>>> env = Environment()
|
||||||
|
>>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
|
||||||
|
>>> list(meta.find_referenced_templates(ast))
|
||||||
|
['layout.html', None]
|
||||||
|
|
||||||
|
This function is useful for dependency tracking. For example if you want
|
||||||
|
to rebuild parts of the website after a layout template has changed.
|
||||||
|
"""
|
||||||
|
template_name: t.Any
|
||||||
|
|
||||||
|
for node in ast.find_all(_ref_types):
|
||||||
|
template: nodes.Expr = node.template # type: ignore
|
||||||
|
|
||||||
|
if not isinstance(template, nodes.Const):
|
||||||
|
# a tuple with some non consts in there
|
||||||
|
if isinstance(template, (nodes.Tuple, nodes.List)):
|
||||||
|
for template_name in template.items:
|
||||||
|
# something const, only yield the strings and ignore
|
||||||
|
# non-string consts that really just make no sense
|
||||||
|
if isinstance(template_name, nodes.Const):
|
||||||
|
if isinstance(template_name.value, str):
|
||||||
|
yield template_name.value
|
||||||
|
# something dynamic in there
|
||||||
|
else:
|
||||||
|
yield None
|
||||||
|
# something dynamic we don't know about here
|
||||||
|
else:
|
||||||
|
yield None
|
||||||
|
continue
|
||||||
|
# constant is a basestring, direct template name
|
||||||
|
if isinstance(template.value, str):
|
||||||
|
yield template.value
|
||||||
|
# a tuple or list (latter *should* not happen) made of consts,
|
||||||
|
# yield the consts that are strings. We could warn here for
|
||||||
|
# non string values
|
||||||
|
elif isinstance(node, nodes.Include) and isinstance(
|
||||||
|
template.value, (tuple, list)
|
||||||
|
):
|
||||||
|
for template_name in template.value:
|
||||||
|
if isinstance(template_name, str):
|
||||||
|
yield template_name
|
||||||
|
# something else we don't care about, we could warn here
|
||||||
|
else:
|
||||||
|
yield None
|
||||||
130
venv/Lib/site-packages/jinja2/nativetypes.py
Normal file
130
venv/Lib/site-packages/jinja2/nativetypes.py
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import typing as t
|
||||||
|
from ast import literal_eval
|
||||||
|
from ast import parse
|
||||||
|
from itertools import chain
|
||||||
|
from itertools import islice
|
||||||
|
from types import GeneratorType
|
||||||
|
|
||||||
|
from . import nodes
|
||||||
|
from .compiler import CodeGenerator
|
||||||
|
from .compiler import Frame
|
||||||
|
from .compiler import has_safe_repr
|
||||||
|
from .environment import Environment
|
||||||
|
from .environment import Template
|
||||||
|
|
||||||
|
|
||||||
|
def native_concat(values: t.Iterable[t.Any]) -> t.Optional[t.Any]:
|
||||||
|
"""Return a native Python type from the list of compiled nodes. If
|
||||||
|
the result is a single node, its value is returned. Otherwise, the
|
||||||
|
nodes are concatenated as strings. If the result can be parsed with
|
||||||
|
:func:`ast.literal_eval`, the parsed value is returned. Otherwise,
|
||||||
|
the string is returned.
|
||||||
|
|
||||||
|
:param values: Iterable of outputs to concatenate.
|
||||||
|
"""
|
||||||
|
head = list(islice(values, 2))
|
||||||
|
|
||||||
|
if not head:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(head) == 1:
|
||||||
|
raw = head[0]
|
||||||
|
if not isinstance(raw, str):
|
||||||
|
return raw
|
||||||
|
else:
|
||||||
|
if isinstance(values, GeneratorType):
|
||||||
|
values = chain(head, values)
|
||||||
|
raw = "".join([str(v) for v in values])
|
||||||
|
|
||||||
|
try:
|
||||||
|
return literal_eval(
|
||||||
|
# In Python 3.10+ ast.literal_eval removes leading spaces/tabs
|
||||||
|
# from the given string. For backwards compatibility we need to
|
||||||
|
# parse the string ourselves without removing leading spaces/tabs.
|
||||||
|
parse(raw, mode="eval")
|
||||||
|
)
|
||||||
|
except (ValueError, SyntaxError, MemoryError):
|
||||||
|
return raw
|
||||||
|
|
||||||
|
|
||||||
|
class NativeCodeGenerator(CodeGenerator):
|
||||||
|
"""A code generator which renders Python types by not adding
|
||||||
|
``str()`` around output nodes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _default_finalize(value: t.Any) -> t.Any:
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
|
||||||
|
return repr("".join([str(v) for v in group]))
|
||||||
|
|
||||||
|
def _output_child_to_const(
|
||||||
|
self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
|
||||||
|
) -> t.Any:
|
||||||
|
const = node.as_const(frame.eval_ctx)
|
||||||
|
|
||||||
|
if not has_safe_repr(const):
|
||||||
|
raise nodes.Impossible()
|
||||||
|
|
||||||
|
if isinstance(node, nodes.TemplateData):
|
||||||
|
return const
|
||||||
|
|
||||||
|
return finalize.const(const) # type: ignore
|
||||||
|
|
||||||
|
def _output_child_pre(
|
||||||
|
self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
|
||||||
|
) -> None:
|
||||||
|
if finalize.src is not None:
|
||||||
|
self.write(finalize.src)
|
||||||
|
|
||||||
|
def _output_child_post(
|
||||||
|
self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
|
||||||
|
) -> None:
|
||||||
|
if finalize.src is not None:
|
||||||
|
self.write(")")
|
||||||
|
|
||||||
|
|
||||||
|
class NativeEnvironment(Environment):
|
||||||
|
"""An environment that renders templates to native Python types."""
|
||||||
|
|
||||||
|
code_generator_class = NativeCodeGenerator
|
||||||
|
concat = staticmethod(native_concat) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class NativeTemplate(Template):
|
||||||
|
environment_class = NativeEnvironment
|
||||||
|
|
||||||
|
def render(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||||
|
"""Render the template to produce a native Python type. If the
|
||||||
|
result is a single node, its value is returned. Otherwise, the
|
||||||
|
nodes are concatenated as strings. If the result can be parsed
|
||||||
|
with :func:`ast.literal_eval`, the parsed value is returned.
|
||||||
|
Otherwise, the string is returned.
|
||||||
|
"""
|
||||||
|
ctx = self.new_context(dict(*args, **kwargs))
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.environment_class.concat( # type: ignore
|
||||||
|
self.root_render_func(ctx)
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return self.environment.handle_exception()
|
||||||
|
|
||||||
|
async def render_async(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||||
|
if not self.environment.is_async:
|
||||||
|
raise RuntimeError(
|
||||||
|
"The environment was not created with async mode enabled."
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = self.new_context(dict(*args, **kwargs))
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.environment_class.concat( # type: ignore
|
||||||
|
[n async for n in self.root_render_func(ctx)] # type: ignore
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return self.environment.handle_exception()
|
||||||
|
|
||||||
|
|
||||||
|
NativeEnvironment.template_class = NativeTemplate
|
||||||
1206
venv/Lib/site-packages/jinja2/nodes.py
Normal file
1206
venv/Lib/site-packages/jinja2/nodes.py
Normal file
File diff suppressed because it is too large
Load Diff
48
venv/Lib/site-packages/jinja2/optimizer.py
Normal file
48
venv/Lib/site-packages/jinja2/optimizer.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
"""The optimizer tries to constant fold expressions and modify the AST
|
||||||
|
in place so that it should be faster to evaluate.
|
||||||
|
|
||||||
|
Because the AST does not contain all the scoping information and the
|
||||||
|
compiler has to find that out, we cannot do all the optimizations we
|
||||||
|
want. For example, loop unrolling doesn't work because unrolled loops
|
||||||
|
would have a different scope. The solution would be a second syntax tree
|
||||||
|
that stored the scoping rules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from . import nodes
|
||||||
|
from .visitor import NodeTransformer
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .environment import Environment
|
||||||
|
|
||||||
|
|
||||||
|
def optimize(node: nodes.Node, environment: "Environment") -> nodes.Node:
|
||||||
|
"""The context hint can be used to perform an static optimization
|
||||||
|
based on the context given."""
|
||||||
|
optimizer = Optimizer(environment)
|
||||||
|
return t.cast(nodes.Node, optimizer.visit(node))
|
||||||
|
|
||||||
|
|
||||||
|
class Optimizer(NodeTransformer):
|
||||||
|
def __init__(self, environment: "t.Optional[Environment]") -> None:
|
||||||
|
self.environment = environment
|
||||||
|
|
||||||
|
def generic_visit(
|
||||||
|
self, node: nodes.Node, *args: t.Any, **kwargs: t.Any
|
||||||
|
) -> nodes.Node:
|
||||||
|
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.
|
||||||
|
if isinstance(node, nodes.Expr):
|
||||||
|
try:
|
||||||
|
return nodes.Const.from_untrusted(
|
||||||
|
node.as_const(args[0] if args else None),
|
||||||
|
lineno=node.lineno,
|
||||||
|
environment=self.environment,
|
||||||
|
)
|
||||||
|
except nodes.Impossible:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return node
|
||||||
1049
venv/Lib/site-packages/jinja2/parser.py
Normal file
1049
venv/Lib/site-packages/jinja2/parser.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user