Passed
Pull Request — master (#4415)
by Alexander
02:17
created

ssg.jinja.load_macros()   A

Complexity

Conditions 3

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.1825

Importance

Changes 0
Metric Value
cc 3
eloc 14
nop 1
dl 0
loc 19
ccs 8
cts 11
cp 0.7272
crap 3.1825
rs 9.7
c 0
b 0
f 0
1 2
from __future__ import absolute_import
2 2
from __future__ import print_function
3
4 2
import os.path
5 2
import jinja2
6
7 2
from .constants import (JINJA_MACROS_BASE_DEFINITIONS,
8
                        JINJA_MACROS_HIGHLEVEL_DEFINITIONS)
9 2
from .utils import required_key
10
11
12 2
class AbsolutePathFileSystemLoader(jinja2.BaseLoader):
13
    """Loads templates from the file system. This loader insists on absolute
14
    paths and fails if a relative path is provided.
15
16
    >>> loader = AbsolutePathFileSystemLoader()
17
18
    Per default the template encoding is ``'utf-8'`` which can be changed
19
    by setting the `encoding` parameter to something else.
20
    """
21
22 2
    def __init__(self, encoding='utf-8'):
23 2
        self.encoding = encoding
24
25 2
    def get_source(self, environment, template):
26 2
        if not os.path.isabs(template):
27
            raise jinja2.TemplateNotFound(template)
28
29 2
        template_file = jinja2.utils.open_if_exists(template)
30 2
        if template_file is None:
31
            raise jinja2.TemplateNotFound(template)
32 2
        try:
33 2
            contents = template_file.read().decode(self.encoding)
34
        finally:
35 2
            template_file.close()
36
37 2
        mtime = os.path.getmtime(template)
38
39 2
        def uptodate():
40 2
            try:
41 2
                return os.path.getmtime(template) == mtime
42
            except OSError:
43
                return False
44 2
        return contents, template, uptodate
45
46
47 2
def _get_jinja_environment(substitutions_dict):
48 2
    if _get_jinja_environment.env is None:
49 2
        bytecode_cache = None
50 2
        if substitutions_dict.get("jinja2_cache_enabled") == "true":
51
            bytecode_cache = jinja2.FileSystemBytecodeCache(
52
                required_key(substitutions_dict, "jinja2_cache_dir")
53
            )
54
55
        # TODO: Choose better syntax?
56 2
        _get_jinja_environment.env = jinja2.Environment(
57
            block_start_string="{{%",
58
            block_end_string="%}}",
59
            variable_start_string="{{{",
60
            variable_end_string="}}}",
61
            comment_start_string="{{#",
62
            comment_end_string="#}}",
63
            loader=AbsolutePathFileSystemLoader(),
64
            bytecode_cache=bytecode_cache
65
        )
66
67 2
    return _get_jinja_environment.env
68
69
70 2
_get_jinja_environment.env = None
71
72
73 2
def extract_substitutions_dict_from_template(filename, substitutions_dict):
74
    """
75
    Treat the given filename as a jinja2 file containing macro definitions,
76
    and export definitions that don't start with _ as a name->macro dictionary.
77
    During macro compilation, symbols from substitutions_dict may be used in those definitions.
78
    """
79 2
    template = _get_jinja_environment(substitutions_dict).get_template(filename)
80 2
    all_symbols = template.make_module(substitutions_dict).__dict__
81 2
    symbols_to_export = dict()
82 2
    for name, symbol in all_symbols.items():
83 2
        if name.startswith("_"):
84 2
            continue
85 2
        symbols_to_export[name] = symbol
86 2
    return symbols_to_export
87
88
89 2
def process_file(filepath, substitutions_dict):
90
    """
91
    Process the jinja file at the given path with the specified
92
    substitutions. Return the result as a string. Note that this will not
93
    load the project macros; use process_file_with_macros(...) for that.
94
    """
95 2
    filepath = os.path.abspath(filepath)
96 2
    template = _get_jinja_environment(substitutions_dict).get_template(filepath)
97 2
    return template.render(substitutions_dict)
98
99
100 2
def load_macros(substitutions_dict):
101
    """
102
    Augment the substitutions_dict dict with project Jinja macros in /shared/.
103
    """
104 2
    if substitutions_dict is None:
105 2
        substitutions_dict = dict()
106
107 2
    try:
108 2
        macro_definitions = extract_substitutions_dict_from_template(
109
            JINJA_MACROS_BASE_DEFINITIONS, substitutions_dict)
110 2
        macro_definitions.update(extract_substitutions_dict_from_template(
111
            JINJA_MACROS_HIGHLEVEL_DEFINITIONS, substitutions_dict))
112
    except Exception as exc:
113
        msg = ("Error extracting macro definitions: {0}"
114
               .format(str(exc)))
115
        raise RuntimeError(msg)
116
117 2
    substitutions_dict.update(macro_definitions)
118 2
    return substitutions_dict
119
120
121 2
def process_file_with_macros(filepath, substitutions_dict):
122
    """
123
    Process the file with jinja macros at the given path with the specified
124
    substitutions. Return the result as a string.
125
126
    See also: process_file
127
    """
128 2
    substitutions_dict = load_macros(substitutions_dict)
129
    return process_file(filepath, substitutions_dict)
130