comparison planemo/lib/python3.7/site-packages/galaxy/tool_util/parser/interface.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 abc import (
3 ABCMeta,
4 abstractmethod
5 )
6
7 import six
8
9 from .util import _parse_name
10
11 NOT_IMPLEMENTED_MESSAGE = "Galaxy tool format does not yet support this tool feature."
12
13
14 @six.python_2_unicode_compatible
15 @six.add_metaclass(ABCMeta)
16 class ToolSource(object):
17 """ This interface represents an abstract source to parse tool
18 information from.
19 """
20 default_is_multi_byte = False
21
22 @abstractmethod
23 def parse_id(self):
24 """ Parse an ID describing the abstract tool. This is not the
25 GUID tracked by the tool shed but the simple id (there may be
26 multiple tools loaded in Galaxy with this same simple id).
27 """
28
29 @abstractmethod
30 def parse_version(self):
31 """ Parse a version describing the abstract tool.
32 """
33
34 def parse_tool_module(self):
35 """ Load Tool class from a custom module. (Optional).
36
37 If not None, return pair containing module and class (as strings).
38 """
39 return None
40
41 def parse_action_module(self):
42 """ Load Tool class from a custom module. (Optional).
43
44 If not None, return pair containing module and class (as strings).
45 """
46 return None
47
48 def parse_tool_type(self):
49 """ Load simple tool type string (e.g. 'data_source', 'default').
50 """
51 return None
52
53 @abstractmethod
54 def parse_name(self):
55 """ Parse a short name for tool (required). """
56
57 @abstractmethod
58 def parse_description(self):
59 """ Parse a description for tool. Longer than name, shorted than help. """
60
61 def parse_is_multi_byte(self):
62 """ Parse is_multi_byte from tool - TODO: figure out what this is and
63 document.
64 """
65 return self.default_is_multi_byte
66
67 def parse_display_interface(self, default):
68 """ Parse display_interface - fallback to default for the tool type
69 (supplied as default parameter) if not specified.
70 """
71 return default
72
73 def parse_require_login(self, default):
74 """ Parse whether the tool requires login (as a bool).
75 """
76 return default
77
78 def parse_request_param_translation_elem(self):
79 """ Return an XML element describing require parameter translation.
80
81 If we wish to support this feature for non-XML based tools this should
82 be converted to return some sort of object interface instead of a RAW
83 XML element.
84 """
85 return None
86
87 @abstractmethod
88 def parse_command(self):
89 """ Return string contianing command to run.
90 """
91
92 def parse_expression(self):
93 """ Return string contianing command to run.
94 """
95 return None
96
97 @abstractmethod
98 def parse_environment_variables(self):
99 """ Return environment variable templates to expose.
100 """
101
102 def parse_home_target(self):
103 """Should be "job_home", "shared_home", "job_tmp", "pwd", or None.
104 """
105 return "pwd"
106
107 def parse_tmp_target(self):
108 """Should be "pwd", "shared_home", "job_tmp", "job_tmp_if_explicit", or None.
109 """
110 return "job_tmp"
111
112 def parse_tmp_directory_vars(self):
113 """Directories to override if a tmp_target is not None."""
114 return ["TMPDIR", "TMP", "TEMP"]
115
116 def parse_docker_env_pass_through(self):
117 return ["GALAXY_SLOTS", "HOME", "_GALAXY_JOB_HOME_DIR", "_GALAXY_JOB_TMP_DIR"] + self.parse_tmp_directory_vars()
118
119 @abstractmethod
120 def parse_interpreter(self):
121 """ Return string containing the interpreter to prepend to the command
122 (for instance this might be 'python' to run a Python wrapper located
123 adjacent to the tool).
124 """
125
126 @abstractmethod
127 def parse_interactivetool(self):
128 """ Return InteractiveTool entry point templates to expose.
129 """
130
131 def parse_redirect_url_params_elem(self):
132 """ Return an XML element describing redirect_url_params.
133
134 If we wish to support this feature for non-XML based tools this should
135 be converted to return some sort of object interface instead of a RAW
136 XML element.
137 """
138 return None
139
140 def parse_version_command(self):
141 """ Parse command used to determine version of primary application
142 driving the tool. Return None to not generate or record such a command.
143 """
144 return None
145
146 def parse_version_command_interpreter(self):
147 """ Parse command used to determine version of primary application
148 driving the tool. Return None to not generate or record such a command.
149 """
150 return None
151
152 def parse_parallelism(self):
153 """ Return a galaxy.jobs.ParallismInfo object describing task splitting
154 or None.
155 """
156 return None
157
158 def parse_hidden(self):
159 """ Return boolean indicating whether tool should be hidden in the tool menu.
160 """
161 return False
162
163 def parse_sanitize(self):
164 """ Return boolean indicating whether tool should be sanitized or not.
165 """
166 return True
167
168 def parse_refresh(self):
169 """ Return boolean indicating ... I have no clue...
170 """
171 return False
172
173 @abstractmethod
174 def parse_requirements_and_containers(self):
175 """ Return pair of ToolRequirement and ContainerDescription lists. """
176
177 @abstractmethod
178 def parse_input_pages(self):
179 """ Return a PagesSource representing inputs by page for tool. """
180
181 def parse_provided_metadata_style(self):
182 """Return style of tool provided metadata file (e.g. galaxy.json).
183
184 A value of of "default" indicates the newer galaxy.json style
185 (the default for XML-based tools with profile >= 17.09) and a value
186 of "legacy" indicates the older galaxy.json style.
187
188 A short description of these two styles can be found at
189 https://github.com/galaxyproject/galaxy/pull/4437.
190 """
191 return "default"
192
193 def parse_provided_metadata_file(self):
194 """Return location of provided metadata file (e.g. galaxy.json)."""
195 return "galaxy.json"
196
197 @abstractmethod
198 def parse_outputs(self, tool):
199 """ Return a pair of output and output collections ordered
200 dictionaries for use by Tool.
201 """
202
203 @abstractmethod
204 def parse_strict_shell(self):
205 """ Return True if tool commands should be executed with
206 set -e.
207 """
208
209 @abstractmethod
210 def parse_stdio(self):
211 """ Builds lists of ToolStdioExitCode and ToolStdioRegex objects
212 to describe tool execution error conditions.
213 """
214 return [], []
215
216 @abstractmethod
217 def parse_help(self):
218 """ Return RST definition of help text for tool or None if the tool
219 doesn't define help text.
220 """
221
222 @abstractmethod
223 def parse_profile(self):
224 """ Return tool profile version as Galaxy major e.g. 16.01 or 16.04.
225 """
226
227 @abstractmethod
228 def parse_python_template_version(self):
229 """
230 Return minimum python version that the tool template has been developed against.
231 """
232
233 @property
234 def macro_paths(self):
235 return []
236
237 @property
238 def source_path(self):
239 return None
240
241 def paths_and_modtimes(self):
242 paths_and_modtimes = {p: os.path.getmtime(p) for p in self.macro_paths}
243 if self.source_path:
244 paths_and_modtimes[self.source_path] = os.path.getmtime(self.source_path)
245 return paths_and_modtimes
246
247 def parse_tests_to_dict(self):
248 return {'tests': []}
249
250 def __str__(self):
251 source_path = self.source_path
252 if source_path:
253 as_str = u'%s[%s]' % (self.__class__.__name__, source_path)
254 else:
255 as_str = u'%s[In-memory]' % (self.__class__.__name__)
256 return as_str
257
258
259 class PagesSource(object):
260 """ Contains a list of Pages - each a list of InputSources -
261 each item in the outer list representing a page of inputs.
262 Pages are deprecated so ideally this outer list will always
263 be exactly a singleton.
264 """
265
266 def __init__(self, page_sources):
267 self.page_sources = page_sources
268
269 @property
270 def inputs_defined(self):
271 return True
272
273
274 @six.add_metaclass(ABCMeta)
275 class PageSource(object):
276
277 def parse_display(self):
278 return None
279
280 @abstractmethod
281 def parse_input_sources(self):
282 """ Return a list of InputSource objects. """
283
284
285 @six.add_metaclass(ABCMeta)
286 class InputSource(object):
287 default_optional = False
288
289 def elem(self):
290 # For things in transition that still depend on XML - provide a way
291 # to grab it and just throw an error if feature is attempted to be
292 # used with other tool sources.
293 raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE)
294
295 @abstractmethod
296 def get(self, key, value=None):
297 """ Return simple named properties as string for this input source.
298 keys to be supported depend on the parameter type.
299 """
300
301 @abstractmethod
302 def get_bool(self, key, default):
303 """ Return simple named properties as boolean for this input source.
304 keys to be supported depend on the parameter type.
305 """
306
307 def parse_label(self):
308 return self.get("label")
309
310 def parse_name(self):
311 """Return name of an input source
312 returns the name or if absent the argument property
313 In the latter case, leading dashes are stripped and
314 all remaining dashes are replaced by underscores.
315 """
316 return _parse_name(self.get('name'), self.get('argument'))
317
318 def parse_help(self):
319 return self.get("help")
320
321 def parse_sanitizer_elem(self):
322 """ Return an XML description of sanitizers. This is a stop gap
323 until we can rework galaxy.tools.parameters.sanitize to not
324 explicitly depend on XML.
325 """
326 return None
327
328 def parse_validator_elems(self):
329 """ Return an XML description of sanitizers. This is a stop gap
330 until we can rework galaxy.tools.parameters.validation to not
331 explicitly depend on XML.
332 """
333 return []
334
335 def parse_optional(self, default=None):
336 """ Return boolean indicating wheter parameter is optional. """
337 if default is None:
338 default = self.default_optional
339 return self.get_bool("optional", default)
340
341 def parse_dynamic_options_elem(self):
342 """ Return an XML elemnt describing dynamic options.
343 """
344 return None
345
346 def parse_static_options(self):
347 """ Return list of static options if this is a select type without
348 defining a dynamic options.
349 """
350 return []
351
352 def parse_conversion_tuples(self):
353 """ Return list of (name, extension) to describe explicit conversions.
354 """
355 return []
356
357 def parse_nested_inputs_source(self):
358 # For repeats
359 raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE)
360
361 def parse_test_input_source(self):
362 # For conditionals
363 raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE)
364
365 def parse_when_input_sources(self):
366 raise NotImplementedError(NOT_IMPLEMENTED_MESSAGE)
367
368
369 class TestCollectionDef(object):
370
371 def __init__(self, attrib, name, collection_type, elements):
372 self.attrib = attrib
373 self.collection_type = collection_type
374 self.elements = elements
375 self.name = name
376
377 @staticmethod
378 def from_xml(elem, parse_param_elem):
379 elements = []
380 attrib = dict(elem.attrib)
381 collection_type = attrib["type"]
382 name = attrib.get("name", "Unnamed Collection")
383 for element in elem.findall("element"):
384 element_attrib = dict(element.attrib)
385 element_identifier = element_attrib["name"]
386 nested_collection_elem = element.find("collection")
387 if nested_collection_elem is not None:
388 element_definition = TestCollectionDef.from_xml(nested_collection_elem, parse_param_elem)
389 else:
390 element_definition = parse_param_elem(element)
391 elements.append({"element_identifier": element_identifier, "element_definition": element_definition})
392
393 return TestCollectionDef(
394 attrib=attrib,
395 collection_type=collection_type,
396 elements=elements,
397 name=name,
398 )
399
400 def to_dict(self):
401 def element_to_dict(element_dict):
402 element_identifier, element_def = element_dict["element_identifier"], element_dict["element_definition"]
403 if isinstance(element_def, TestCollectionDef):
404 element_def = element_def.to_dict()
405 return {
406 "element_identifier": element_identifier,
407 "element_definition": element_def,
408 }
409
410 return {
411 "model_class": "TestCollectionDef",
412 "attributes": self.attrib,
413 "collection_type": self.collection_type,
414 "elements": list(map(element_to_dict, self.elements or [])),
415 "name": self.name,
416 }
417
418 @staticmethod
419 def from_dict(as_dict):
420 assert as_dict["model_class"] == "TestCollectionDef"
421
422 def element_from_dict(element_dict):
423 if "element_definition" not in element_dict:
424 raise Exception("Invalid element_dict %s" % element_dict)
425 element_def = element_dict["element_definition"]
426 if element_def.get("model_class", None) == "TestCollectionDef":
427 element_def = TestCollectionDef.from_dict(element_def)
428 return {"element_identifier": element_dict["element_identifier"], "element_definition": element_def}
429
430 return TestCollectionDef(
431 attrib=as_dict["attributes"],
432 name=as_dict["name"],
433 elements=list(map(element_from_dict, as_dict["elements"] or [])),
434 collection_type=as_dict["collection_type"],
435 )
436
437 def collect_inputs(self):
438 inputs = []
439 for element in self.elements:
440 value = element["element_definition"]
441 if isinstance(value, TestCollectionDef):
442 inputs.extend(value.collect_inputs())
443 else:
444 inputs.append(value)
445 return inputs
446
447
448 class TestCollectionOutputDef(object):
449
450 def __init__(self, name, attrib, element_tests):
451 self.name = name
452 self.collection_type = attrib.get("type", None)
453 count = attrib.get("count", None)
454 self.count = int(count) if count is not None else None
455 self.attrib = attrib
456 self.element_tests = element_tests
457
458 @staticmethod
459 def from_dict(as_dict):
460 return TestCollectionOutputDef(
461 name=as_dict["name"],
462 attrib=as_dict["attributes"],
463 element_tests=as_dict["element_tests"],
464 )
465
466 def to_dict(self):
467 return dict(
468 name=self.name,
469 attributes=self.attrib,
470 element_tests=self.element_tests
471 )