comparison toolfactory/galaxyxml/tool/parameters/__init__.py @ 35:5d38cb3d9be8 draft

added patched galaxyxml code temporarily until PR accepted
author fubar
date Sat, 08 Aug 2020 19:55:55 -0400
parents
children ce2b1f8ea68d
comparison
equal deleted inserted replaced
34:5052ac89c036 35:5d38cb3d9be8
1 from builtins import str
2 from builtins import object
3 from lxml import etree
4 from galaxyxml import Util
5
6
7 class XMLParam(object):
8 name = 'node'
9
10 def __init__(self, *args, **kwargs):
11 # http://stackoverflow.com/a/12118700
12 self.children = []
13 kwargs = {k: v for k, v in list(kwargs.items()) if v is not None}
14 kwargs = Util.coerce(kwargs, kill_lists=True)
15 kwargs = Util.clean_kwargs(kwargs, final=True)
16 self.node = etree.Element(self.name, **kwargs)
17
18 def append(self, sub_node):
19 if self.acceptable_child(sub_node):
20 # If one of ours, they aren't etree nodes, they're custom objects
21 if issubclass(type(sub_node), XMLParam):
22 self.node.append(sub_node.node)
23 self.children.append(sub_node)
24 else:
25 raise Exception("Child was unacceptable to parent (%s is not appropriate for %s)" % (
26 type(self), type(sub_node)))
27 else:
28 raise Exception("Child was unacceptable to parent (%s is not appropriate for %s)" % (
29 type(self), type(sub_node)))
30
31 def validate(self):
32 # Very few need validation, but some nodes we may want to have
33 # validation routines on. Should only be called when DONE.
34 for child in self.children:
35 # If any child fails to validate return false.
36 if not child.validate():
37 return False
38 return True
39
40 def cli(self):
41 lines = []
42 for child in self.children:
43 lines.append(child.command_line())
44 # lines += child.command_line()
45 return '\n'.join(lines)
46
47 def command_line(self):
48 return None
49
50
51 class RequestParamTranslation(XMLParam):
52 name = 'request_param_translation'
53
54 def __init__(self, **kwargs):
55 self.node = etree.Element(self.name)
56
57 def acceptable_child(self, child):
58 return isinstance(child, RequestParamTranslation)
59
60
61 class RequestParam(XMLParam):
62 name = 'request_param'
63
64 def __init__(self, galaxy_name, remote_name, missing, **kwargs):
65 # TODO: bulk copy locals into self.attr?
66 self.galaxy_name = galaxy_name
67 # http://stackoverflow.com/a/1408860
68 params = Util.clean_kwargs(locals().copy())
69 super(RequestParam, self).__init__(**params)
70
71 def acceptable_child(self, child):
72 return isinstance(child, AppendParam) and self.galaxy_name == "URL"
73
74
75 class AppendParam(XMLParam):
76 name = 'append_param'
77
78 def __init__(self, separator="&", first_separator="?", join="=", **kwargs):
79 params = Util.clean_kwargs(locals().copy())
80 super(AppendParam, self).__init__(**params)
81
82 def acceptable_child(self, child):
83 return isinstance(child, AppendParamValue)
84
85
86 class AppendParamValue(XMLParam):
87 name = 'value'
88
89 def __init__(self, name="_export", missing="1", **kwargs):
90 params = Util.clean_kwargs(locals().copy())
91 super(AppendParamValue, self).__init__(**params)
92
93 def acceptable_child(self, child):
94 return False
95
96
97 class EdamOperations(XMLParam):
98 name = 'edam_operations'
99
100 def acceptable_child(self, child):
101 return issubclass(type(child), EdamOperation)
102
103 def has_operation(self, edam_operation):
104 """
105 Check the presence of a given edam_operation.
106
107 :type edam_operation: STRING
108 """
109 for operation in self.children:
110 if operation.node.text == edam_operation:
111 return True
112 return False
113
114
115 class EdamOperation(XMLParam):
116 name = 'edam_operation'
117
118 def __init__(self, value):
119 super(EdamOperation, self).__init__()
120 self.node.text = str(value)
121
122
123 class EdamTopics(XMLParam):
124 name = 'edam_topics'
125
126 def acceptable_child(self, child):
127 return issubclass(type(child), EdamTopic)
128
129 def has_topic(self, edam_topic):
130 """
131 Check the presence of a given edam_topic.
132
133 :type edam_topic: STRING
134 """
135 for topic in self.children:
136 if topic.node.text == edam_topic:
137 return True
138 return False
139
140
141 class EdamTopic(XMLParam):
142 name = 'edam_topic'
143
144 def __init__(self, value):
145 super(EdamTopic, self).__init__()
146 self.node.text = str(value)
147
148
149 class Requirements(XMLParam):
150 name = 'requirements'
151 # This bodes to be an issue -__-
152
153 def acceptable_child(self, child):
154 return issubclass(type(child), Requirement) or issubclass(type(child), Container)
155
156
157 class Requirement(XMLParam):
158 name = 'requirement'
159
160 def __init__(self, type, value, version=None, **kwargs):
161 params = Util.clean_kwargs(locals().copy())
162 passed_kwargs = {}
163 passed_kwargs['version'] = params['version']
164 passed_kwargs['type'] = params['type']
165 super(Requirement, self).__init__(**passed_kwargs)
166 self.node.text = str(value)
167
168
169 class Container(XMLParam):
170 name = 'container'
171
172 def __init__(self, type, value, **kwargs):
173 params = Util.clean_kwargs(locals().copy())
174 passed_kwargs = {}
175 passed_kwargs['type'] = params['type']
176 super(Container, self).__init__(**passed_kwargs)
177 self.node.text = str(value)
178
179
180 class Configfiles(XMLParam):
181 name = 'configfiles'
182
183 def acceptable_child(self, child):
184 return issubclass(type(child), Configfile) or issubclass(type(child), ConfigfileDefaultInputs)
185
186
187 class Configfile(XMLParam):
188 name = 'configfile'
189
190 def __init__(self, name, text, **kwargs):
191 params = Util.clean_kwargs(locals().copy())
192 passed_kwargs = {}
193 passed_kwargs['name'] = params['name']
194 super(Configfile, self).__init__(**passed_kwargs)
195 self.node.text = etree.CDATA(str(text))
196
197
198 class ConfigfileDefaultInputs(XMLParam):
199 name = 'inputs'
200
201 def __init__(self, name, **kwargs):
202 params = Util.clean_kwargs(locals().copy())
203 passed_kwargs = {}
204 passed_kwargs['name'] = params['name']
205 super(ConfigfileDefaultInputs, self).__init__(**passed_kwargs)
206
207
208 class Inputs(XMLParam):
209 name = 'inputs'
210 # This bodes to be an issue -__-
211
212 def __init__(self, action=None, check_value=None, method=None,
213 target=None, nginx_upload=None, **kwargs):
214 params = Util.clean_kwargs(locals().copy())
215 super(Inputs, self).__init__(**params)
216
217 def acceptable_child(self, child):
218 return issubclass(type(child), InputParameter)
219
220
221 class InputParameter(XMLParam):
222
223 def __init__(self, name, **kwargs):
224 # TODO: look at
225 self.mako_identifier = name
226 # We use kwargs instead of the usual locals(), so manually copy the
227 # name to kwargs
228 if name is not None:
229 kwargs['name'] = name
230
231 # Handle positional parameters
232 if 'positional' in kwargs and kwargs['positional']:
233 self.positional = True
234 else:
235 self.positional = False
236
237 if 'num_dashes' in kwargs:
238 self.num_dashes = kwargs['num_dashes']
239 del kwargs['num_dashes']
240 else:
241 self.num_dashes = 0
242
243 self.space_between_arg = " "
244
245 # Not sure about this :(
246 # https://wiki.galaxyproject.org/Tools/BestPractices#Parameter_help
247 if 'label' in kwargs:
248 # TODO: replace with positional attribute
249 if len(self.flag()) > 0:
250 if kwargs['label'] is None:
251 kwargs[
252 'label'] = 'Author did not provide help for this parameter... '
253 if not self.positional:
254 kwargs['argument'] = self.flag()
255
256 super(InputParameter, self).__init__(**kwargs)
257
258 def command_line(self):
259 before = self.command_line_before()
260 cli = self.command_line_actual()
261 after = self.command_line_after()
262
263 complete = [x for x in (before, cli, after) if x is not None]
264 return '\n'.join(complete)
265
266 def command_line_before(self):
267 try:
268 return self.command_line_before_override
269 except Exception:
270 return None
271
272 def command_line_after(self):
273 try:
274 return self.command_line_after_override
275 except Exception:
276 return None
277
278 def command_line_actual(self):
279 try:
280 return self.command_line_override
281 except Exception:
282 if self.positional:
283 return self.mako_name()
284 else:
285 return "%s%s%s" % (self.flag(), self.space_between_arg, self.mako_name())
286
287 def mako_name(self):
288 # TODO: enhance logic to check up parents for things like
289 # repeat>condotion>param
290 return '$' + self.mako_identifier
291
292 def flag(self):
293 flag = '-' * self.num_dashes
294 return flag + self.mako_identifier
295
296
297 class Section(InputParameter):
298 name = 'section'
299
300 def __init__(self, name, title, expanded=None, help=None, **kwargs):
301 params = Util.clean_kwargs(locals().copy())
302 super(Section, self).__init__(**params)
303
304 def acceptable_child(self, child):
305 return issubclass(type(child), InputParameter)
306
307
308 class Repeat(InputParameter):
309 name = 'repeat'
310
311 def __init__(self, name, title, min=None, max=None, default=None,
312 **kwargs):
313 params = Util.clean_kwargs(locals().copy())
314 # Allow overriding
315 self.command_line_before_override = '#for $i in $%s:' % name
316 self.command_line_after_override = '#end for'
317 # self.command_line_override
318 super(Repeat, self).__init__(**params)
319
320 def acceptable_child(self, child):
321 return issubclass(type(child), InputParameter)
322
323 def command_line_actual(self):
324 if hasattr(self, 'command_line_override'):
325 return self.command_line_override
326 else:
327 return "%s" % self.mako_name()
328
329
330 class Conditional(InputParameter):
331 name = 'conditional'
332
333 def __init__(self, name, **kwargs):
334 params = Util.clean_kwargs(locals().copy())
335 super(Conditional, self).__init__(**params)
336
337 def acceptable_child(self, child):
338 return issubclass(type(child), InputParameter) \
339 and not isinstance(child, Conditional)
340
341 def validate(self):
342 # Find a way to check if one of the kids is a WHEN
343 pass
344
345
346 class When(InputParameter):
347 name = 'when'
348
349 def __init__(self, value):
350 params = Util.clean_kwargs(locals().copy())
351 super(When, self).__init__(None, **params)
352
353 def acceptable_child(self, child):
354 return issubclass(type(child), InputParameter)
355
356
357 class Param(InputParameter):
358 name = 'param'
359
360 # This...isn't really valid as-is, and shouldn't be used.
361 def __init__(self, name, optional=None, label=None, help=None, **kwargs):
362 params = Util.clean_kwargs(locals().copy())
363 params['type'] = self.type
364 super(Param, self).__init__(**params)
365
366 if type(self) == Param:
367 raise Exception(
368 "Param class is not an actual parameter type, use a subclass of Param")
369
370 def acceptable_child(self, child):
371 return issubclass(type(child, InputParameter) or isinstance(child), ValidatorParam)
372
373
374 class TextParam(Param):
375 type = 'text'
376
377 def __init__(self, name, optional=None, label=None, help=None,
378 value=None, **kwargs):
379 params = Util.clean_kwargs(locals().copy())
380 super(TextParam, self).__init__(**params)
381
382 def command_line_actual(self):
383 try:
384 return self.command_line_override
385 except Exception:
386 if self.positional:
387 return self.mako_name()
388 else:
389 return f"{self.flag}{self.space_between_arg}'{self.mako_name()}'"
390
391
392 class _NumericParam(Param):
393
394 def __init__(self, name, value, optional=None, label=None, help=None,
395 min=None, max=None, **kwargs):
396 params = Util.clean_kwargs(locals().copy())
397 super(_NumericParam, self).__init__(**params)
398
399
400 class IntegerParam(_NumericParam):
401 type = 'integer'
402
403
404 class FloatParam(_NumericParam):
405 type = 'float'
406
407
408 class BooleanParam(Param):
409 type = 'boolean'
410
411 def __init__(self, name, optional=None, label=None, help=None,
412 checked=False, truevalue=None, falsevalue=None, **kwargs):
413 params = Util.clean_kwargs(locals().copy())
414
415 super(BooleanParam, self).__init__(**params)
416 if truevalue is None:
417 # If truevalue and falsevalue are None, then we use "auto", the IUC
418 # recommended default.
419 #
420 # truevalue is set to the parameter's value, and falsevalue is not.
421 #
422 # Unfortunately, mako_identifier is set as a result of the super
423 # call, which we shouldn't call TWICE, so we'll just hack around this :(
424 # params['truevalue'] = '%s%s' % (self.)
425 self.node.attrib['truevalue'] = self.flag()
426
427 if falsevalue is None:
428 self.node.attrib['falsevalue'] = ""
429
430 def command_line_actual(self):
431 if hasattr(self, 'command_line_override'):
432 return self.command_line_override
433 else:
434 return "%s" % self.mako_name()
435
436
437 class DataParam(Param):
438 type = 'data'
439
440 def __init__(self, name, optional=None, label=None, help=None, format=None,
441 multiple=None, **kwargs):
442 params = Util.clean_kwargs(locals().copy())
443 super(DataParam, self).__init__(**params)
444
445
446 class SelectParam(Param):
447 type = 'select'
448
449 def __init__(self, name, optional=None, label=None, help=None,
450 data_ref=None, display=None, multiple=None, options=None,
451 default=None, **kwargs):
452 params = Util.clean_kwargs(locals().copy())
453 del params['options']
454 del params['default']
455
456 super(SelectParam, self).__init__(**params)
457
458 if options is not None and default is not None:
459 if default not in options:
460 raise Exception("Specified a default that isn't in options")
461
462 if options:
463 for k, v in list(sorted(options.items())):
464 selected = (k == default)
465 self.append(SelectOption(k, v, selected=selected))
466
467 def acceptable_child(self, child):
468 return issubclass(type(child), SelectOption) \
469 or issubclass(type(child), Options)
470
471
472 class SelectOption(InputParameter):
473 name = 'option'
474
475 def __init__(self, value, text, selected=False, **kwargs):
476 params = Util.clean_kwargs(locals().copy())
477
478 passed_kwargs = {}
479 if selected:
480 passed_kwargs['selected'] = "true"
481 passed_kwargs['value'] = params['value']
482
483 super(SelectOption, self).__init__(None, **passed_kwargs)
484 self.node.text = str(text)
485
486
487 class Options(InputParameter):
488 name = 'options'
489
490 def __init__(self, from_dataset=None, from_file=None, from_data_table=None,
491 from_parameter=None, **kwargs):
492 params = Util.clean_kwargs(locals().copy())
493 super(Options, self).__init__(None, **params)
494
495 def acceptable_child(self, child):
496 return issubclass(type(child), Column) or issubclass(type(child), Filter)
497
498
499 class Column(InputParameter):
500 name = 'column'
501
502 def __init__(self, name, index, **kwargs):
503 params = Util.clean_kwargs(locals().copy())
504 super(Column, self).__init__(**params)
505
506
507 class Filter(InputParameter):
508 name = 'filter'
509
510 def __init__(self, type, column=None, name=None, ref=None, key=None,
511 multiple=None, separator=None, keep=None, value=None,
512 ref_attribute=None, index=None, **kwargs):
513 params = Util.clean_kwargs(locals().copy())
514 super(Filter, self).__init__(**params)
515
516
517 class ValidatorParam(InputParameter):
518 name = 'validator'
519
520 def __init__(self, type, message=None, filename=None, metadata_name=None,
521 metadata_column=None, line_startswith=None, min=None,
522 max=None, **kwargs):
523 params = Util.clean_kwargs(locals().copy())
524 super(ValidatorParam, self).__init__(**params)
525
526
527 class Outputs(XMLParam):
528 name = 'outputs'
529
530 def acceptable_child(self, child):
531 return isinstance(child, OutputData) or isinstance(child, OutputCollection)
532
533
534 class OutputData(XMLParam):
535 """Copypasta of InputParameter, needs work
536 """
537 name = 'data'
538
539 def __init__(self, name, format, format_source=None, metadata_source=None,
540 label=None, from_work_dir=None, hidden=False, **kwargs):
541 # TODO: validate format_source&metadata_source against something in the
542 # XMLParam children tree.
543 self.mako_identifier = name
544 if 'num_dashes' in kwargs:
545 self.num_dashes = kwargs['num_dashes']
546 del kwargs['num_dashes']
547 else:
548 self.num_dashes = 0
549 self.space_between_arg = " "
550 params = Util.clean_kwargs(locals().copy())
551
552 super(OutputData, self).__init__(**params)
553
554 def command_line(self):
555 if hasattr(self, 'command_line_override'):
556 return self.command_line_override
557 else:
558 return "%s%s%s" % (self.flag(), self.space_between_arg, self.mako_name())
559
560 def mako_name(self):
561 return '$' + self.mako_identifier
562
563 def flag(self):
564 flag = '-' * self.num_dashes
565 return flag + self.mako_identifier
566
567 def acceptable_child(self, child):
568 return isinstance(child, OutputFilter) or \
569 isinstance(child, ChangeFormat) or \
570 isinstance(child, DiscoverDatasets)
571
572
573 class OutputFilter(XMLParam):
574 name = 'filter'
575
576 def __init__(self, text, **kwargs):
577 params = Util.clean_kwargs(locals().copy())
578 del params['text']
579 super(OutputFilter, self).__init__(**params)
580 self.node.text = text
581
582 def acceptable_child(self, child):
583 return False
584
585
586 class ChangeFormat(XMLParam):
587 name = 'change_format'
588
589 def __init__(self, **kwargs):
590 params = Util.clean_kwargs(locals().copy())
591 super(ChangeFormat, self).__init__(**params)
592
593 def acceptable_child(self, child):
594 return isinstance(child, ChangeFormatWhen)
595
596
597 class ChangeFormatWhen(XMLParam):
598 name = 'when'
599
600 def __init__(self, input, format, value, **kwargs):
601 params = Util.clean_kwargs(locals().copy())
602 super(ChangeFormatWhen, self).__init__(**params)
603
604 def acceptable_child(self, child):
605 return False
606
607
608 class OutputCollection(XMLParam):
609 name = 'collection'
610
611 def __init__(self, name, type=None, label=None, format_source=None,
612 type_source=None, structured_like=None, inherit_format=None, **kwargs):
613 params = Util.clean_kwargs(locals().copy())
614 super(OutputCollection, self).__init__(**params)
615
616 def acceptable_child(self, child):
617 return isinstance(child, OutputData) or isinstance(child, OutputFilter) \
618 or isinstance(child, DiscoverDatasets)
619
620
621 class DiscoverDatasets(XMLParam):
622 name = 'discover_datasets'
623
624 def __init__(self, pattern, directory=None, format=None, ext=None,
625 visible=None, **kwargs):
626 params = Util.clean_kwargs(locals().copy())
627 super(DiscoverDatasets, self).__init__(**params)
628
629
630 class Tests(XMLParam):
631 name = 'tests'
632
633 def acceptable_child(self, child):
634 return issubclass(type(child), Test)
635
636
637 class Test(XMLParam):
638 name = 'test'
639
640 def acceptable_child(self, child):
641 return isinstance(child, TestParam) or isinstance(child, TestOutput)
642
643
644 class TestParam(XMLParam):
645 name = 'param'
646
647 def __init__(self, name, value=None, ftype=None, dbkey=None, **kwargs):
648 params = Util.clean_kwargs(locals().copy())
649 super(TestParam, self).__init__(**params)
650
651
652 class TestOutput(XMLParam):
653 name = 'output'
654
655 def __init__(self, name=None, file=None, ftype=None, sort=None, value=None,
656 md5=None, checksum=None, compare=None, lines_diff=None,
657 delta=None, **kwargs):
658 params = Util.clean_kwargs(locals().copy())
659 super(TestOutput, self).__init__(**params)
660
661
662 class Citations(XMLParam):
663 name = 'citations'
664
665 def acceptable_child(self, child):
666 return issubclass(type(child), Citation)
667
668 def has_citation(self, type, value):
669 """
670 Check the presence of a given citation.
671
672 :type type: STRING
673 :type value: STRING
674 """
675 for citation in self.children:
676 if citation.node.attrib['type'] == type and citation.node.text == value:
677 return True
678 return False
679
680
681 class Citation(XMLParam):
682 name = 'citation'
683
684 def __init__(self, type, value):
685 passed_kwargs = {}
686 passed_kwargs['type'] = type
687 super(Citation, self).__init__(**passed_kwargs)
688 self.node.text = str(value)