Test Failed
Push — master ( 08436c...bdc824 )
by Jan
01:36 queued 21s
created

ssg.jinja.AbsolutePathFileSystemLoader.__init__()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 2
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
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
try:
8 2
    from urllib.parse import quote
9
except ImportError:
10
    from urllib import quote
11
12
13 2
from .constants import JINJA_MACROS_DIRECTORY
14 2
from .utils import (required_key,
15
                    prodtype_to_name,
16
                    name_to_platform,
17
                    prodtype_to_platform,
18
                    banner_regexify,
19
                    banner_anchor_wrap,
20
                    escape_id,
21
                    escape_regex,
22
                    escape_yaml_key,
23
                    sha256
24
                    )
25
26
27 2
class MacroError(RuntimeError):
28 2
    pass
29
30
31 2
class AbsolutePathFileSystemLoader(jinja2.BaseLoader):
32
    """Loads templates from the file system. This loader insists on absolute
33
    paths and fails if a relative path is provided.
34
35
    >>> loader = AbsolutePathFileSystemLoader()
36
37
    Per default the template encoding is ``'utf-8'`` which can be changed
38
    by setting the `encoding` parameter to something else.
39
    """
40
41 2
    def __init__(self, encoding='utf-8'):
42 2
        self.encoding = encoding
43
44 2
    def get_source(self, environment, template):
45 2
        if not os.path.isabs(template):
46
            raise jinja2.TemplateNotFound(template)
47
48 2
        template_file = jinja2.utils.open_if_exists(template)
49 2
        if template_file is None:
50
            raise jinja2.TemplateNotFound(template)
51 2
        try:
52 2
            contents = template_file.read().decode(self.encoding)
53
        except Exception as exc:
54
            msg = ("Error reading file {template}: {exc}"
55
                   .format(template=template, exc=str(exc)))
56
            raise RuntimeError(msg)
57
        finally:
58 2
            template_file.close()
59
60 2
        mtime = os.path.getmtime(template)
61
62 2
        def uptodate():
63 2
            try:
64 2
                return os.path.getmtime(template) == mtime
65
            except OSError:
66
                return False
67 2
        return contents, template, uptodate
68
69
70 2
def _get_jinja_environment(substitutions_dict):
71 2
    if _get_jinja_environment.env is None:
72 2
        bytecode_cache = None
73 2
        if substitutions_dict.get("jinja2_cache_enabled") == "true":
74
            bytecode_cache = jinja2.FileSystemBytecodeCache(
75
                required_key(substitutions_dict, "jinja2_cache_dir")
76
            )
77
78
        # TODO: Choose better syntax?
79 2
        _get_jinja_environment.env = jinja2.Environment(
80
            block_start_string="{{%",
81
            block_end_string="%}}",
82
            variable_start_string="{{{",
83
            variable_end_string="}}}",
84
            comment_start_string="{{#",
85
            comment_end_string="#}}",
86
            loader=AbsolutePathFileSystemLoader(),
87
            bytecode_cache=bytecode_cache
88
        )
89 2
        _get_jinja_environment.env.filters['banner_regexify'] = banner_regexify
90 2
        _get_jinja_environment.env.filters['banner_anchor_wrap'] = banner_anchor_wrap
91 2
        _get_jinja_environment.env.filters['escape_regex'] = escape_regex
92 2
        _get_jinja_environment.env.filters['escape_id'] = escape_id
93 2
        _get_jinja_environment.env.filters['escape_yaml_key'] = escape_yaml_key
94 2
        _get_jinja_environment.env.filters['sha256'] = sha256
95
96 2
    return _get_jinja_environment.env
97
98
99 2
_get_jinja_environment.env = None
100
101
102 2
def raise_exception(message):
103
    raise MacroError(message)
104
105
106 2
def update_substitutions_dict(filename, substitutions_dict):
107
    """
108
    Treat the given filename as a jinja2 file containing macro definitions,
109
    and export definitions that don't start with _ into the substitutions_dict,
110
    a name->macro dictionary. During macro compilation, symbols already
111
    existing in substitutions_dict may be used by those definitions.
112
    """
113 2
    template = _get_jinja_environment(substitutions_dict).get_template(filename)
114 2
    all_symbols = template.make_module(substitutions_dict).__dict__
115 2
    for name, symbol in all_symbols.items():
116 2
        if name.startswith("_"):
117 2
            continue
118 2
        substitutions_dict[name] = symbol
119
120
121 2
def process_file(filepath, substitutions_dict):
122
    """
123
    Process the jinja file at the given path with the specified
124
    substitutions. Return the result as a string. Note that this will not
125
    load the project macros; use process_file_with_macros(...) for that.
126
    """
127 2
    filepath = os.path.abspath(filepath)
128 2
    template = _get_jinja_environment(substitutions_dict).get_template(filepath)
129 2
    return template.render(substitutions_dict)
130
131
132 2
def add_python_functions(substitutions_dict):
133 2
    substitutions_dict['prodtype_to_name'] = prodtype_to_name
134 2
    substitutions_dict['name_to_platform'] = name_to_platform
135 2
    substitutions_dict['prodtype_to_platform'] = prodtype_to_platform
136 2
    substitutions_dict['url_encode'] = url_encode
137 2
    substitutions_dict['raise'] = raise_exception
138 2
    substitutions_dict['expand_yaml_path'] = expand_yaml_path
139
140
141 2
def load_macros(substitutions_dict=None):
142
    """
143
    Augment the substitutions_dict dict with project Jinja macros in /shared/.
144
    """
145 2
    if substitutions_dict is None:
146 2
        substitutions_dict = dict()
147
148 2
    add_python_functions(substitutions_dict)
149 2
    try:
150 2
        for filename in os.listdir(JINJA_MACROS_DIRECTORY):
151 2
            if filename.endswith(".jinja"):
152 2
                macros_file = os.path.join(JINJA_MACROS_DIRECTORY, filename)
153 2
                update_substitutions_dict(macros_file, substitutions_dict)
154
    except Exception as exc:
155
        msg = ("Error extracting macro definitions from '{1}': {0}"
156
               .format(str(exc), filename))
0 ignored issues
show
introduced by
The variable filename does not seem to be defined in case the for loop on line 150 is not entered. Are you sure this can never be the case?
Loading history...
157
        raise RuntimeError(msg)
158
159 2
    return substitutions_dict
160
161
162 2
def process_file_with_macros(filepath, substitutions_dict):
163
    """
164
    Process the file with jinja macros at the given path with the specified
165
    substitutions. Return the result as a string.
166
167
    See also: process_file
168
    """
169 2
    substitutions_dict = load_macros(substitutions_dict)
170 2
    assert 'indent' not in substitutions_dict
171 2
    return process_file(filepath, substitutions_dict)
172
173
174 2
def url_encode(source):
175
    return quote(source)
176
177
178 2
def expand_yaml_path(path, parameter):
179
    out = ""
180
    i = 0
181
    for x in path.split("."):
182
        i += 1
183
        if i != len(path.split(".")):
184
            out += i * "  " + x + ":\n"
185
        elif parameter != "":
186
            out += i * "  " + x + ":\n"
187
            i += 1
188
            out += i * "  " + parameter
189
        else:
190
            out += i * "  " + x
191
    return out
192