1e31aef6aSopenharmony_ciimport typing as t
2e31aef6aSopenharmony_cifrom ast import literal_eval
3e31aef6aSopenharmony_cifrom ast import parse
4e31aef6aSopenharmony_cifrom itertools import chain
5e31aef6aSopenharmony_cifrom itertools import islice
6e31aef6aSopenharmony_cifrom types import GeneratorType
7e31aef6aSopenharmony_ci
8e31aef6aSopenharmony_cifrom . import nodes
9e31aef6aSopenharmony_cifrom .compiler import CodeGenerator
10e31aef6aSopenharmony_cifrom .compiler import Frame
11e31aef6aSopenharmony_cifrom .compiler import has_safe_repr
12e31aef6aSopenharmony_cifrom .environment import Environment
13e31aef6aSopenharmony_cifrom .environment import Template
14e31aef6aSopenharmony_ci
15e31aef6aSopenharmony_ci
16e31aef6aSopenharmony_cidef native_concat(values: t.Iterable[t.Any]) -> t.Optional[t.Any]:
17e31aef6aSopenharmony_ci    """Return a native Python type from the list of compiled nodes. If
18e31aef6aSopenharmony_ci    the result is a single node, its value is returned. Otherwise, the
19e31aef6aSopenharmony_ci    nodes are concatenated as strings. If the result can be parsed with
20e31aef6aSopenharmony_ci    :func:`ast.literal_eval`, the parsed value is returned. Otherwise,
21e31aef6aSopenharmony_ci    the string is returned.
22e31aef6aSopenharmony_ci
23e31aef6aSopenharmony_ci    :param values: Iterable of outputs to concatenate.
24e31aef6aSopenharmony_ci    """
25e31aef6aSopenharmony_ci    head = list(islice(values, 2))
26e31aef6aSopenharmony_ci
27e31aef6aSopenharmony_ci    if not head:
28e31aef6aSopenharmony_ci        return None
29e31aef6aSopenharmony_ci
30e31aef6aSopenharmony_ci    if len(head) == 1:
31e31aef6aSopenharmony_ci        raw = head[0]
32e31aef6aSopenharmony_ci        if not isinstance(raw, str):
33e31aef6aSopenharmony_ci            return raw
34e31aef6aSopenharmony_ci    else:
35e31aef6aSopenharmony_ci        if isinstance(values, GeneratorType):
36e31aef6aSopenharmony_ci            values = chain(head, values)
37e31aef6aSopenharmony_ci        raw = "".join([str(v) for v in values])
38e31aef6aSopenharmony_ci
39e31aef6aSopenharmony_ci    try:
40e31aef6aSopenharmony_ci        return literal_eval(
41e31aef6aSopenharmony_ci            # In Python 3.10+ ast.literal_eval removes leading spaces/tabs
42e31aef6aSopenharmony_ci            # from the given string. For backwards compatibility we need to
43e31aef6aSopenharmony_ci            # parse the string ourselves without removing leading spaces/tabs.
44e31aef6aSopenharmony_ci            parse(raw, mode="eval")
45e31aef6aSopenharmony_ci        )
46e31aef6aSopenharmony_ci    except (ValueError, SyntaxError, MemoryError):
47e31aef6aSopenharmony_ci        return raw
48e31aef6aSopenharmony_ci
49e31aef6aSopenharmony_ci
50e31aef6aSopenharmony_ciclass NativeCodeGenerator(CodeGenerator):
51e31aef6aSopenharmony_ci    """A code generator which renders Python types by not adding
52e31aef6aSopenharmony_ci    ``str()`` around output nodes.
53e31aef6aSopenharmony_ci    """
54e31aef6aSopenharmony_ci
55e31aef6aSopenharmony_ci    @staticmethod
56e31aef6aSopenharmony_ci    def _default_finalize(value: t.Any) -> t.Any:
57e31aef6aSopenharmony_ci        return value
58e31aef6aSopenharmony_ci
59e31aef6aSopenharmony_ci    def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
60e31aef6aSopenharmony_ci        return repr("".join([str(v) for v in group]))
61e31aef6aSopenharmony_ci
62e31aef6aSopenharmony_ci    def _output_child_to_const(
63e31aef6aSopenharmony_ci        self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
64e31aef6aSopenharmony_ci    ) -> t.Any:
65e31aef6aSopenharmony_ci        const = node.as_const(frame.eval_ctx)
66e31aef6aSopenharmony_ci
67e31aef6aSopenharmony_ci        if not has_safe_repr(const):
68e31aef6aSopenharmony_ci            raise nodes.Impossible()
69e31aef6aSopenharmony_ci
70e31aef6aSopenharmony_ci        if isinstance(node, nodes.TemplateData):
71e31aef6aSopenharmony_ci            return const
72e31aef6aSopenharmony_ci
73e31aef6aSopenharmony_ci        return finalize.const(const)  # type: ignore
74e31aef6aSopenharmony_ci
75e31aef6aSopenharmony_ci    def _output_child_pre(
76e31aef6aSopenharmony_ci        self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
77e31aef6aSopenharmony_ci    ) -> None:
78e31aef6aSopenharmony_ci        if finalize.src is not None:
79e31aef6aSopenharmony_ci            self.write(finalize.src)
80e31aef6aSopenharmony_ci
81e31aef6aSopenharmony_ci    def _output_child_post(
82e31aef6aSopenharmony_ci        self, node: nodes.Expr, frame: Frame, finalize: CodeGenerator._FinalizeInfo
83e31aef6aSopenharmony_ci    ) -> None:
84e31aef6aSopenharmony_ci        if finalize.src is not None:
85e31aef6aSopenharmony_ci            self.write(")")
86e31aef6aSopenharmony_ci
87e31aef6aSopenharmony_ci
88e31aef6aSopenharmony_ciclass NativeEnvironment(Environment):
89e31aef6aSopenharmony_ci    """An environment that renders templates to native Python types."""
90e31aef6aSopenharmony_ci
91e31aef6aSopenharmony_ci    code_generator_class = NativeCodeGenerator
92e31aef6aSopenharmony_ci    concat = staticmethod(native_concat)  # type: ignore
93e31aef6aSopenharmony_ci
94e31aef6aSopenharmony_ci
95e31aef6aSopenharmony_ciclass NativeTemplate(Template):
96e31aef6aSopenharmony_ci    environment_class = NativeEnvironment
97e31aef6aSopenharmony_ci
98e31aef6aSopenharmony_ci    def render(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
99e31aef6aSopenharmony_ci        """Render the template to produce a native Python type. If the
100e31aef6aSopenharmony_ci        result is a single node, its value is returned. Otherwise, the
101e31aef6aSopenharmony_ci        nodes are concatenated as strings. If the result can be parsed
102e31aef6aSopenharmony_ci        with :func:`ast.literal_eval`, the parsed value is returned.
103e31aef6aSopenharmony_ci        Otherwise, the string is returned.
104e31aef6aSopenharmony_ci        """
105e31aef6aSopenharmony_ci        ctx = self.new_context(dict(*args, **kwargs))
106e31aef6aSopenharmony_ci
107e31aef6aSopenharmony_ci        try:
108e31aef6aSopenharmony_ci            return self.environment_class.concat(  # type: ignore
109e31aef6aSopenharmony_ci                self.root_render_func(ctx)
110e31aef6aSopenharmony_ci            )
111e31aef6aSopenharmony_ci        except Exception:
112e31aef6aSopenharmony_ci            return self.environment.handle_exception()
113e31aef6aSopenharmony_ci
114e31aef6aSopenharmony_ci    async def render_async(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
115e31aef6aSopenharmony_ci        if not self.environment.is_async:
116e31aef6aSopenharmony_ci            raise RuntimeError(
117e31aef6aSopenharmony_ci                "The environment was not created with async mode enabled."
118e31aef6aSopenharmony_ci            )
119e31aef6aSopenharmony_ci
120e31aef6aSopenharmony_ci        ctx = self.new_context(dict(*args, **kwargs))
121e31aef6aSopenharmony_ci
122e31aef6aSopenharmony_ci        try:
123e31aef6aSopenharmony_ci            return self.environment_class.concat(  # type: ignore
124e31aef6aSopenharmony_ci                [n async for n in self.root_render_func(ctx)]  # type: ignore
125e31aef6aSopenharmony_ci            )
126e31aef6aSopenharmony_ci        except Exception:
127e31aef6aSopenharmony_ci            return self.environment.handle_exception()
128e31aef6aSopenharmony_ci
129e31aef6aSopenharmony_ci
130e31aef6aSopenharmony_ciNativeEnvironment.template_class = NativeTemplate
131