Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/galaxy/util/xml_macros.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 import os | |
2 from copy import deepcopy | |
3 | |
4 from galaxy.util import parse_xml | |
5 | |
6 REQUIRED_PARAMETER = object() | |
7 | |
8 | |
9 def load_with_references(path): | |
10 """Load XML documentation from file system and preprocesses XML macros. | |
11 | |
12 Return the XML representation of the expanded tree and paths to | |
13 referenced files that were imported (macros). | |
14 """ | |
15 tree = raw_xml_tree(path) | |
16 root = tree.getroot() | |
17 | |
18 macro_paths = _import_macros(root, path) | |
19 | |
20 # Collect tokens | |
21 tokens = _macros_of_type(root, 'token', lambda el: el.text or '') | |
22 tokens = expand_nested_tokens(tokens) | |
23 | |
24 # Expand xml macros | |
25 macro_dict = _macros_of_type(root, 'xml', lambda el: XmlMacroDef(el)) | |
26 _expand_macros([root], macro_dict, tokens) | |
27 | |
28 return tree, macro_paths | |
29 | |
30 | |
31 def load(path): | |
32 tree, _ = load_with_references(path) | |
33 return tree | |
34 | |
35 | |
36 def template_macro_params(root): | |
37 """ | |
38 Look for template macros and populate param_dict (for cheetah) | |
39 with these. | |
40 """ | |
41 param_dict = {} | |
42 macro_dict = _macros_of_type(root, 'template', lambda el: el.text) | |
43 for key, value in macro_dict.items(): | |
44 param_dict[key] = value | |
45 return param_dict | |
46 | |
47 | |
48 def raw_xml_tree(path): | |
49 """ Load raw (no macro expansion) tree representation of XML represented | |
50 at the specified path. | |
51 """ | |
52 tree = parse_xml(path, strip_whitespace=False, remove_comments=True) | |
53 return tree | |
54 | |
55 | |
56 def imported_macro_paths(root): | |
57 macros_el = _macros_el(root) | |
58 return _imported_macro_paths_from_el(macros_el) | |
59 | |
60 | |
61 def _import_macros(root, path): | |
62 xml_base_dir = os.path.dirname(path) | |
63 macros_el = _macros_el(root) | |
64 if macros_el is not None: | |
65 macro_els, macro_paths = _load_macros(macros_el, xml_base_dir) | |
66 _xml_set_children(macros_el, macro_els) | |
67 return macro_paths | |
68 | |
69 | |
70 def _macros_el(root): | |
71 return root.find('macros') | |
72 | |
73 | |
74 def _macros_of_type(root, type, el_func): | |
75 macros_el = root.find('macros') | |
76 macro_dict = {} | |
77 if macros_el is not None: | |
78 macro_els = macros_el.findall('macro') | |
79 filtered_els = [(macro_el.get("name"), el_func(macro_el)) | |
80 for macro_el in macro_els | |
81 if macro_el.get('type') == type] | |
82 macro_dict = dict(filtered_els) | |
83 return macro_dict | |
84 | |
85 | |
86 def expand_nested_tokens(tokens): | |
87 for token_name in tokens.keys(): | |
88 for current_token_name, current_token_value in tokens.items(): | |
89 if token_name in current_token_value: | |
90 if token_name == current_token_name: | |
91 raise Exception("Token '%s' cannot contain itself" % token_name) | |
92 tokens[current_token_name] = current_token_value.replace(token_name, tokens[token_name]) | |
93 return tokens | |
94 | |
95 | |
96 def _expand_tokens(elements, tokens): | |
97 if not tokens or elements is None: | |
98 return | |
99 | |
100 for element in elements: | |
101 _expand_tokens_for_el(element, tokens) | |
102 | |
103 | |
104 def _expand_tokens_for_el(element, tokens): | |
105 value = element.text | |
106 if value: | |
107 new_value = _expand_tokens_str(element.text, tokens) | |
108 if not (new_value is value): | |
109 element.text = new_value | |
110 for key, value in element.attrib.items(): | |
111 new_value = _expand_tokens_str(value, tokens) | |
112 if not (new_value is value): | |
113 element.attrib[key] = new_value | |
114 _expand_tokens(list(element), tokens) | |
115 | |
116 | |
117 def _expand_tokens_str(s, tokens): | |
118 for key, value in tokens.items(): | |
119 if key in s: | |
120 s = s.replace(key, value) | |
121 return s | |
122 | |
123 | |
124 def _expand_macros(elements, macros, tokens): | |
125 if not macros and not tokens: | |
126 return | |
127 | |
128 for element in elements: | |
129 while True: | |
130 expand_el = element.find('.//expand') | |
131 if expand_el is None: | |
132 break | |
133 _expand_macro(element, expand_el, macros, tokens) | |
134 | |
135 _expand_tokens_for_el(element, tokens) | |
136 | |
137 | |
138 def _expand_macro(element, expand_el, macros, tokens): | |
139 macro_name = expand_el.get('macro') | |
140 macro_def = macros[macro_name] | |
141 expanded_elements = deepcopy(macro_def.elements) | |
142 | |
143 _expand_yield_statements(expanded_elements, expand_el) | |
144 | |
145 # Recursively expand contained macros. | |
146 _expand_macros(expanded_elements, macros, tokens) | |
147 macro_tokens = macro_def.macro_tokens(expand_el) | |
148 if macro_tokens: | |
149 _expand_tokens(expanded_elements, macro_tokens) | |
150 | |
151 # HACK for elementtree, newer implementations (etree/lxml) won't | |
152 # require this parent_map data structure but elementtree does not | |
153 # track parents or recognize .find('..'). | |
154 # TODO fix this now that we're not using elementtree | |
155 parent_map = dict((c, p) for p in element.iter() for c in p) | |
156 _xml_replace(expand_el, expanded_elements, parent_map) | |
157 | |
158 | |
159 def _expand_yield_statements(macro_def, expand_el): | |
160 yield_els = [yield_el for macro_def_el in macro_def for yield_el in macro_def_el.findall('.//yield')] | |
161 | |
162 expand_el_children = list(expand_el) | |
163 macro_def_parent_map = \ | |
164 dict((c, p) for macro_def_el in macro_def for p in macro_def_el.iter() for c in p) | |
165 | |
166 for yield_el in yield_els: | |
167 _xml_replace(yield_el, expand_el_children, macro_def_parent_map) | |
168 | |
169 # Replace yields at the top level of a macro, seems hacky approach | |
170 replace_yield = True | |
171 while replace_yield: | |
172 for i, macro_def_el in enumerate(macro_def): | |
173 if macro_def_el.tag == "yield": | |
174 for target in expand_el_children: | |
175 i += 1 | |
176 macro_def.insert(i, target) | |
177 macro_def.remove(macro_def_el) | |
178 continue | |
179 | |
180 replace_yield = False | |
181 | |
182 | |
183 def _load_macros(macros_el, xml_base_dir): | |
184 macros = [] | |
185 # Import macros from external files. | |
186 imported_macros, macro_paths = _load_imported_macros(macros_el, xml_base_dir) | |
187 macros.extend(imported_macros) | |
188 # Load all directly defined macros. | |
189 macros.extend(_load_embedded_macros(macros_el, xml_base_dir)) | |
190 return macros, macro_paths | |
191 | |
192 | |
193 def _load_embedded_macros(macros_el, xml_base_dir): | |
194 macros = [] | |
195 | |
196 macro_els = [] | |
197 # attribute typed macro | |
198 if macros_el is not None: | |
199 macro_els = macros_el.findall("macro") | |
200 for macro in macro_els: | |
201 if 'type' not in macro.attrib: | |
202 macro.attrib['type'] = 'xml' | |
203 macros.append(macro) | |
204 | |
205 # type shortcuts (<xml> is a shortcut for <macro type="xml", | |
206 # likewise for <template>. | |
207 typed_tag = ['template', 'xml', 'token'] | |
208 for tag in typed_tag: | |
209 macro_els = [] | |
210 if macros_el is not None: | |
211 macro_els = macros_el.findall(tag) | |
212 for macro_el in macro_els: | |
213 macro_el.attrib['type'] = tag | |
214 macro_el.tag = 'macro' | |
215 macros.append(macro_el) | |
216 | |
217 return macros | |
218 | |
219 | |
220 def _load_imported_macros(macros_el, xml_base_dir): | |
221 macros = [] | |
222 macro_paths = [] | |
223 | |
224 for tool_relative_import_path in _imported_macro_paths_from_el(macros_el): | |
225 import_path = \ | |
226 os.path.join(xml_base_dir, tool_relative_import_path) | |
227 macro_paths.append(import_path) | |
228 file_macros, current_macro_paths = _load_macro_file(import_path, xml_base_dir) | |
229 macros.extend(file_macros) | |
230 macro_paths.extend(current_macro_paths) | |
231 | |
232 return macros, macro_paths | |
233 | |
234 | |
235 def _imported_macro_paths_from_el(macros_el): | |
236 imported_macro_paths = [] | |
237 macro_import_els = [] | |
238 if macros_el is not None: | |
239 macro_import_els = macros_el.findall("import") | |
240 for macro_import_el in macro_import_els: | |
241 raw_import_path = macro_import_el.text | |
242 imported_macro_paths.append(raw_import_path) | |
243 return imported_macro_paths | |
244 | |
245 | |
246 def _load_macro_file(path, xml_base_dir): | |
247 tree = parse_xml(path, strip_whitespace=False) | |
248 root = tree.getroot() | |
249 return _load_macros(root, xml_base_dir) | |
250 | |
251 | |
252 def _xml_set_children(element, new_children): | |
253 for old_child in element: | |
254 element.remove(old_child) | |
255 for i, new_child in enumerate(new_children): | |
256 element.insert(i, new_child) | |
257 | |
258 | |
259 def _xml_replace(query, targets, parent_map): | |
260 # parent_el = query.find('..') ## Something like this would be better with newer xml library | |
261 parent_el = parent_map[query] | |
262 matching_index = -1 | |
263 # for index, el in enumerate(parent_el.iter('.')): ## Something like this for newer implementation | |
264 for index, el in enumerate(list(parent_el)): | |
265 if el == query: | |
266 matching_index = index | |
267 break | |
268 assert matching_index >= 0 | |
269 current_index = matching_index | |
270 for target in targets: | |
271 current_index += 1 | |
272 parent_el.insert(current_index, deepcopy(target)) | |
273 parent_el.remove(query) | |
274 | |
275 | |
276 class XmlMacroDef(object): | |
277 | |
278 def __init__(self, el): | |
279 self.elements = list(el) | |
280 parameters = {} | |
281 tokens = [] | |
282 token_quote = "@" | |
283 for key, value in el.attrib.items(): | |
284 if key == "token_quote": | |
285 token_quote = value | |
286 if key == "tokens": | |
287 for token in value.split(","): | |
288 tokens.append((token, REQUIRED_PARAMETER)) | |
289 elif key.startswith("token_"): | |
290 token = key[len("token_"):] | |
291 tokens.append((token, value)) | |
292 for name, default in tokens: | |
293 parameters[name] = (token_quote, default) | |
294 self.parameters = parameters | |
295 | |
296 def macro_tokens(self, expand_el): | |
297 tokens = {} | |
298 for key, (wrap_char, default_val) in self.parameters.items(): | |
299 token_value = expand_el.attrib.get(key, default_val) | |
300 if token_value is REQUIRED_PARAMETER: | |
301 message = "Failed to expand macro - missing required parameter [%s]." | |
302 raise ValueError(message % key) | |
303 token_name = "%s%s%s" % (wrap_char, key.upper(), wrap_char) | |
304 tokens[token_name] = token_value | |
305 return tokens | |
306 | |
307 | |
308 __all__ = ( | |
309 "imported_macro_paths", | |
310 "load", | |
311 "load_with_references", | |
312 "raw_xml_tree", | |
313 "template_macro_params", | |
314 ) |