-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathrendering.py
More file actions
146 lines (122 loc) · 5.33 KB
/
rendering.py
File metadata and controls
146 lines (122 loc) · 5.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from os import path
from shutil import copy2
import codecs
import fnmatch
import os
import re
import six
import stat
from jinja2 import Environment, StrictUndefined
import mrbob.plugins as plugins
jinja2_env = Environment(
block_start_string="{{%",
block_end_string="%}}",
variable_start_string="{{{",
variable_end_string="}}}",
trim_blocks=True,
undefined=StrictUndefined,
)
jinja2_renderer = lambda s, v: jinja2_env.from_string(s).render(parse_variables(v))
python_formatting_renderer = lambda s, v: s % v
DEFAULT_IGNORED = ['.mrbob.ini', '.DS_Store']
def parse_variables(variables):
d = dict()
for key, value in variables.items():
keys = key.split('.')
new_d = None
for k in keys[:-1]:
if new_d is None:
if k not in d:
d[k] = dict()
new_d = d[k]
else:
if k not in new_d:
new_d[k] = dict()
new_d = new_d[k]
if new_d is None:
d[keys[-1]] = value
else:
new_d[keys[-1]] = value
return dict(d)
def matches_any(filename, patterns):
result = any(fnmatch.fnmatch(filename, pat) for pat in patterns)
return result
def render_structure(fs_source_root, fs_target_root, variables, verbose,
renderer, ignored_files):
"""Recursively copies the given filesystem path `fs_source_root_ to a target directory `fs_target_root`.
Any files ending in `.bob` are rendered as templates using the given
renderer using the variables dictionary, thereby losing the `.bob` suffix.
strings wrapped in `+` signs in file- or directory names will be replaced
with values from the variables, i.e. a file named `+name+.py.bob` given a
dictionary {'name': 'bar'} would be rendered as `bar.py`.
"""
ignored_files.extend(DEFAULT_IGNORED)
if not isinstance(fs_source_root, six.text_type): # pragma: no cover
fs_source_root = six.u(fs_source_root)
for fs_source_dir, local_directories, local_files in os.walk(fs_source_root):
fs_target_dir = path.abspath(path.join(fs_target_root, path.relpath(fs_source_dir, fs_source_root)))
for local_file in local_files:
if matches_any(local_file, ignored_files):
continue
filename = render_filename(fs_target_dir, variables)
if filename is not None:
render_template(
path.join(fs_source_dir, local_file),
filename,
variables,
verbose,
renderer,
)
for local_directory in local_directories:
abs_dir = render_filename(path.join(fs_target_dir, local_directory), variables)
if abs_dir is not None:
if not path.exists(abs_dir):
if verbose:
print(six.u("mkdir %s") % abs_dir)
os.mkdir(abs_dir)
def render_template(fs_source, fs_target_dir, variables, verbose, renderer):
filename = render_filename(path.split(fs_source)[1], variables)
if filename is not None:
if filename.endswith('.bob'):
filename = filename.split('.bob')[0]
fs_target_path = path.join(fs_target_dir, filename)
if verbose:
print(six.u("Rendering %s to %s") % (fs_source, fs_target_path))
fs_source_mode = stat.S_IMODE(os.stat(fs_source).st_mode)
with codecs.open(fs_source, 'r', 'utf-8') as f:
source_output = f.read()
output = renderer(source_output, variables)
if source_output.endswith('\n') and not output.endswith('\n'):
output += '\n'
with codecs.open(fs_target_path, 'w', 'utf-8') as fs_target:
fs_target.write(output)
os.chmod(fs_target_path, fs_source_mode)
else:
fs_target_path = path.join(fs_target_dir, filename)
if verbose:
print(six.u("Copying %s to %s") % (fs_source, fs_target_path))
copy2(fs_source, fs_target_path)
return path.join(fs_target_dir, filename)
def render_filename(filename, variables):
"""Overridable (via entry_points) rendering.
Now plugguable, see :ref:`writing your plugin` to modify your replacements or other variables substitutions.
This is a useful option to generate templates or conditionnal rendering.
"""
plugin = plugins.PLUGINS.get('render_filename')
if plugin is not None:
if getattr(plugin, 'get_filename', None) is None:
raise AttributeError('get_filename method not found in plugin')
else:
plug_inst = plugin(filename, variables)
filename, will_continue = plug_inst.get_filename()
if filename is None or not will_continue:
return filename
variables_regex = re.compile(r"\+[^+%s]+\+" % re.escape(os.sep))
replaceables = variables_regex.findall(filename)
for replaceable in replaceables:
actual_replaceable = replaceable.replace('+', '')
if actual_replaceable in variables:
filename = filename.replace(replaceable, variables[actual_replaceable])
else:
raise KeyError('%s key part of filename %s was not found in variables %s' % (actual_replaceable, filename, variables))
return filename