Passed
Pull Request — master (#4595)
by Alexander
01:47
created

AbsolutePathFileSystemLoader.get_source()   A

Complexity

Conditions 4

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4.25

Importance

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