Source code for ssg.jinja

from __future__ import absolute_import
from __future__ import print_function

import os.path
import jinja2

try:
    from urllib.parse import quote
except ImportError:
    from urllib import quote

try:
    from shlex import quote as shell_quote
except ImportError:
    from pipes import quote as shell_quote

from .constants import JINJA_MACROS_DIRECTORY
from .utils import (required_key,
                    product_to_name,
                    name_to_platform,
                    product_to_platform,
                    banner_regexify,
                    banner_anchor_wrap,
                    escape_id,
                    escape_regex,
                    escape_yaml_key,
                    sha256
                    )


[docs] class MacroError(RuntimeError): pass
[docs] class AbsolutePathFileSystemLoader(jinja2.BaseLoader): """Loads templates from the file system. This loader insists on absolute paths and fails if a relative path is provided. >>> loader = AbsolutePathFileSystemLoader() Per default the template encoding is ``'utf-8'`` which can be changed by setting the `encoding` parameter to something else. """ def __init__(self, encoding='utf-8'): self.encoding = encoding
[docs] def get_source(self, environment, template): if not os.path.isabs(template): raise jinja2.TemplateNotFound(template) template_file = jinja2.utils.open_if_exists(template) if template_file is None: raise jinja2.TemplateNotFound(template) try: contents = template_file.read().decode(self.encoding) except Exception as exc: msg = ("Error reading file {template}: {exc}" .format(template=template, exc=str(exc))) raise RuntimeError(msg) finally: template_file.close() mtime = os.path.getmtime(template) def uptodate(): try: return os.path.getmtime(template) == mtime except OSError: return False return contents, template, uptodate
def _get_jinja_environment(substitutions_dict): if _get_jinja_environment.env is None: bytecode_cache = None if substitutions_dict.get("jinja2_cache_enabled") == "true": bytecode_cache = jinja2.FileSystemBytecodeCache( required_key(substitutions_dict, "jinja2_cache_dir") ) # TODO: Choose better syntax? _get_jinja_environment.env = jinja2.Environment( block_start_string="{{%", block_end_string="%}}", variable_start_string="{{{", variable_end_string="}}}", comment_start_string="{{#", comment_end_string="#}}", loader=AbsolutePathFileSystemLoader(), bytecode_cache=bytecode_cache ) _get_jinja_environment.env.filters['banner_anchor_wrap'] = banner_anchor_wrap _get_jinja_environment.env.filters['banner_regexify'] = banner_regexify _get_jinja_environment.env.filters['escape_id'] = escape_id _get_jinja_environment.env.filters['escape_regex'] = escape_regex _get_jinja_environment.env.filters['escape_yaml_key'] = escape_yaml_key _get_jinja_environment.env.filters['quote'] = shell_quote _get_jinja_environment.env.filters['sha256'] = sha256 return _get_jinja_environment.env _get_jinja_environment.env = None
[docs] def raise_exception(message): raise MacroError(message)
[docs] def update_substitutions_dict(filename, substitutions_dict): """ Treat the given filename as a jinja2 file containing macro definitions, and export definitions that don't start with _ into the substitutions_dict, a name->macro dictionary. During macro compilation, symbols already existing in substitutions_dict may be used by those definitions. """ template = _get_jinja_environment(substitutions_dict).get_template(filename) all_symbols = template.make_module(substitutions_dict).__dict__ for name, symbol in all_symbols.items(): if name.startswith("_"): continue substitutions_dict[name] = symbol
[docs] def process_file(filepath, substitutions_dict): """ Process the jinja file at the given path with the specified substitutions. Return the result as a string. Note that this will not load the project macros; use process_file_with_macros(...) for that. """ filepath = os.path.abspath(filepath) template = _get_jinja_environment(substitutions_dict).get_template(filepath) return template.render(substitutions_dict)
[docs] def add_python_functions(substitutions_dict): substitutions_dict['product_to_name'] = product_to_name substitutions_dict['name_to_platform'] = name_to_platform substitutions_dict['product_to_platform'] = product_to_platform substitutions_dict['url_encode'] = url_encode substitutions_dict['raise'] = raise_exception substitutions_dict['expand_yaml_path'] = expand_yaml_path
[docs] def load_macros(substitutions_dict=None): """ Augment the substitutions_dict dict with project Jinja macros in /shared/. """ if substitutions_dict is None: substitutions_dict = dict() add_python_functions(substitutions_dict) try: for filename in sorted(os.listdir(JINJA_MACROS_DIRECTORY)): if filename.endswith(".jinja"): macros_file = os.path.join(JINJA_MACROS_DIRECTORY, filename) update_substitutions_dict(macros_file, substitutions_dict) except Exception as exc: msg = ("Error extracting macro definitions from '{1}': {0}" .format(str(exc), filename)) raise RuntimeError(msg) return substitutions_dict
[docs] def process_file_with_macros(filepath, substitutions_dict): """ Process the file with jinja macros at the given path with the specified substitutions. Return the result as a string. See also: process_file """ substitutions_dict = load_macros(substitutions_dict) assert 'indent' not in substitutions_dict return process_file(filepath, substitutions_dict)
[docs] def url_encode(source): return quote(source)
[docs] def expand_yaml_path(path, parameter): out = "" i = 0 for x in path.split("."): i += 1 if i != len(path.split(".")): out += i * " " + x + ":\n" elif parameter != "": out += i * " " + x + ":\n" i += 1 out += i * " " + parameter else: out += i * " " + x return out
[docs] def render_template(data, template_path, output_path, loader): env = _get_jinja_environment(dict()) env.loader = loader result = process_file(template_path, data) with open(output_path, "wb") as f: f.write(result.encode('utf8', 'replace'))