def template(relative_path, *args, **kwargs):
    """ A decorator for easily rendering templates.
    Use as follows:

    main.py: 
        from mustache import (template)

        @template('../tests/static/say_hello.html')
        def index():
            context = {'name' : 'world'}
            partials = {}
            return context, partials

        if __name__=="__main__":
            print index()
    
    static/say_hello.html:

        <h1> Hello, {{name}}! </h1>

    from the command line:
        
        > python main.py
        <h1> Hello, world! </h1>
    """
    directory, filename = os.path.split(relative_path)
    
    partials_dir = os.path.abspath(directory)
    name, ext = os.path.splitext(filename)

    state = State(partials_dir=directory, extension=ext, *args, **kwargs)
    template = load_template(name, directory, ext, state.encoding, state.encoding_error)

    def wrapper(fn):
        def render_template(*args, **kwargs):
            res = fn(*args, **kwargs)
            if isinstance(res, tuple):
                if len(res) == 2:
                    (new_context, partials) = res
                elif len(res) == 1:
                    (new_context, partials) = (res[0], {})
            elif isinstance(res, dict):
                (new_context, partials) = (res, {})
            else:
                (new_context, partials) = ({}, {})
            context = copy(template_globals)
            context.update(new_context)
            return render(template, context, partials, state)
        return render_template
    return wrapper
示例#2
0
def template(relative_path, *args, **kwargs):
    """ A decorator for easily rendering templates.
    Use as follows:

    main.py:
        from mustache import (template)

        @template('../tests/static/say_hello.html')
        def index():
            context = {'name' : 'world'}
            partials = {}
            return context, partials

        if __name__=="__main__":
            print index()

    static/say_hello.html:

        <h1> Hello, {{name}}! </h1>

    from the command line:

        > python main.py
        <h1> Hello, world! </h1>
    """
    directory, filename = os.path.split(relative_path)

    partials_dir = os.path.abspath(directory)
    name, ext = os.path.splitext(filename)

    state = State(partials_dir=directory, extension=ext, *args, **kwargs)
    template = load_template(name, directory, ext, state.encoding, state.encoding_error)

    def wrapper(fn):
        def render_template(*args, **kwargs):
            res = fn(*args, **kwargs)
            if isinstance(res, tuple):
                if len(res) == 2:
                    (new_context, partials) = res
                elif len(res) == 1:
                    (new_context, partials) = (res[0], {})
            elif isinstance(res, dict):
                (new_context, partials) = (res, {})
            else:
                (new_context, partials) = ({}, {})
            context = copy(template_globals)
            context.update(new_context)
            return render(template, context, partials, state)
        return render_template
    return wrapper
示例#3
0
def __render(template, state, index=0):
    """
    Given a /template/ string, a parser /state/, and a starting
    offset (/index/), return the rendered version of the template.
    """
    # Find a Match
    match = state.tag_re.search(template, index)
    if not match:
        return template[index:]

    info = get_match_info(template, match, state)
    _pre = template[index : info['tag_start']] # template before the tag
    _tag = template[info['tag_start'] : info['tag_end']] # tag
    _continue = info['tag_end'] # the index at which to continue

    # Comment
    if info['tag_type'] == '!':
        # Comments are removed from output
        repl = ""

    # Delimiter change
    elif info['tag_type'] == '=':
        # Delimiters are changed; the tag is rendered as ""
        delimiters = re.split(r'\s*', info['tag_key'])
        new_tags = state.tags(_copy=True)
        new_tags['otag'], new_tags['ctag'] = map(re.escape, delimiters)
        state.push_tags(new_tags)
        repl = ""

    # Plain tag
    elif info['tag_type'] == '':
        repl = __render_tag(info, state)

    # Raw tag (should not be escaped)
    elif info['tag_type'] == '&':
        state.escape.push(False)
        repl = __render_tag(info, state)
        state.escape.pop()

    # Partial
    elif info['tag_type'] == '>':
        partial_name = info['tag_key']
        partial_template = None
        new_dir = None
        lead_wsp = re.compile(r'^(.)', re.M)
        repl = ''
        try:
            # Cached
            partial_template = state.partials()[partial_name]
        except (KeyError, IndexError):
            try:
                # Load the partial template from a file (if it exists)
                new_dir, filename = split(partial_name)
                if new_dir:
                    state.partials_dir.push(new_dir)

                partial_template = load_template(filename, state.abs_partials_dir, state.extension,
                                                 state.encoding, state.encoding_error)
            except (IOError):
                pass

        if partial_template:
            # Preserve indentation
            if info['standalone']:
                partial_template = lead_wsp.sub(info['lead_wsp']+r'\1', partial_template)

            # Update state
            state.partials.push(state.partials()) # XXX wtf is this shit?
            state.push_tags(state.default_tags)

            # Render the partial
            repl = __render(partial_template, state)

            # Restore state
            state.partials.pop()
            state.pop_tags()
            if new_dir:
                state.partials_dir.pop()

    # Section

    # TODO(peter): add a stop= index to __render so that template_to_inner does
    # not need to be constructed with [:] indexing, which is extremely
    # expensive.
    elif info['tag_type'] in ('#', '^'):
        otag_info = info
        ctag_info = section_end_info(template, info['tag_key'], state, _continue)

        # Don't want to parse beyond the end of the inner section, but
        # must include information on prior contents so that whitespace
        # is preserved correctly and inner tags are not marked as standalone.
        inner_start = otag_info['tag_end']
        inner_end = ctag_info['tag_start']
        _continue = ctag_info['tag_end']

        template_with_inner = template[:inner_end]

        new_contexts, ctm = get_tag_context(otag_info['tag_key'], state)
        truthy = otag_info['tag_type'] == '#'

        #if ctm is not None:
        if ctm:
            # If there's a match and it's callable, feed it the inner template
            if callable(ctm):
                template_to_inner = template[:inner_start]
                inner = template[inner_start:inner_end]
                template_with_inner = template_to_inner + make_unicode(ctm(inner))

            # Make the context list an iterable from the ctm
            if not hasattr(ctm, '__iter__') or isinstance(ctm, dict):
                ctx_list = [ctm]
            else:
                ctx_list = ctm
        # If there's no match, there are no new contexts
        else:
            ctx_list = [False]

        # If there are new contexts and the section is truthy, or if
        # there are no new contexts and the section is falsy, render
        # the contents
        repl_stack = []
        for ctx in ctx_list:
            if (truthy and ctx) or (not truthy and not ctx):
                state.context.push(ctx)
                repl_stack.append(
                    __render(template_with_inner, state, inner_start))

            else:
                break

        repl = ''.join(repl_stack)
        for i in xrange(new_contexts): state.context.pop()
    else:
        raise Exception("found unpaired end of section tag!")

    return u''.join((
        _pre, make_unicode(repl), __render(template, state, _continue)))