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