about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/jinja2/nativetypes.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/jinja2/nativetypes.py')
-rw-r--r--.venv/lib/python3.12/site-packages/jinja2/nativetypes.py130
1 files changed, 130 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/jinja2/nativetypes.py b/.venv/lib/python3.12/site-packages/jinja2/nativetypes.py
new file mode 100644
index 00000000..71db8cc3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/jinja2/nativetypes.py
@@ -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