Newer
Older
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
from xml.etree import ElementTree, ElementInclude
from copy import deepcopy
import os
def load_tool(path):
"""
Loads tool from file system and preprocesses tool macros.
"""
tree = raw_tool_xml_tree(path)
root = tree.getroot()
_import_macros(root, path)
# Expand xml macros
macro_dict = _macros_of_type(root, 'xml', lambda el: list(el))
_expand_macros([root], macro_dict)
# Expand tokens
macro_dict = _macros_of_type(root, 'token', lambda el: el.text)
_expand_tokens([root], macro_dict)
return tree
def template_macro_params(root):
"""
Look for template macros and populate param_dict (for cheetah)
with these.
"""
param_dict = {}
macro_dict = _macros_of_type(root, 'template', lambda el: el.text)
for key, value in macro_dict.iteritems():
param_dict[key] = value
return param_dict
def raw_tool_xml_tree(path):
""" Load raw (no macro expansion) tree representation of tool represented
at the specified path.
"""
tree = _parse_xml(path)
return tree
def imported_macro_paths(root):
macros_el = _macros_el(root)
return _imported_macro_paths_from_el(macros_el)
def _import_macros(root, path):
tool_dir = os.path.dirname(path)
macros_el = _macros_el(root)
if macros_el is not None:
macro_els = _load_macros(macros_el, tool_dir)
_xml_set_children(macros_el, macro_els)
def _macros_el(root):
return root.find('macros')
def _macros_of_type(root, type, el_func):
macros_el = root.find('macros')
macro_dict = {}
if macros_el is not None:
macro_els = macros_el.findall('macro')
macro_dict = dict([(macro_el.get("name"), el_func(macro_el)) \
for macro_el in macro_els \
if macro_el.get('type') == type])
return macro_dict
def _expand_tokens(elements, tokens):
if not tokens or not elements:
return
for element in elements:
value = element.text
if value:
new_value = _expand_tokens_str(element.text, tokens)
if not (new_value is value):
element.text = new_value
for key, value in element.attrib.iteritems():
new_value = _expand_tokens_str(value, tokens)
if not (new_value is value):
element.attrib[key] = new_value
_expand_tokens(list(element), tokens)
def _expand_tokens_str(str, tokens):
for key, value in tokens.iteritems():
if str.find(key) > -1:
str = str.replace(key, value)
return str
def _expand_macros(elements, macros):
if not macros:
return
for element in elements:
while True:
expand_el = element.find('.//expand')
if expand_el is None:
break
_expand_macro(element, expand_el, macros)
def _expand_macro(element, expand_el, macros):
macro_name = expand_el.get('macro')
macro_def = deepcopy(macros[macro_name])
_expand_yield_statements(macro_def, expand_el)
# Recursively expand contained macros.
_expand_macros(macro_def, macros)
# HACK for elementtree, newer implementations (etree/lxml) won't
# require this parent_map data structure but elementtree does not
# track parents or recongnize .find('..').
# TODO fix this now that we're not using elementtree
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
parent_map = dict((c, p) for p in element.getiterator() for c in p)
_xml_replace(expand_el, macro_def, parent_map)
def _expand_yield_statements(macro_def, expand_el):
yield_els = [yield_el for macro_def_el in macro_def for yield_el in macro_def_el.findall('.//yield')]
expand_el_children = list(expand_el)
macro_def_parent_map = \
dict((c, p) for macro_def_el in macro_def for p in macro_def_el.getiterator() for c in p)
for yield_el in yield_els:
_xml_replace(yield_el, expand_el_children, macro_def_parent_map)
def _load_macros(macros_el, tool_dir):
macros = []
# Import macros from external files.
macros.extend(_load_imported_macros(macros_el, tool_dir))
# Load all directly defined macros.
macros.extend(_load_embedded_macros(macros_el, tool_dir))
return macros
def _load_embedded_macros(macros_el, tool_dir):
macros = []
macro_els = []
# attribute typed macro
if macros_el is not None:
macro_els = macros_el.findall("macro")
for macro in macro_els:
if 'type' not in macro.attrib:
macro.attrib['type'] = 'xml'
macros.append(macro)
# type shortcuts (<xml> is a shortcut for <macro type="xml",
# likewise for <template>.
typed_tag = ['template', 'xml', 'token']
for tag in typed_tag:
macro_els = []
if macros_el is not None:
macro_els = macros_el.findall(tag)
for macro_el in macro_els:
macro_el.attrib['type'] = tag
macro_el.tag = 'macro'
macros.append(macro_el)
return macros
def _load_imported_macros(macros_el, tool_dir):
macros = []
for tool_relative_import_path in _imported_macro_paths_from_el(macros_el):
import_path = \
os.path.join(tool_dir, tool_relative_import_path)
file_macros = _load_macro_file(import_path, tool_dir)
macros.extend(file_macros)
return macros
def _imported_macro_paths_from_el(macros_el):
imported_macro_paths = []
macro_import_els = []
if macros_el is not None:
macro_import_els = macros_el.findall("import")
for macro_import_el in macro_import_els:
raw_import_path = macro_import_el.text
tool_relative_import_path = \
os.path.basename(raw_import_path) # Sanitize this
imported_macro_paths.append( tool_relative_import_path )
return imported_macro_paths
def _load_macro_file(path, tool_dir):
tree = _parse_xml(path)
root = tree.getroot()
return _load_macros(root, tool_dir)
def _xml_set_children(element, new_children):
for old_child in element:
element.remove(old_child)
for i, new_child in enumerate(new_children):
element.insert(i, new_child)
def _xml_replace(query, targets, parent_map):
#parent_el = query.find('..') ## Something like this would be better with newer xml library
parent_el = parent_map[query]
matching_index = -1
#for index, el in enumerate(parent_el.iter('.')): ## Something like this for newer implementation
for index, el in enumerate(list(parent_el)):
if el == query:
matching_index = index
break
assert matching_index >= 0
current_index = matching_index
for target in targets:
current_index += 1
parent_el.insert(current_index, deepcopy(target))
parent_el.remove(query)
def _parse_xml(fname):
tree = ElementTree.parse(fname)
root = tree.getroot()
ElementInclude.include(root)
return tree