File: //lib/python3/dist-packages/jinja2/nativetypes.py
import sys
from ast import literal_eval
from itertools import islice, chain
from jinja2 import nodes
from jinja2._compat import text_type
from jinja2.compiler import CodeGenerator, has_safe_repr
from jinja2.environment import Environment, Template
from jinja2.utils import concat, escape
def native_concat(nodes):
    """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.
    """
    head = list(islice(nodes, 2))
    if not head:
        return None
    if len(head) == 1:
        out = head[0]
    else:
        out = u''.join([text_type(v) for v in chain(head, nodes)])
    try:
        return literal_eval(out)
    except (ValueError, SyntaxError, MemoryError):
        return out
class NativeCodeGenerator(CodeGenerator):
    """A code generator which avoids injecting ``to_string()`` calls around the
    internal code Jinja uses to render templates.
    """
    def visit_Output(self, node, frame):
        """Same as :meth:`CodeGenerator.visit_Output`, but do not call
        ``to_string`` on output nodes in generated code.
        """
        if self.has_known_extends and frame.require_output_check:
            return
        finalize = self.environment.finalize
        finalize_context = getattr(finalize, 'contextfunction', False)
        finalize_eval = getattr(finalize, 'evalcontextfunction', False)
        finalize_env = getattr(finalize, 'environmentfunction', False)
        if finalize is not None:
            if finalize_context or finalize_eval:
                const_finalize = None
            elif finalize_env:
                def const_finalize(x):
                    return finalize(self.environment, x)
            else:
                const_finalize = finalize
        else:
            def const_finalize(x):
                return x
        # If we are inside a frame that requires output checking, we do so.
        outdent_later = False
        if frame.require_output_check:
            self.writeline('if parent_template is None:')
            self.indent()
            outdent_later = True
        # Try to evaluate as many chunks as possible into a static string at
        # compile time.
        body = []
        for child in node.nodes:
            try:
                if const_finalize is None:
                    raise nodes.Impossible()
                const = child.as_const(frame.eval_ctx)
                if not has_safe_repr(const):
                    raise nodes.Impossible()
            except nodes.Impossible:
                body.append(child)
                continue
            # the frame can't be volatile here, because otherwise the as_const
            # function would raise an Impossible exception at that point
            try:
                if frame.eval_ctx.autoescape:
                    if hasattr(const, '__html__'):
                        const = const.__html__()
                    else:
                        const = escape(const)
                const = const_finalize(const)
            except Exception:
                # if something goes wrong here we evaluate the node at runtime
                # for easier debugging
                body.append(child)
                continue
            if body and isinstance(body[-1], list):
                body[-1].append(const)
            else:
                body.append([const])
        # if we have less than 3 nodes or a buffer we yield or extend/append
        if len(body) < 3 or frame.buffer is not None:
            if frame.buffer is not None:
                # for one item we append, for more we extend
                if len(body) == 1:
                    self.writeline('%s.append(' % frame.buffer)
                else:
                    self.writeline('%s.extend((' % frame.buffer)
                self.indent()
            for item in body:
                if isinstance(item, list):
                    val = repr(native_concat(item))
                    if frame.buffer is None:
                        self.writeline('yield ' + val)
                    else:
                        self.writeline(val + ',')
                else:
                    if frame.buffer is None:
                        self.writeline('yield ', item)
                    else:
                        self.newline(item)
                    close = 0
                    if finalize is not None:
                        self.write('environment.finalize(')
                        if finalize_context:
                            self.write('context, ')
                        close += 1
                    self.visit(item, frame)
                    if close > 0:
                        self.write(')' * close)
                    if frame.buffer is not None:
                        self.write(',')
            if frame.buffer is not None:
                # close the open parentheses
                self.outdent()
                self.writeline(len(body) == 1 and ')' or '))')
        # otherwise we create a format string as this is faster in that case
        else:
            format = []
            arguments = []
            for item in body:
                if isinstance(item, list):
                    format.append(native_concat(item).replace('%', '%%'))
                else:
                    format.append('%s')
                    arguments.append(item)
            self.writeline('yield ')
            self.write(repr(concat(format)) + ' % (')
            self.indent()
            for argument in arguments:
                self.newline(argument)
                close = 0
                if finalize is not None:
                    self.write('environment.finalize(')
                    if finalize_context:
                        self.write('context, ')
                    elif finalize_eval:
                        self.write('context.eval_ctx, ')
                    elif finalize_env:
                        self.write('environment, ')
                    close += 1
                self.visit(argument, frame)
                self.write(')' * close + ', ')
            self.outdent()
            self.writeline(')')
        if outdent_later:
            self.outdent()
class NativeTemplate(Template):
    def render(self, *args, **kwargs):
        """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.
        """
        vars = dict(*args, **kwargs)
        try:
            return native_concat(self.root_render_func(self.new_context(vars)))
        except Exception:
            exc_info = sys.exc_info()
        return self.environment.handle_exception(exc_info, True)
class NativeEnvironment(Environment):
    """An environment that renders templates to native Python types."""
    code_generator_class = NativeCodeGenerator
    template_class = NativeTemplate