From: noah morrison Date: Sat, 22 Nov 2014 18:33:20 +0000 (-0500) Subject: Move render into it's own file X-Git-Url: https://git.devinivas.org/?a=commitdiff_plain;h=cb6279f4ed711e08154d5b1f14119c80a8ea1b9b;p=chevron.git Move render into it's own file --- diff --git a/chevron/chevron.py b/chevron/chevron.py index 5a41883..3862334 100755 --- a/chevron/chevron.py +++ b/chevron/chevron.py @@ -5,233 +5,7 @@ import json from sys import argv from chevron.tokenizer import tokenize - - -try: # python 2 - unicode - python3 = False -except: - def unicode(x, y): - return x - python3 = True - - -def render(template='', data={}, partials_path='.', partials_ext='mustache', - partials_dict={}, padding=0, def_ldel='{{', def_rdel='}}', - scopes=None): - """Render a mustache template. - - Renders a mustache template with a data scope and partial capability. - Given the file structure... - . - |- main.py - |- main.ms - |- partials - |- part.ms - - then main.py would make the following call: - - render(open('main.ms', 'r'), {...}, 'partials', 'ms') - - Arguments: - template -- A file-like object or a string containing the template - data -- A python dictionary with your data scope - partials_path -- The path to where your partials are stored - (defaults to '.') - partials_ext -- The extension that you want the parser to look for - (defaults to 'mustache') - partials_dict -- A python dictionary which will be search for partials - before the filesystem is. {'include': 'foo'} is the same - as a file called include.mustache - (defaults to {}) - padding -- This is for padding partials, and shouldn't be used - (but can be if you really want to) - - Returns: - A string containing the rendered template. - """ - - def html_escape(string): - """HTML escape all of these " & < >""" - - html_codes = { - '"': '"', - '<': '<', - '>': '>', - } - - # & must be handled first - string = string.replace('&', '&') - for char in html_codes: - string = string.replace(char, html_codes[char]) - return string - - def get_key(key): - """Get a key from the current scope""" - - # If the key is a dot - if key == '.': - # Then just return the current scope - return scopes[0] - - # Loop through the scopes - for scope in scopes: - try: - # For every dot seperated key - for key in key.split('.'): - # Move into the scope - scope = scope[key] - # Return the last scope we got - return scope - except (TypeError, KeyError): - # We couldn't find the key in the current scope - # We'll try again on the next pass - pass - - # We couldn't find the key in any of the scopes - return '' - - def get_partial(name): - """Load a partial""" - try: - # Maybe the partial is in the dictionary - return partials_dict[name] - except KeyError: - # Nope... - try: - # Maybe it's in the file system - path = partials_path + '/' + name + '.' + partials_ext - with open(path, 'r') as partial: - return partial.read() - - except IOError: - # Alright I give up on you - return '' - - # If the template is a list - if type(template) is list: - # Then we don't need to tokenize it - tokens = template - else: - # Otherwise make a generator - tokens = tokenize(template, def_ldel, def_rdel) - - output = unicode('', 'utf-8') - - if scopes is None: - scopes = [data] - - # Run through the tokens - for tag, key in tokens: - # Set the current scope - current_scope = scopes[0] - - # If we're an end tag - if tag == 'end': - # Pop out of the latest scope - scopes = scopes[1:] - - # If the current scope is falsy and not the only scope - elif not current_scope and len(scopes) != 1: - # If we're a section tag - if tag == 'section': - # Set it as the most recent scope - scopes.insert(0, scope) - - # If we're an inverted section tag - elif tag == 'inverted section': - # Set the flipped scope as the most recent scope - scopes.insert(0, not scope) - - # If we're a literal tag - elif tag == 'literal': - # Add padding to the key and add it to the output - if type(key) != unicode: - key = unicode(key, 'utf-8') - output += key.replace('\n', '\n' + (' ' * padding)) - - # If we're a variable tag - elif tag == 'variable': - # Add the html escaped key to the output - thing = get_key(key) - if type(thing) != unicode: - thing = unicode(str(thing), 'utf-8') - output += html_escape(thing) - - # If we're a no html escape tag - elif tag == 'no escape': - # Just lookup the key and add it - output += str(get_key(key)) - - # If we're a section tag - elif tag == 'section': - # Get the sections scope - scope = get_key(key) - - # If the scope is a list - if type(scope) is list: - # Then we need to do some looping - - # Gather up all the tags inside the section - tags = [] - for tag in tokens: - if tag == ('end', key): - break - tags.append(tag) - - # For every item in the scope - for thing in scope: - # Append it as the most recent scope and render - new_scope = [thing] + scopes - output += render(template=tags, scopes=new_scope, - partials_path=partials_path, - partials_ext=partials_ext, - partials_dict=partials_dict, - def_ldel=def_ldel, def_rdel=def_rdel) - - else: - # Otherwise we're just a scope section - scopes.insert(0, scope) - - # If we're an inverted section - elif tag == 'inverted section': - # Add the flipped scope to the scopes - scope = get_key(key) - scopes.insert(0, not scope) - - # If we're a partial - elif tag == 'partial': - # Load the partial - partial = get_partial(key) - - # Find how much to pad the partial - left = output.split('\n')[-1] - part_padding = padding - if left.isspace(): - part_padding += left.count(' ') - - # Render the partial - part_out = render(template=partial, partials_path=partials_path, - partials_ext=partials_ext, - partials_dict=partials_dict, - def_ldel=def_ldel, def_rdel=def_rdel, - padding=part_padding, scopes=scopes) - - # If the partial was indented - if left.isspace(): - # then remove the spaces from the end - part_out = part_out.rstrip(' ') - - # Add the partials output to the ouput - if python3: - output += part_out - else: # python 2 - output += part_out.decode('utf-8') - - if python3: - return output - else: # python 2 - return output.encode('utf-8') +from chevron.renderer import render def main(template, data={}, **kwargs): diff --git a/chevron/renderer.py b/chevron/renderer.py new file mode 100644 index 0000000..f26b36d --- /dev/null +++ b/chevron/renderer.py @@ -0,0 +1,229 @@ +#!/usr/bin/python + +from chevron.tokenizer import tokenize + +try: # python 2 + unicode + python3 = False +except: + def unicode(x, y): + return x + python3 = True + + +def render(template='', data={}, partials_path='.', partials_ext='mustache', + partials_dict={}, padding=0, def_ldel='{{', def_rdel='}}', + scopes=None): + """Render a mustache template. + + Renders a mustache template with a data scope and partial capability. + Given the file structure... + . + |- main.py + |- main.ms + |- partials + |- part.ms + + then main.py would make the following call: + + render(open('main.ms', 'r'), {...}, 'partials', 'ms') + + Arguments: + template -- A file-like object or a string containing the template + data -- A python dictionary with your data scope + partials_path -- The path to where your partials are stored + (defaults to '.') + partials_ext -- The extension that you want the parser to look for + (defaults to 'mustache') + partials_dict -- A python dictionary which will be search for partials + before the filesystem is. {'include': 'foo'} is the same + as a file called include.mustache + (defaults to {}) + padding -- This is for padding partials, and shouldn't be used + (but can be if you really want to) + + Returns: + A string containing the rendered template. + """ + + def html_escape(string): + """HTML escape all of these " & < >""" + + html_codes = { + '"': '"', + '<': '<', + '>': '>', + } + + # & must be handled first + string = string.replace('&', '&') + for char in html_codes: + string = string.replace(char, html_codes[char]) + return string + + def get_key(key): + """Get a key from the current scope""" + + # If the key is a dot + if key == '.': + # Then just return the current scope + return scopes[0] + + # Loop through the scopes + for scope in scopes: + try: + # For every dot seperated key + for key in key.split('.'): + # Move into the scope + scope = scope[key] + # Return the last scope we got + return scope + except (TypeError, KeyError): + # We couldn't find the key in the current scope + # We'll try again on the next pass + pass + + # We couldn't find the key in any of the scopes + return '' + + def get_partial(name): + """Load a partial""" + try: + # Maybe the partial is in the dictionary + return partials_dict[name] + except KeyError: + # Nope... + try: + # Maybe it's in the file system + path = partials_path + '/' + name + '.' + partials_ext + with open(path, 'r') as partial: + return partial.read() + + except IOError: + # Alright I give up on you + return '' + + # If the template is a list + if type(template) is list: + # Then we don't need to tokenize it + tokens = template + else: + # Otherwise make a generator + tokens = tokenize(template, def_ldel, def_rdel) + + output = unicode('', 'utf-8') + + if scopes is None: + scopes = [data] + + # Run through the tokens + for tag, key in tokens: + # Set the current scope + current_scope = scopes[0] + + # If we're an end tag + if tag == 'end': + # Pop out of the latest scope + scopes = scopes[1:] + + # If the current scope is falsy and not the only scope + elif not current_scope and len(scopes) != 1: + # If we're a section tag + if tag == 'section': + # Set it as the most recent scope + scopes.insert(0, scope) + + # If we're an inverted section tag + elif tag == 'inverted section': + # Set the flipped scope as the most recent scope + scopes.insert(0, not scope) + + # If we're a literal tag + elif tag == 'literal': + # Add padding to the key and add it to the output + if type(key) != unicode: + key = unicode(key, 'utf-8') + output += key.replace('\n', '\n' + (' ' * padding)) + + # If we're a variable tag + elif tag == 'variable': + # Add the html escaped key to the output + thing = get_key(key) + if type(thing) != unicode: + thing = unicode(str(thing), 'utf-8') + output += html_escape(thing) + + # If we're a no html escape tag + elif tag == 'no escape': + # Just lookup the key and add it + output += str(get_key(key)) + + # If we're a section tag + elif tag == 'section': + # Get the sections scope + scope = get_key(key) + + # If the scope is a list + if type(scope) is list: + # Then we need to do some looping + + # Gather up all the tags inside the section + tags = [] + for tag in tokens: + if tag == ('end', key): + break + tags.append(tag) + + # For every item in the scope + for thing in scope: + # Append it as the most recent scope and render + new_scope = [thing] + scopes + output += render(template=tags, scopes=new_scope, + partials_path=partials_path, + partials_ext=partials_ext, + partials_dict=partials_dict, + def_ldel=def_ldel, def_rdel=def_rdel) + + else: + # Otherwise we're just a scope section + scopes.insert(0, scope) + + # If we're an inverted section + elif tag == 'inverted section': + # Add the flipped scope to the scopes + scope = get_key(key) + scopes.insert(0, not scope) + + # If we're a partial + elif tag == 'partial': + # Load the partial + partial = get_partial(key) + + # Find how much to pad the partial + left = output.split('\n')[-1] + part_padding = padding + if left.isspace(): + part_padding += left.count(' ') + + # Render the partial + part_out = render(template=partial, partials_path=partials_path, + partials_ext=partials_ext, + partials_dict=partials_dict, + def_ldel=def_ldel, def_rdel=def_rdel, + padding=part_padding, scopes=scopes) + + # If the partial was indented + if left.isspace(): + # then remove the spaces from the end + part_out = part_out.rstrip(' ') + + # Add the partials output to the ouput + if python3: + output += part_out + else: # python 2 + output += part_out.decode('utf-8') + + if python3: + return output + else: # python 2 + return output.encode('utf-8')