1from ast import literal_eval 2from itertools import chain 3from itertools import islice 4 5from . import nodes 6from ._compat import text_type 7from .compiler import CodeGenerator 8from .compiler import has_safe_repr 9from .environment import Environment 10from .environment import Template 11 12 13def native_concat(nodes): 14 """Return a native Python type from the list of compiled nodes. If 15 the result is a single node, its value is returned. Otherwise, the 16 nodes are concatenated as strings. If the result can be parsed with 17 :func:`ast.literal_eval`, the parsed value is returned. Otherwise, 18 the string is returned. 19 20 :param nodes: Iterable of nodes to concatenate. 21 """ 22 head = list(islice(nodes, 2)) 23 24 if not head: 25 return None 26 27 if len(head) == 1: 28 raw = head[0] 29 else: 30 raw = u"".join([text_type(v) for v in chain(head, nodes)]) 31 32 try: 33 return literal_eval(raw) 34 except (ValueError, SyntaxError, MemoryError): 35 return raw 36 37 38class NativeCodeGenerator(CodeGenerator): 39 """A code generator which renders Python types by not adding 40 ``to_string()`` around output nodes. 41 """ 42 43 @staticmethod 44 def _default_finalize(value): 45 return value 46 47 def _output_const_repr(self, group): 48 return repr(u"".join([text_type(v) for v in group])) 49 50 def _output_child_to_const(self, node, frame, finalize): 51 const = node.as_const(frame.eval_ctx) 52 53 if not has_safe_repr(const): 54 raise nodes.Impossible() 55 56 if isinstance(node, nodes.TemplateData): 57 return const 58 59 return finalize.const(const) 60 61 def _output_child_pre(self, node, frame, finalize): 62 if finalize.src is not None: 63 self.write(finalize.src) 64 65 def _output_child_post(self, node, frame, finalize): 66 if finalize.src is not None: 67 self.write(")") 68 69 70class NativeEnvironment(Environment): 71 """An environment that renders templates to native Python types.""" 72 73 code_generator_class = NativeCodeGenerator 74 75 76class NativeTemplate(Template): 77 environment_class = NativeEnvironment 78 79 def render(self, *args, **kwargs): 80 """Render the template to produce a native Python type. If the 81 result is a single node, its value is returned. Otherwise, the 82 nodes are concatenated as strings. If the result can be parsed 83 with :func:`ast.literal_eval`, the parsed value is returned. 84 Otherwise, the string is returned. 85 """ 86 vars = dict(*args, **kwargs) 87 88 try: 89 return native_concat(self.root_render_func(self.new_context(vars))) 90 except Exception: 91 return self.environment.handle_exception() 92 93 94NativeEnvironment.template_class = NativeTemplate 95