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