Repository URL to install this package:
|
Version:
1.26.0.dev0+gite506aa5f ▾
|
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
import os
import pkgutil
import pystache
class MustacheRenderer:
"""Renders text using mustache templates."""
class MustacheError(Exception):
"""Indicates failure rendering mustache templates."""
@staticmethod
def expand(args):
# Add foo? for each foo in the map that evaluates to true.
# Mustache needs this, especially in cases where foo is a list: there is no way to render a
# block exactly once iff a list is not empty.
# Note: if the original map contains foo?, it will take precedence over our synthetic foo?.
def convert_val(x):
# Pystache can't handle sets, so we convert to maps of key->True.
if isinstance(x, set):
return dict([(k, True) for k in x])
elif isinstance(x, dict):
return MustacheRenderer.expand(x)
elif isinstance(x, list):
return [convert_val(e) for e in x]
else:
return x
items = [(key, convert_val(val)) for (key, val) in args.items()]
ret = dict([(key + "?", True) for (key, val) in items if val and not key.endswith("?")])
ret.update(dict(items))
return ret
@staticmethod
def parse_template(template_text):
template = pystache.parse(template_text)
return template
def __init__(self, template_dir=None, package_name=None):
"""Create a renderer that finds templates by name in one of two ways.
* If template_dir is specified, finds template foo in the file foo.mustache in that dir.
* Otherwise, if package_name is specified, finds template foo embedded in that
package under templates/foo.mustache.
* Otherwise will not find templates by name, so can only be used with an existing
template string.
"""
self._template_dir = template_dir
self._package_name = package_name
if self._package_name is not None:
class PartialsLoader:
def get(me, template_name):
return self._get_template_text_from_package(template_name)
partials = PartialsLoader()
else:
partials = None
self._pystache_renderer = pystache.Renderer(search_dirs=template_dir, partials=partials)
self._templates = {}
def render_name(self, template_name, args):
parsed_template = self._load_template(template_name)
return self.render(parsed_template, args)
def render(self, template, args):
return self._pystache_renderer.render(template, MustacheRenderer.expand(args))
def _load_template(self, template_name):
template = self._templates.get(template_name)
if not template:
if self._template_dir:
# Let pystache find the template by name.
template = self._pystache_renderer.load_template(template_name)
else:
template_text = self._get_template_text_from_package(template_name)
template = MustacheRenderer.parse_template(template_text)
self._templates[template_name] = template
return template
def _get_template_text_from_package(self, template_name):
"""Load the named template embedded in our package."""
if self._package_name is None:
raise self.MustacheError("No package specified for template loading.")
path = os.path.join("templates", template_name + ".mustache")
template_text = pkgutil.get_data(self._package_name, path)
if template_text is None:
raise self.MustacheError(
"could not find template {} in package {}".format(path, self._package_name)
)
return template_text.decode("utf8")