Mercurial > repos > fubar > tool_factory_2
comparison toolfactory/rgToolFactory2.py @ 49:35a912ce0c83 draft
Can now make the bwa example from planemo :)
author | fubar |
---|---|
date | Thu, 27 Aug 2020 23:11:01 -0400 |
parents | 5a7a5b06bce0 |
children | bf432f4486c7 |
comparison
equal
deleted
inserted
replaced
48:5a7a5b06bce0 | 49:35a912ce0c83 |
---|---|
4 # | 4 # |
5 # copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012 | 5 # copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012 |
6 # | 6 # |
7 # all rights reserved | 7 # all rights reserved |
8 # Licensed under the LGPL | 8 # Licensed under the LGPL |
9 # suggestions for improvement and bug fixes welcome at https://github.com/fubar2/toolfactory | 9 # suggestions for improvement and bug fixes welcome at |
10 # https://github.com/fubar2/toolfactory | |
10 # | 11 # |
11 # July 2020: BCC was fun and I feel like rip van winkle after 5 years. | 12 # July 2020: BCC was fun and I feel like rip van winkle after 5 years. |
12 # Decided to | 13 # Decided to |
13 # 1. Fix the toolfactory so it works - done for simplest case | 14 # 1. Fix the toolfactory so it works - done for simplest case |
14 # 2. Fix planemo so the toolfactory function works | 15 # 2. Fix planemo so the toolfactory function works |
44 myversion = "V2.1 July 2020" | 45 myversion = "V2.1 July 2020" |
45 verbose = True | 46 verbose = True |
46 debug = True | 47 debug = True |
47 toolFactoryURL = "https://github.com/fubar2/toolfactory" | 48 toolFactoryURL = "https://github.com/fubar2/toolfactory" |
48 ourdelim = "~~~" | 49 ourdelim = "~~~" |
50 ALOT = 10000000 # srsly. command or test overrides use read() so just in case | |
51 STDIOXML = """<stdio> | |
52 <exit_code range="100:" level="debug" description="shite happens" /> | |
53 </stdio>""" | |
49 | 54 |
50 # --input_files="$input_files~~~$CL~~~$input_formats~~~$input_label | 55 # --input_files="$input_files~~~$CL~~~$input_formats~~~$input_label |
51 # ~~~$input_help" | 56 # ~~~$input_help" |
52 IPATHPOS = 0 | 57 IPATHPOS = 0 |
53 ICLPOS = 1 | 58 ICLPOS = 1 |
54 IFMTPOS = 2 | 59 IFMTPOS = 2 |
55 ILABPOS = 3 | 60 ILABPOS = 3 |
56 IHELPOS = 4 | 61 IHELPOS = 4 |
57 IOCLPOS = 5 | 62 IOCLPOS = 5 |
58 | 63 |
59 # --output_files "$otab.history_name~~~$otab.history_format~~~$otab.CL | 64 # --output_files "$otab.history_name~~~$otab.history_format~~~$otab.CL~~~otab.history_test |
60 ONAMEPOS = 0 | 65 ONAMEPOS = 0 |
61 OFMTPOS = 1 | 66 OFMTPOS = 1 |
62 OCLPOS = 2 | 67 OCLPOS = 2 |
63 OOCLPOS = 3 | 68 OTESTPOS = 3 |
69 OOCLPOS = 4 | |
70 | |
64 | 71 |
65 # --additional_parameters="$i.param_name~~~$i.param_value~~~ | 72 # --additional_parameters="$i.param_name~~~$i.param_value~~~ |
66 # $i.param_label~~~$i.param_help~~~$i.param_type~~~$i.CL~~~i$.param_CLoverride" | 73 # $i.param_label~~~$i.param_help~~~$i.param_type~~~$i.CL~~~i$.param_CLoverride" |
67 ANAMEPOS = 0 | 74 ANAMEPOS = 0 |
68 AVALPOS = 1 | 75 AVALPOS = 1 |
74 AOCLPOS = 7 | 81 AOCLPOS = 7 |
75 | 82 |
76 | 83 |
77 foo = len(lxml.__version__) | 84 foo = len(lxml.__version__) |
78 # fug you, flake8. Say my name! | 85 # fug you, flake8. Say my name! |
86 FAKEEXE = "~~~REMOVE~~~ME~~~" | |
87 # need this until a PR/version bump to fix galaxyxml prepending the exe even | |
88 # with override. | |
89 | |
79 | 90 |
80 def timenow(): | 91 def timenow(): |
81 """return current time as a string | 92 """return current time as a string |
82 """ | 93 """ |
83 return time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(time.time())) | 94 return time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(time.time())) |
118 citation_tuples = [] | 129 citation_tuples = [] |
119 for citation in citations: | 130 for citation in citations: |
120 if citation.startswith("doi"): | 131 if citation.startswith("doi"): |
121 citation_tuples.append(("doi", citation[len("doi") :].strip())) | 132 citation_tuples.append(("doi", citation[len("doi") :].strip())) |
122 else: | 133 else: |
123 citation_tuples.append( | 134 citation_tuples.append(("bibtex", citation[len("bibtex") :].strip())) |
124 ("bibtex", citation[len("bibtex") :].strip()) | |
125 ) | |
126 return citation_tuples | 135 return citation_tuples |
127 | 136 |
128 | 137 |
129 class ScriptRunner: | 138 class ScriptRunner: |
130 """Wrapper for an arbitrary script | 139 """Wrapper for an arbitrary script |
135 def __init__(self, args=None): | 144 def __init__(self, args=None): |
136 """ | 145 """ |
137 prepare command line cl for running the tool here | 146 prepare command line cl for running the tool here |
138 and prepare elements needed for galaxyxml tool generation | 147 and prepare elements needed for galaxyxml tool generation |
139 """ | 148 """ |
140 | |
141 self.infiles = [x.split(ourdelim) for x in args.input_files] | 149 self.infiles = [x.split(ourdelim) for x in args.input_files] |
142 self.outfiles = [x.split(ourdelim) for x in args.output_files] | 150 self.outfiles = [x.split(ourdelim) for x in args.output_files] |
143 self.addpar = [x.split(ourdelim) for x in args.additional_parameters] | 151 self.addpar = [x.split(ourdelim) for x in args.additional_parameters] |
144 self.args = args | 152 self.args = args |
145 self.cleanuppar() | 153 self.cleanuppar() |
146 self.lastclredirect = None | 154 self.lastclredirect = None |
147 self.lastxclredirect = None | 155 self.lastxclredirect = None |
148 self.cl = [] | 156 self.cl = [] |
149 self.xmlcl = [] | 157 self.xmlcl = [] |
150 self.is_positional = self.args.parampass == "positional" | 158 self.is_positional = self.args.parampass == "positional" |
159 if self.args.packages: | |
160 self.executeme = self.args.packages.split(",")[0].split(":")[0] | |
161 else: | |
162 self.executeme = self.args.sysexe | |
163 assert ( | |
164 self.executeme is not None | |
165 ), "No system or managed executable passed in. Cannot build" | |
151 aCL = self.cl.append | 166 aCL = self.cl.append |
167 aXCL = self.xmlcl.append | |
152 assert args.parampass in [ | 168 assert args.parampass in [ |
153 "0", | 169 "0", |
154 "argparse", | 170 "argparse", |
155 "positional", | 171 "positional", |
156 ], 'Parameter passing in args.parampass must be "0","positional" or "argparse"' | 172 ], 'args.parampass must be "0","positional" or "argparse"' |
157 self.tool_name = re.sub("[^a-zA-Z0-9_]+", "", args.tool_name) | 173 self.tool_name = re.sub("[^a-zA-Z0-9_]+", "", args.tool_name) |
158 self.tool_id = self.tool_name | 174 self.tool_id = self.tool_name |
159 if self.args.interpreter_name: | |
160 exe = "$runMe" | |
161 else: | |
162 exe = self.args.exe_package | |
163 assert ( | |
164 exe is not None | |
165 ), "No interpeter or executable passed in - nothing to run so cannot build" | |
166 self.tool = gxt.Tool( | 175 self.tool = gxt.Tool( |
167 self.args.tool_name, | 176 self.args.tool_name, |
168 self.tool_id, | 177 self.tool_id, |
169 self.args.tool_version, | 178 self.args.tool_version, |
170 self.args.tool_desc, | 179 self.args.tool_desc, |
171 exe, | 180 FAKEEXE, |
172 ) | 181 ) |
182 if self.args.script_path: | |
183 self.tool.interpreter = self.executeme | |
173 self.tooloutdir = "tfout" | 184 self.tooloutdir = "tfout" |
174 self.repdir = "TF_run_report_tempdir" | 185 self.repdir = "TF_run_report_tempdir" |
175 self.testdir = os.path.join(self.tooloutdir, "test-data") | 186 self.testdir = os.path.join(self.tooloutdir, "test-data") |
176 if not os.path.exists(self.tooloutdir): | 187 if not os.path.exists(self.tooloutdir): |
177 os.mkdir(self.tooloutdir) | 188 os.mkdir(self.tooloutdir) |
180 if not os.path.exists(self.repdir): | 191 if not os.path.exists(self.repdir): |
181 os.mkdir(self.repdir) | 192 os.mkdir(self.repdir) |
182 self.tinputs = gxtp.Inputs() | 193 self.tinputs = gxtp.Inputs() |
183 self.toutputs = gxtp.Outputs() | 194 self.toutputs = gxtp.Outputs() |
184 self.testparam = [] | 195 self.testparam = [] |
185 if ( | 196 if self.args.script_path: |
186 self.args.runmode == "Executable" or self.args.runmode == "system" | 197 self.prepScript() |
187 ): | 198 if self.args.command_override: |
188 if len(self.args.cl_override) > 0: | 199 scos = open(self.args.command_override, "r").readlines() |
189 for x in self.args.cl_override.split(' '): | 200 self.command_override = [x.rstrip() for x in scos] |
190 aCL(x) | 201 else: |
202 self.command_override = None | |
203 if self.args.test_override: | |
204 stos = open(self.args.test_override, "r").readlines() | |
205 self.test_override = [x.rstrip() for x in stos] | |
206 else: | |
207 self.test_override = None | |
208 if self.args.cl_prefix: # DIY CL start | |
209 clp = self.args.cl_prefix.split(" ") | |
210 for c in clp: | |
211 aCL(c) | |
212 aXCL(c) | |
213 else: | |
214 if self.args.runmode == "Executable": | |
215 if self.args.script_path: | |
216 aCL(self.executeme) | |
217 aCL(self.sfile) | |
218 aXCL("$runme") | |
219 else: | |
220 aCL(self.executeme) # this little CL will just run | |
221 aXCL(self.executeme) | |
191 else: | 222 else: |
192 aCL(self.args.exe_package) # this little CL will just run | 223 if self.args.script_path: |
193 else: | 224 aCL(self.executeme) |
194 self.prepScript() | 225 aCL(self.sfile) |
195 aCL(self.args.interpreter_name) | 226 aXCL("$runme") |
196 aCL(self.sfile) | 227 else: |
197 | 228 aCL(self.executeme) # this little CL will just run |
198 self.elog = "%s_error_log.txt" % self.tool_name | 229 aXCL(self.executeme) |
199 self.tlog = "%s_runner_log.txt" % self.tool_name | 230 self.elog = os.path.join(self.repdir,"%s_error_log.txt" % self.tool_name) |
231 self.tlog = os.path.join(self.repdir,"%s_runner_log.txt" % self.tool_name) | |
200 | 232 |
201 if self.args.parampass == "0": | 233 if self.args.parampass == "0": |
202 self.clsimple() | 234 self.clsimple() |
203 else: | 235 else: |
204 clsuffix = [] | 236 clsuffix = [] |
220 else: | 252 else: |
221 appendme = [p[IOCLPOS], p[ICLPOS], p[IPATHPOS], ""] | 253 appendme = [p[IOCLPOS], p[ICLPOS], p[IPATHPOS], ""] |
222 xappendme = [p[IOCLPOS], p[ICLPOS], "$%s" % p[ICLPOS], ""] | 254 xappendme = [p[IOCLPOS], p[ICLPOS], "$%s" % p[ICLPOS], ""] |
223 clsuffix.append(appendme) | 255 clsuffix.append(appendme) |
224 xclsuffix.append(xappendme) | 256 xclsuffix.append(xappendme) |
225 # print('##infile i=%d, appendme=%s' % (i,appendme)) | |
226 for i, p in enumerate(self.outfiles): | 257 for i, p in enumerate(self.outfiles): |
227 if p[OOCLPOS] == "STDOUT": | 258 if p[OOCLPOS] == "STDOUT": |
228 self.lastclredirect = [">", p[ONAMEPOS]] | 259 self.lastclredirect = [">", p[ONAMEPOS]] |
229 self.lastxclredirect = [">", "$%s" % p[OCLPOS]] | 260 self.lastxclredirect = [">", "$%s" % p[OCLPOS]] |
230 else: | 261 else: |
231 clsuffix.append([p[OOCLPOS], p[OCLPOS], p[ONAMEPOS], ""]) | 262 clsuffix.append([p[OOCLPOS], p[OCLPOS], p[ONAMEPOS], ""]) |
232 xclsuffix.append( | 263 xclsuffix.append([p[OOCLPOS], p[OCLPOS], "$%s" % p[ONAMEPOS], ""]) |
233 [p[OOCLPOS], p[OCLPOS], "$%s" % p[ONAMEPOS], ""] | |
234 ) | |
235 for p in self.addpar: | 264 for p in self.addpar: |
236 clsuffix.append( | 265 clsuffix.append([p[AOCLPOS], p[ACLPOS], p[AVALPOS], p[AOVERPOS]]) |
237 [p[AOCLPOS], p[ACLPOS], p[AVALPOS], p[AOVERPOS]] | |
238 ) | |
239 xclsuffix.append( | 266 xclsuffix.append( |
240 [p[AOCLPOS], p[ACLPOS], '"$%s"' % p[ANAMEPOS], p[AOVERPOS]] | 267 [p[AOCLPOS], p[ACLPOS], '"$%s"' % p[ANAMEPOS], p[AOVERPOS]] |
241 ) | 268 ) |
242 clsuffix.sort() | 269 clsuffix.sort() |
243 xclsuffix.sort() | 270 xclsuffix.sort() |
253 rx = [x.rstrip() for x in rx] | 280 rx = [x.rstrip() for x in rx] |
254 rxcheck = [x.strip() for x in rx if x.strip() > ""] | 281 rxcheck = [x.strip() for x in rx if x.strip() > ""] |
255 assert len(rxcheck) > 0, "Supplied script is empty. Cannot run" | 282 assert len(rxcheck) > 0, "Supplied script is empty. Cannot run" |
256 self.script = "\n".join(rx) | 283 self.script = "\n".join(rx) |
257 fhandle, self.sfile = tempfile.mkstemp( | 284 fhandle, self.sfile = tempfile.mkstemp( |
258 prefix=self.tool_name, suffix="_%s" % (self.args.interpreter_name) | 285 prefix=self.tool_name, suffix="_%s" % (self.executeme) |
259 ) | 286 ) |
260 tscript = open(self.sfile, "w") | 287 tscript = open(self.sfile, "w") |
261 tscript.write(self.script) | 288 tscript.write(self.script) |
262 tscript.close() | 289 tscript.close() |
263 self.indentedScript = " %s" % "\n".join( | 290 self.indentedScript = " %s" % "\n".join([" %s" % html_escape(x) for x in rx]) |
264 [" %s" % html_escape(x) for x in rx] | 291 self.escapedScript = "%s" % "\n".join([" %s" % html_escape(x) for x in rx]) |
265 ) | 292 art = "%s.%s" % (self.tool_name, self.executeme) |
266 self.escapedScript = "%s" % "\n".join( | |
267 [" %s" % html_escape(x) for x in rx] | |
268 ) | |
269 art = "%s.%s" % (self.tool_name, self.args.interpreter_name) | |
270 artifact = open(art, "wb") | 293 artifact = open(art, "wb") |
271 if self.args.interpreter_name == "python": | |
272 artifact.write(bytes("#!/usr/bin/env python\n", "utf8")) | |
273 artifact.write(bytes(self.script, "utf8")) | 294 artifact.write(bytes(self.script, "utf8")) |
274 artifact.close() | 295 artifact.close() |
275 | 296 |
276 def cleanuppar(self): | 297 def cleanuppar(self): |
277 """ positional parameters are complicated by their numeric ordinal""" | 298 """ positional parameters are complicated by their numeric ordinal""" |
375 return ndash | 396 return ndash |
376 | 397 |
377 def doXMLparam(self): | 398 def doXMLparam(self): |
378 """flake8 made me do this...""" | 399 """flake8 made me do this...""" |
379 for p in self.outfiles: | 400 for p in self.outfiles: |
380 newname, newfmt, newcl, oldcl = p | 401 newname, newfmt, newcl, test, oldcl = p |
381 ndash = self.getNdash(newcl) | 402 ndash = self.getNdash(newcl) |
382 aparm = gxtp.OutputData(newcl, format=newfmt, num_dashes=ndash) | 403 aparm = gxtp.OutputData(newcl, format=newfmt, num_dashes=ndash) |
383 aparm.positional = self.is_positional | 404 aparm.positional = self.is_positional |
384 if self.is_positional: | 405 if self.is_positional: |
385 if oldcl == "STDOUT": | 406 if oldcl == "STDOUT": |
387 aparm.command_line_override = "> $%s" % newcl | 408 aparm.command_line_override = "> $%s" % newcl |
388 else: | 409 else: |
389 aparm.positional = int(oldcl) | 410 aparm.positional = int(oldcl) |
390 aparm.command_line_override = "$%s" % newcl | 411 aparm.command_line_override = "$%s" % newcl |
391 self.toutputs.append(aparm) | 412 self.toutputs.append(aparm) |
392 tp = gxtp.TestOutput( | 413 usetest = None |
393 name=newcl, value="%s_sample" % newcl, format=newfmt | 414 ld = None |
394 ) | 415 if test > '': |
416 if test.startswith('diff'): | |
417 usetest = 'diff' | |
418 if test.split(':')[1].isdigit: | |
419 ld = int(test.split(':')[1]) | |
420 else: | |
421 usetest = test | |
422 tp = gxtp.TestOutput(name=newcl, value="%s_sample" % newcl, format=newfmt, | |
423 compare=usetest, lines_diff=ld, delta=None,) | |
395 self.testparam.append(tp) | 424 self.testparam.append(tp) |
396 for p in self.infiles: | 425 for p in self.infiles: |
397 newname = p[ICLPOS] | 426 newname = p[ICLPOS] |
398 newfmt = p[IFMTPOS] | 427 newfmt = p[IFMTPOS] |
399 ndash = self.getNdash(newname) | 428 ndash = self.getNdash(newname) |
455 self.tinputs.append(aparm) | 484 self.tinputs.append(aparm) |
456 self.tparm = gxtp.TestParam(newname, value=newval) | 485 self.tparm = gxtp.TestParam(newname, value=newval) |
457 self.testparam.append(tparm) | 486 self.testparam.append(tparm) |
458 | 487 |
459 def doNoXMLparam(self): | 488 def doNoXMLparam(self): |
489 """filter style package - stdin to stdout""" | |
460 alab = self.infiles[0][ILABPOS] | 490 alab = self.infiles[0][ILABPOS] |
461 if len(alab) == 0: | 491 if len(alab) == 0: |
462 alab = self.infiles[0][ICLPOS] | 492 alab = self.infiles[0][ICLPOS] |
463 max1s = ( | 493 max1s = ( |
464 "Maximum one input if parampass is 0 - more than one input files supplied - %s" | 494 "Maximum one input if parampass is 0 but multiple input files supplied - %s" |
465 % str(self.infiles) | 495 % str(self.infiles) |
466 ) | 496 ) |
467 assert len(self.infiles) == 1, max1s | 497 assert len(self.infiles) == 1, max1s |
468 newname = self.infiles[0][ICLPOS] | 498 newname = self.infiles[0][ICLPOS] |
469 aninput = gxtp.DataParam( | 499 aninput = gxtp.DataParam( |
484 newfmt = self.outfiles[0][OFMTPOS] | 514 newfmt = self.outfiles[0][OFMTPOS] |
485 anout = gxtp.OutputData(newname, format=newfmt, num_dashes=0) | 515 anout = gxtp.OutputData(newname, format=newfmt, num_dashes=0) |
486 anout.command_line_override = "> $%s" % newname | 516 anout.command_line_override = "> $%s" % newname |
487 anout.positional = self.is_positional | 517 anout.positional = self.is_positional |
488 self.toutputs.append(anout) | 518 self.toutputs.append(anout) |
489 tp = gxtp.TestOutput( | 519 tp = gxtp.TestOutput(name=newname, value="%s_sample" % newname, format=newfmt) |
490 name=newname, value="%s_sample" % newname, format=newfmt | |
491 ) | |
492 self.testparam.append(tp) | 520 self.testparam.append(tp) |
493 | 521 |
494 def makeXML(self): | 522 def makeXML(self): |
495 """ | 523 """ |
496 Create a Galaxy xml tool wrapper for the new script | 524 Create a Galaxy xml tool wrapper for the new script |
497 Uses galaxyhtml | 525 Uses galaxyhtml |
498 Hmmm. How to get the command line into correct order... | 526 Hmmm. How to get the command line into correct order... |
499 """ | 527 """ |
500 if self.args.cl_override: | 528 if self.command_override: |
501 self.tool.command_line_override = self.args.cl_override.split(' ') + self.xmlcl | 529 self.tool.command_line_override = self.command_override # config file |
502 else: | 530 else: |
503 self.tool.command_line_override = self.xmlcl | 531 self.tool.command_line_override = self.xmlcl |
504 if self.args.interpreter_name: | 532 # if self.args.interpreter_name: |
505 self.tool.interpreter = self.args.interpreter_name | 533 # self.tool.interpreter = self.args.interpreter_name |
506 if self.args.help_text: | 534 if self.args.help_text: |
507 helptext = open(self.args.help_text, "r").readlines() | 535 helptext = open(self.args.help_text, "r").readlines() |
508 helptext = [html_escape(x) for x in helptext] | 536 helptext = [html_escape(x) for x in helptext] |
509 self.tool.help = "".join([x for x in helptext]) | 537 self.tool.help = "".join([x for x in helptext]) |
510 else: | 538 else: |
513 as none was supplied at tool generation\n" | 541 as none was supplied at tool generation\n" |
514 % (self.args.user_email) | 542 % (self.args.user_email) |
515 ) | 543 ) |
516 self.tool.version_command = None # do not want | 544 self.tool.version_command = None # do not want |
517 requirements = gxtp.Requirements() | 545 requirements = gxtp.Requirements() |
518 | 546 if self.args.packages: |
519 if self.args.interpreter_name: | 547 for d in self.args.packages.split(","): |
520 if self.args.dependencies: | 548 if ":" in d: |
521 for d in self.args.dependencies.split(','): | 549 packg, ver = d.split(":") |
522 requirements.append( | 550 else: |
523 gxtp.Requirement( | 551 packg = d |
524 "package", d, "" | 552 ver = "" |
525 ) | 553 requirements.append(gxtp.Requirement("package", packg.strip(), ver.strip())) |
526 ) | |
527 if self.args.interpreter_name == "python": | |
528 requirements.append( | |
529 gxtp.Requirement( | |
530 "package", "python", self.args.interpreter_version | |
531 ) | |
532 ) | |
533 elif self.args.interpreter_name not in ["bash", "sh"]: | |
534 requirements.append( | |
535 gxtp.Requirement( | |
536 "package", | |
537 self.args.interpreter_name, | |
538 self.args.interpreter_version, | |
539 ) | |
540 ) | |
541 else: | |
542 if self.args.exe_package and self.args.parampass != "system": | |
543 requirements.append( | |
544 gxtp.Requirement( | |
545 "package", | |
546 self.args.exe_package, | |
547 self.args.exe_package_version, | |
548 ) | |
549 ) | |
550 self.tool.requirements = requirements | 554 self.tool.requirements = requirements |
551 if self.args.parampass == "0": | 555 if self.args.parampass == "0": |
552 self.doNoXMLparam() | 556 self.doNoXMLparam() |
553 else: | 557 else: |
554 self.doXMLparam() | 558 self.doXMLparam() |
555 self.tool.outputs = self.toutputs | 559 self.tool.outputs = self.toutputs |
556 self.tool.inputs = self.tinputs | 560 self.tool.inputs = self.tinputs |
557 if self.args.runmode not in ["Executable", "system"]: | 561 if ( |
562 self.args.script_path | |
563 ): | |
558 configfiles = gxtp.Configfiles() | 564 configfiles = gxtp.Configfiles() |
559 configfiles.append(gxtp.Configfile(name="runMe", text=self.script)) | 565 configfiles.append(gxtp.Configfile(name="runme", text=self.script)) |
560 self.tool.configfiles = configfiles | 566 self.tool.configfiles = configfiles |
561 tests = gxtp.Tests() | 567 tests = gxtp.Tests() |
562 test_a = gxtp.Test() | 568 test_a = gxtp.Test() |
563 for tp in self.testparam: | 569 for tp in self.testparam: |
564 test_a.append(tp) | 570 test_a.append(tp) |
571 self.tool.add_comment("Source in git at: %s" % (toolFactoryURL)) | 577 self.tool.add_comment("Source in git at: %s" % (toolFactoryURL)) |
572 self.tool.add_comment( | 578 self.tool.add_comment( |
573 "Cite: Creating re-usable tools from scripts doi: \ | 579 "Cite: Creating re-usable tools from scripts doi: \ |
574 10.1093/bioinformatics/bts573" | 580 10.1093/bioinformatics/bts573" |
575 ) | 581 ) |
576 exml = self.tool.export() | 582 exml0 = self.tool.export() |
577 xf = open('%s.xml' % self.tool_name, "w") | 583 exml = exml0.replace(FAKEEXE, "") # temporary work around until PR accepted |
584 if self.test_override: # cannot do this inside galaxyxml as it expects lxml objects for tests | |
585 part1 = exml.split('<tests>')[0] | |
586 part2 = exml.split('</tests>')[1] | |
587 fixed = '%s\n%s\n%s' % (part1,self.test_override,part2) | |
588 exml = fixed | |
589 xf = open("%s.xml" % self.tool_name, "w") | |
578 xf.write(exml) | 590 xf.write(exml) |
579 xf.write("\n") | 591 xf.write("\n") |
580 xf.close() | 592 xf.close() |
581 # ready for the tarball | 593 # ready for the tarball |
582 | 594 |
583 def makeTooltar(self): | 595 def makeTool(self): |
584 """ | 596 """write xmls and samples into place |
585 a tool is a gz tarball with eg | 597 """ |
586 /toolname/tool.xml /toolname/tool.py /toolname/test-data/test1_in.foo ... | |
587 NOTE names for test inputs and outputs are munged here so must | |
588 correspond to actual input and output names used on the generated cl | |
589 """ | |
590 retval = self.run() | |
591 if retval: | |
592 sys.stderr.write( | |
593 "## Run failed. Cannot build yet. Please fix and retry" | |
594 ) | |
595 sys.exit(1) | |
596 self.makeXML() | 598 self.makeXML() |
599 if self.args.script_path: | |
600 stname = os.path.join(self.tooloutdir, "%s" % (self.sfile)) | |
601 if not os.path.exists(stname): | |
602 shutil.copyfile(self.sfile, stname) | |
603 xreal = "%s.xml" % self.tool_name | |
604 xout = os.path.join(self.tooloutdir, xreal) | |
605 shutil.copyfile(xreal, xout) | |
597 for p in self.infiles: | 606 for p in self.infiles: |
598 pth = p[IPATHPOS] | 607 pth = p[IPATHPOS] |
599 dest = os.path.join(self.testdir, "%s_sample" % p[ICLPOS]) | 608 dest = os.path.join(self.testdir, "%s_sample" % p[ICLPOS]) |
600 shutil.copyfile(pth, dest) | 609 shutil.copyfile(pth, dest) |
601 for p in self.outfiles: | 610 |
602 pth = p[OCLPOS] | 611 def makeToolTar(self): |
603 if p[OOCLPOS] == "STDOUT" or self.args.parampass == "0": | |
604 pth = p[ONAMEPOS] | |
605 dest = os.path.join(self.testdir, "%s_sample" % p[ONAMEPOS]) | |
606 shutil.copyfile(pth, dest) | |
607 dest = os.path.join(self.tooloutdir, p[ONAMEPOS]) | |
608 shutil.copyfile(pth, dest) | |
609 else: | |
610 pth = p[OCLPOS] | |
611 dest = os.path.join(self.testdir, "%s_sample" % p[OCLPOS]) | |
612 shutil.copyfile(pth, dest) | |
613 dest = os.path.join(self.tooloutdir, p[OCLPOS]) | |
614 shutil.copyfile(pth, dest) | |
615 if os.path.exists(self.tlog) and os.stat(self.tlog).st_size > 0: | |
616 shutil.copyfile(self.tlog, os.path.join(self.tooloutdir, "test1_log_outfiletxt")) | |
617 if self.args.runmode not in ["Executable", "system"]: | |
618 stname = os.path.join(self.tooloutdir, "%s" % (self.sfile)) | |
619 if not os.path.exists(stname): | |
620 shutil.copyfile(self.sfile, stname) | |
621 xreal = '%s.xml' % self.tool_name | |
622 xout = os.path.join(self.tooloutdir,xreal) | |
623 shutil.copyfile(xreal, xout) | |
624 self.newtarpath = "toolfactory_%s.tgz" % self.tool_name | 612 self.newtarpath = "toolfactory_%s.tgz" % self.tool_name |
625 tf = tarfile.open(self.newtarpath, "w:gz") | 613 tf = tarfile.open(self.newtarpath, "w:gz") |
626 tf.add(name=self.tooloutdir, arcname=self.tool_name) | 614 tf.add(name=self.tooloutdir, arcname=self.tool_name) |
627 tf.close() | 615 tf.close() |
628 shutil.copyfile(self.newtarpath, self.args.new_tool) | 616 shutil.copyfile(self.newtarpath, self.args.new_tool) |
629 shutil.copyfile(xreal,"tool_xml.txt") | 617 if os.path.exists(self.tlog) and os.stat(self.tlog).st_size > 0: |
630 repoutnames = [x[OCLPOS] for x in self.outfiles] | 618 shutil.copyfile( |
631 with os.scandir('.') as outs: | 619 self.tlog, os.path.join(self.tooloutdir, "test1_log_outfiletxt") |
620 ) | |
621 | |
622 def moveRunOutputs(self): | |
623 """need to move files into toolfactory collection after any run - planemo or not | |
624 """ | |
625 for p in self.outfiles: | |
626 naym = p[ONAMEPOS] | |
627 src = os.path.join(self.tooloutdir,naym) | |
628 if os.path.isfile(src): | |
629 dest = os.path.join(self.testdir, "%s_sample" % naym) | |
630 shutil.copyfile(naym, dest) | |
631 else: | |
632 print('### problem - output file %s not found in tooloutdir %s' % (src,self.tooloutdir)) | |
633 with os.scandir(self.tooloutdir) as outs: | |
632 for entry in outs: | 634 for entry in outs: |
633 if entry.name.endswith('.tgz') or not entry.is_file(): | 635 if not entry.is_file() or entry.name.startswith('.'): |
634 continue | 636 continue |
635 if entry.name in repoutnames: | 637 if "." in entry.name: |
636 if '.' in entry.name: | 638 nayme,ext = os.path.splitext(entry.name) |
637 ofne = os.path.splitext(entry.name)[1] | 639 else: |
638 else: | 640 ext = ".txt" |
639 ofne = '.txt' | 641 ofn = "%s%s" % (entry.name.replace(".", "_"), ext) |
640 ofn = '%s%s' % (entry.name.replace('.','_'),ofne) | 642 dest = os.path.join(self.repdir, ofn) |
641 shutil.copyfile(entry.name,os.path.join(self.repdir,ofn)) | 643 src = os.path.join(self.tooloutdir,entry.name) |
642 elif entry.name == "%s.xml" % self.tool_name: | 644 shutil.copyfile(src, dest) |
643 shutil.copyfile(entry.name,os.path.join(self.repdir,"new_tool_xml.xml")) | |
644 return retval | |
645 | 645 |
646 def run(self): | 646 def run(self): |
647 """ | 647 """ |
648 | 648 |
649 """ | 649 """ |
653 scl = " ".join(self.cl) | 653 scl = " ".join(self.cl) |
654 err = None | 654 err = None |
655 if self.args.parampass != "0": | 655 if self.args.parampass != "0": |
656 ste = open(self.elog, "wb") | 656 ste = open(self.elog, "wb") |
657 if self.lastclredirect: | 657 if self.lastclredirect: |
658 sto = open( | 658 sto = open(self.lastclredirect[1], "wb") # is name of an output file |
659 self.lastclredirect[1], "wb" | |
660 ) # is name of an output file | |
661 else: | 659 else: |
662 sto = open(self.tlog, "wb") | 660 sto = open(self.tlog, "wb") |
663 sto.write( | 661 sto.write( |
664 bytes( | 662 bytes( |
665 "## Executing Toolfactory generated command line = %s\n" | 663 "## Executing Toolfactory generated command line = %s\n" % scl, |
666 % scl, | |
667 "utf8", | 664 "utf8", |
668 ) | 665 ) |
669 ) | 666 ) |
670 sto.flush() | 667 sto.flush() |
671 p = subprocess.run(self.cl, shell=False, stdout=sto, stderr=ste) | 668 p = subprocess.run(self.cl, shell=False, stdout=sto, stderr=ste) |
681 break | 678 break |
682 except OverflowError: | 679 except OverflowError: |
683 pass | 680 pass |
684 tmp_stderr.close() | 681 tmp_stderr.close() |
685 retval = p.returncode | 682 retval = p.returncode |
686 else: # work around special case of simple scripts that take stdin and write to stdout | 683 else: # work around special case - stdin and write to stdout |
687 sti = open(self.infiles[0][IPATHPOS], "rb") | 684 sti = open(self.infiles[0][IPATHPOS], "rb") |
688 sto = open(self.outfiles[0][ONAMEPOS], "wb") | 685 sto = open(self.outfiles[0][ONAMEPOS], "wb") |
689 # must use shell to redirect | 686 # must use shell to redirect |
690 p = subprocess.run(self.cl, shell=False, stdout=sto, stdin=sti) | 687 p = subprocess.run(self.cl, shell=False, stdout=sto, stdin=sti) |
691 retval = p.returncode | 688 retval = p.returncode |
698 if retval != 0 and err: # problem | 695 if retval != 0 and err: # problem |
699 sys.stderr.write(err) | 696 sys.stderr.write(err) |
700 logging.debug("run done") | 697 logging.debug("run done") |
701 return retval | 698 return retval |
702 | 699 |
703 def install_load(self): | |
704 testres = self.planemo_test() | |
705 if testres == 0: | |
706 self.planemo_shedload() | |
707 self,eph_galaxy_load() | |
708 else: | |
709 stderr.write('Planemo test failed - tool %s was not installed' % self.args.tool_name) | |
710 | |
711 | |
712 def planemo_shedload(self): | 700 def planemo_shedload(self): |
713 """ | 701 """ |
714 planemo shed_create --shed_target testtoolshed | 702 planemo shed_create --shed_target testtoolshed |
715 planemo shed_update --check_diff --shed_target testtoolshed | 703 planemo shed_update --check_diff --shed_target testtoolshed |
716 """ | 704 """ |
717 cll = ['planemo', 'shed_create', '--shed_target','local'] | 705 if os.path.exists(self.tlog): |
718 p = subprocess.run(cll, shell=False,cwd=self.tooloutdir ) | 706 tout = open(self.tlog,'a') |
707 else: | |
708 tout = open(self.tlog,'w') | |
709 cll = ["planemo", "shed_create", "--shed_target", "local"] | |
710 try: | |
711 p = subprocess.run(cll, shell=False, cwd=self.tooloutdir, stdout=tout, stderr = tout) | |
712 except: | |
713 pass | |
719 if p.returncode != 0: | 714 if p.returncode != 0: |
720 print('Repository %s exists' % self.args.tool_name) | 715 print("Repository %s exists" % self.args.tool_name) |
721 else: | 716 else: |
722 print('initiated %s' % self.args.tool_name) | 717 print("initiated %s" % self.args.tool_name) |
723 cll = ['planemo', 'shed_upload', '--shed_target','local','-r','--owner','fubar', | 718 cll = [ |
724 '--name',self.args.tool_name,'--shed_key',self.args.toolshed_api_key,'--tar',self.newtarpath] | 719 "planemo", |
725 p = subprocess.run(cll, shell=False,cwd=self.tooloutdir) | 720 "shed_upload", |
726 print('Ran',' '.join(cll),'got',p.returncode) | 721 "--shed_target", |
722 "local", | |
723 "--owner", | |
724 "fubar", | |
725 "--name", | |
726 self.args.tool_name, | |
727 "--shed_key", | |
728 self.args.toolshed_api_key, | |
729 "--tar", | |
730 self.newtarpath, | |
731 ] | |
732 print("Run", " ".join(cll)) | |
733 p = subprocess.run(cll, shell=False) | |
734 print("Ran", " ".join(cll), "got", p.returncode) | |
735 tout.close() | |
727 return p.returncode | 736 return p.returncode |
728 | 737 |
729 | 738 def planemo_test(self, genoutputs=True): |
730 def planemo_test(self): | |
731 """planemo is a requirement so is available | 739 """planemo is a requirement so is available |
732 """ | 740 """ |
733 cll = ['planemo', 'test', '--galaxy_root', self.args.galaxy_root, | 741 xreal = "%s.xml" % self.tool_name |
734 self.args.tool_name] | 742 if os.path.exists(self.tlog): |
735 p = subprocess.run(cll, shell=False) | 743 tout = open(self.tlog,'a') |
736 ols = os.listdir('.') | 744 else: |
737 for fn in ols: | 745 tout = open(self.tlog,'w') |
738 if '.' in fn: | 746 if genoutputs: |
739 ofne = os.path.splitext(fn)[1] | 747 cll = [ |
740 else: | 748 "planemo", |
741 ofne = '.txt' | 749 "test", |
742 ofn = '%s%s' % (fn.replace('.','_'),ofne) | 750 "--galaxy_root", |
743 shutil.copyfile(fn,os.path.join(self.repdir,ofn)) | 751 self.args.galaxy_root, |
752 "--update_test_data", | |
753 xreal, | |
754 ] | |
755 else: | |
756 cll = ["planemo", "test", "--galaxy_root", self.args.galaxy_root, xreal] | |
757 try: | |
758 p = subprocess.run(cll, shell=False, cwd=self.tooloutdir, stderr=tout, stdout=tout) | |
759 except: | |
760 pass | |
761 tout.close() | |
744 return p.returncode | 762 return p.returncode |
745 | 763 |
746 | |
747 def eph_galaxy_load(self): | 764 def eph_galaxy_load(self): |
748 """ | 765 """ |
749 """ | 766 """ |
750 cll = ['shed-tools', 'install', '-g', self.args.galaxy_url, '--latest', | 767 if os.path.exists(self.tlog): |
751 '-a', self.args.galaxy_api_key , '--name', self.args.tool_name, '--owner','fubar', | 768 tout = open(self.tlog,'a') |
752 '--toolshed', self.args.toolshed_url, | 769 else: |
753 '--section_label','Generated Tools','--install_tool_dependencies',] | 770 tout = open(self.tlog,'w') |
754 print('running\n',' '.join(cll)) | 771 cll = [ |
755 p = subprocess.run(cll, shell=False) | 772 "shed-tools", |
773 "install", | |
774 "-g", | |
775 self.args.galaxy_url, | |
776 "--latest", | |
777 "-a", | |
778 self.args.galaxy_api_key, | |
779 "--name", | |
780 self.args.tool_name, | |
781 "--owner", | |
782 "fubar", | |
783 "--toolshed", | |
784 self.args.toolshed_url, | |
785 "--section_label", | |
786 "Generated Tools", | |
787 "--install_tool_dependencies", | |
788 ] | |
789 print("running\n", " ".join(cll)) | |
790 p = subprocess.run(cll, shell=False, stderr=tout, stdout=tout) | |
756 if p.returncode != 0: | 791 if p.returncode != 0: |
757 print('Repository %s installation returned %d' % (self.args.tool_name,p.retcode)) | 792 print( |
758 else: | 793 "Repository %s installation returned %d" |
759 print('installed %s' % self.args.tool_name) | 794 % (self.args.tool_name, p.returncode) |
795 ) | |
796 else: | |
797 print("installed %s" % self.args.tool_name) | |
798 tout.close() | |
760 return p.returncode | 799 return p.returncode |
761 | 800 |
762 def writeShedyml(self): | 801 def writeShedyml(self): |
763 yuser = self.args.user_email.split('@')[0] | 802 yuser = self.args.user_email.split("@")[0] |
764 yfname = os.path.join(self.tooloutdir,'.shed.yml') | 803 yfname = os.path.join(self.tooloutdir, ".shed.yml") |
765 yamlf = open(yfname, 'w') | 804 yamlf = open(yfname, "w") |
766 odict = {'name':self.tool_name,'owner':yuser,'type':'unrestricted','description':self.args.tool_desc} | 805 odict = { |
806 "name": self.tool_name, | |
807 "owner": yuser, | |
808 "type": "unrestricted", | |
809 "description": self.args.tool_desc, | |
810 } | |
767 yaml.dump(odict, yamlf, allow_unicode=True) | 811 yaml.dump(odict, yamlf, allow_unicode=True) |
768 yamlf.close() | 812 yamlf.close() |
813 | |
814 | |
815 def install_load(self): | |
816 _ = self.planemo_test(genoutputs=True) | |
817 testres = self.planemo_test(genoutputs=False) | |
818 if testres == 0: | |
819 if self.args.make_Tool == "install": | |
820 self.planemo_shedload() | |
821 self.eph_galaxy_load() | |
822 else: | |
823 os.stderr.write( | |
824 "Planemo test failed - tool %s was not installed" % self.args.tool_name | |
825 ) | |
769 | 826 |
770 def main(): | 827 def main(): |
771 """ | 828 """ |
772 This is a Galaxy wrapper. It expects to be called by a special purpose tool.xml as: | 829 This is a Galaxy wrapper. It expects to be called by a special purpose tool.xml as: |
773 <command interpreter="python">rgBaseScriptWrapper.py --script_path "$scriptPath" --tool_name "foo" --interpreter "Rscript" | 830 <command interpreter="python">rgBaseScriptWrapper.py --script_path "$scriptPath" |
831 --tool_name "foo" --interpreter "Rscript" | |
774 </command> | 832 </command> |
775 """ | 833 """ |
776 parser = argparse.ArgumentParser() | 834 parser = argparse.ArgumentParser() |
777 a = parser.add_argument | 835 a = parser.add_argument |
778 a("--script_path", default="") | 836 a("--script_path", default=None) |
779 a("--dependencies", default="") | 837 a("--history_test", default=None) |
780 a("--cl_override", default="") | 838 a("--cl_prefix", default=None) |
839 a("--sysexe", default=None) | |
840 a("--packages", default=None) | |
781 a("--tool_name", default=None) | 841 a("--tool_name", default=None) |
782 a("--interpreter_name", default=None) | |
783 a("--interpreter_version", default=None) | |
784 a("--exe_package", default=None) | |
785 a("--exe_package_version", default=None) | |
786 a("--input_files", default=[], action="append") | 842 a("--input_files", default=[], action="append") |
787 a("--output_files", default=[], action="append") | 843 a("--output_files", default=[], action="append") |
788 a("--user_email", default="Unknown") | 844 a("--user_email", default="Unknown") |
789 a("--bad_user", default=None) | 845 a("--bad_user", default=None) |
790 a("--make_Tool", default=None) | 846 a("--make_Tool", default="runonly") |
791 a("--help_text", default=None) | 847 a("--help_text", default=None) |
792 a("--tool_desc", default=None) | 848 a("--tool_desc", default=None) |
793 a("--tool_version", default=None) | 849 a("--tool_version", default=None) |
794 a("--citations", default=None) | 850 a("--citations", default=None) |
851 a("--command_override", default=None) | |
852 a("--test_override", default=None) | |
795 a("--additional_parameters", action="append", default=[]) | 853 a("--additional_parameters", action="append", default=[]) |
796 a("--edit_additional_parameters", action="store_true", default=False) | 854 a("--edit_additional_parameters", action="store_true", default=False) |
797 a("--parampass", default="positional") | 855 a("--parampass", default="positional") |
798 a("--tfout", default="./tfout") | 856 a("--tfout", default="./tfout") |
799 a("--new_tool", default="new_tool") | 857 a("--new_tool", default="new_tool") |
800 a("--runmode", default=None) | 858 a("--runmode", default=None) |
801 a("--galaxy_url", default="http://localhost") | 859 a("--galaxy_url", default="http://localhost:8080") |
802 a("--galaxy_api_key", default='fakekey') | 860 a("--galaxy_api_key", default="fbdd3c2eecd191e88939fffc02eeeaf8") |
803 a("--toolshed_url", default="http://localhost:9009") | 861 a("--toolshed_url", default="http://localhost:9009") |
804 a("--toolshed_api_key", default=None) | 862 a("--toolshed_api_key", default="d46e5ed0e242ed52c6e1f506b5d7f9f7") |
805 a("--planemo_test", default="yes") | 863 a("--galaxy_root", default="/home/ross/galaxy") |
806 | 864 |
807 args = parser.parse_args() | 865 args = parser.parse_args() |
808 assert not args.bad_user, ( | 866 assert not args.bad_user, ( |
809 'UNAUTHORISED: %s is NOT authorized to use this tool until Galaxy admin adds %s to "admin_users" in the Galaxy configuration file' | 867 'UNAUTHORISED: %s is NOT authorized to use this tool until Galaxy admin adds %s to "admin_users" in the Galaxy configuration file' |
810 % (args.bad_user, args.bad_user) | 868 % (args.bad_user, args.bad_user) |
811 ) | 869 ) |
870 assert args.tool_name, "## Tool Factory expects a tool name - eg --tool_name=DESeq" | |
812 assert ( | 871 assert ( |
813 args.tool_name | 872 args.sysexe or args.packages |
814 ), "## Tool Factory expects a tool name - eg --tool_name=DESeq" | |
815 assert ( | |
816 args.interpreter_name or args.exe_package | |
817 ), "## Tool Factory wrapper expects an interpreter or an executable package" | 873 ), "## Tool Factory wrapper expects an interpreter or an executable package" |
818 assert args.exe_package or ( | 874 args.input_files = [x.replace('"', "").replace("'", "") for x in args.input_files] |
819 len(args.script_path) > 0 and os.path.isfile(args.script_path) | |
820 ), "## Tool Factory wrapper expects a script path - eg --script_path=foo.R if no executable" | |
821 args.input_files = [ | |
822 x.replace('"', "").replace("'", "") for x in args.input_files | |
823 ] | |
824 # remove quotes we need to deal with spaces in CL params | 875 # remove quotes we need to deal with spaces in CL params |
825 for i, x in enumerate(args.additional_parameters): | 876 for i, x in enumerate(args.additional_parameters): |
826 args.additional_parameters[i] = args.additional_parameters[i].replace( | 877 args.additional_parameters[i] = args.additional_parameters[i].replace('"', "") |
827 '"', "" | |
828 ) | |
829 r = ScriptRunner(args) | 878 r = ScriptRunner(args) |
830 if args.make_Tool: | 879 r.writeShedyml() |
831 r.writeShedyml() | 880 r.makeTool() |
832 retcode = r.makeTooltar() | 881 if args.make_Tool == "runonly": |
833 if retcode == 0: | 882 retcode = r.run() |
834 if args.planemo_test == "yes": | 883 if retcode: |
835 r.install_load() | 884 sys.stderr.write( |
885 "## Run failed with return code %d. Cannot build yet. Please fix and retry" | |
886 % retcode | |
887 ) | |
888 sys.exit(1) | |
889 else: | |
890 r.moveRunOutputs() | |
891 elif args.make_Tool in ["gentestinstall", "generate", "gentest"]: | |
892 retcode = r.run() | |
893 if retcode: | |
894 sys.stderr.write( | |
895 "## Run failed with return code %d. Cannot build yet. Please fix and retry" | |
896 % retcode | |
897 ) | |
898 sys.exit(1) | |
899 r.moveRunOutputs() | |
900 r.makeToolTar() | |
901 if args.make_Tool in ["gentestinstall","gentest"]: | |
902 r.planemo_test(genoutputs=False) | |
903 r.moveRunOutputs() | |
904 r.planemo_shedload() | |
905 r.eph_galaxy_load() | |
836 else: | 906 else: |
837 retcode = r.run() | 907 retcode = r.planemo_test(genoutputs=True) # this fails :( |
838 # if retcode: | 908 r.moveRunOutputs() |
909 r.makeToolTar() | |
910 retcode = r.planemo_test(genoutputs=False) | |
911 r.moveRunOutputs() | |
912 if args.make_Tool == "planemotestinstall": | |
913 r.planemo_shedload() | |
914 r.eph_galaxy_load() | |
915 # if retcode: | |
839 # sys.exit(retcode) # indicate failure to job runner | 916 # sys.exit(retcode) # indicate failure to job runner |
840 | 917 |
841 | 918 |
842 if __name__ == "__main__": | 919 if __name__ == "__main__": |
843 main() | 920 main() |