comparison toolfactory/ToolFactory_tester.xml @ 4:2a46da701dde draft

Uploaded
author fubar
date Mon, 26 Apr 2021 05:25:26 +0000
parents
children efefe43f23c8
comparison
equal deleted inserted replaced
3:c4f192ec521c 4:2a46da701dde
1 <tool name="toolfactory_tester" id="toolfactory_tester" version="1">
2 <!--Source in git at: https://github.com/fubar2/toolfactory-->
3 <!--Created by admin@galaxy.org at 23/04/2021 10:25:58 using the Galaxy Tool Factory.-->
4 <description>Test an untested tool and update it</description>
5 <requirements>
6 <requirement type="package" version="2.30.2">git</requirement>
7 <requirement type="package" version="0.74.3">planemo</requirement> </requirements>
8 <stdio>
9 <exit_code range="1:" level="fatal"/>
10 </stdio>
11 <version_command><![CDATA[echo "1"]]></version_command>
12 <command><![CDATA[
13 python
14 $runme
15 --in_tool_archive
16 $in_tool_archive
17 --new_tested_tool_archive
18 $new_tested_tool_archive
19 --galaxy_root
20 "$galaxyroot"
21 >
22 $tf_archive_tester_log;
23 ]]></command>
24 <configfiles>
25 <configfile name="runme"><![CDATA[#raw
26
27 # see https://github.com/fubar2/toolfactory
28 #
29 # copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012
30 #
31 # all rights reserved
32 # Licensed under the LGPL
33 # suggestions for improvement and bug fixes welcome at
34 # https://github.com/fubar2/toolfactory
35 #
36 # July 2020: BCC was fun and I feel like rip van winkle after 5 years.
37 # Decided to
38 # 1. Fix the toolfactory so it works - done for simplest case
39 # 2. Fix planemo so the toolfactory function works
40 # 3. Rewrite bits using galaxyxml functions where that makes sense - done
41
42 import argparse
43 import copy
44 import os
45 import subprocess
46 import shutil
47 import sys
48 import tarfile
49 import tempfile
50 import time
51 import xml.etree.ElementTree as ET
52
53
54 myversion = "V2.2 April 2021"
55 verbose = True
56 debug = True
57 toolFactoryURL = "https://github.com/fubar2/toolfactory"
58
59 def timenow():
60 """return current time as a string"""
61 return time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(time.time()))
62
63 class ToolTester():
64 # requires highly insecure docker settings - like write to tool_conf.xml and to tools !
65 # if in a container possibly not so courageous.
66 # Fine on your own laptop but security red flag for most production instances
67 # uncompress passed tar, run planemo and rebuild a new tarball with tests
68
69 def __init__(self, args=None, in_tool_archive='/galaxy-central/tools/newtool/newtool_toolshed.gz', new_tool_archive=None):
70 self.args = args
71 self.new_tool_archive = new_tool_archive
72 assert tarfile.is_tarfile(in_tool_archive)
73 # this is not going to go well with arbitrary names.
74 tff = tarfile.open(in_tool_archive, "r:*")
75 flist = tff.getnames()
76 ourdir = os.path.commonpath(flist) # eg pyrevpos
77 self.tool_name = ourdir
78 ourxmls = [x for x in flist if x.lower().endswith('.xml') and os.path.split(x)[0] == ourdir]
79 # planemo_test/planemo_test.xml
80 assert len(ourxmls) > 0
81 self.ourxmls = ourxmls # [os.path.join(tool_path,x) for x in ourxmls]
82 res = tff.extractall()
83 self.update_tests(ourdir)
84 tff.close()
85 self.tooloutdir = "./tfout"
86 self.repdir = "./TF_run_report"
87 self.testdir = os.path.join(self.tooloutdir, "test-data")
88 if not os.path.exists(self.tooloutdir):
89 os.mkdir(self.tooloutdir)
90 if not os.path.exists(self.testdir):
91 os.mkdir(self.testdir)
92 if not os.path.exists(self.repdir):
93 os.mkdir(self.repdir)
94 self.makeTool()
95 self.moveRunOutputs()
96 self.makeToolTar()
97
98 def call_planemo(self,xmlpath,ourdir):
99 penv = os.environ
100 #penv['HOME'] = os.path.join(self.args.galaxy_root,'planemo')
101 #penv["GALAXY_VIRTUAL_ENV"] = os.path.join(penv['HOME'],'.planemo','gx_venv_3.9')
102 penv["PIP_CACHE_DIR"] = os.path.join(self.args.galaxy_root,'pipcache')
103 toolfile = os.path.split(xmlpath)[1]
104 tool_name = self.tool_name
105 tool_test_output = f"{tool_name}_planemo_test_report.html"
106 cll = [
107 "planemo",
108 "test",
109 "--biocontainers",
110 "--test_output",
111 os.path.abspath(tool_test_output),
112 "--galaxy_root",
113 self.args.galaxy_root,
114 "--update_test_data",
115 os.path.abspath(xmlpath),
116 ]
117 print(cll)
118 p = subprocess.run(
119 cll,
120 #capture_output=True,
121 encoding='utf8',
122 env = penv,
123 shell=False,
124 )
125 return p
126
127 def makeTool(self):
128 """write xmls and input samples into place"""
129 for xreal in self.ourxmls:
130 x = os.path.split(xreal)[1]
131 xout = os.path.join(self.tooloutdir,x)
132 shutil.copyfile(xreal, xout)
133 # for p in self.infiles:
134 # pth = p["name"]
135 # dest = os.path.join(self.testdir, "%s_sample" % p["infilename"])
136 # shutil.copyfile(pth, dest)
137 # dest = os.path.join(self.repdir, "%s_sample" % p["infilename"])
138 # shutil.copyfile(pth, dest)
139
140 def makeToolTar(self):
141 """move outputs into test-data and prepare the tarball"""
142 excludeme = "_planemo_test_report.html"
143
144 def exclude_function(tarinfo):
145 filename = tarinfo.name
146 return None if filename.endswith(excludeme) else tarinfo
147
148 newtar = 'new_%s_toolshed.gz' % self.tool_name
149 ttf = tarfile.open(newtar, "w:gz")
150 ttf.add(name=self.tool_name,
151 arcname=self.tool_name,
152 filter=exclude_function)
153 ttf.close()
154 shutil.copyfile(newtar, self.new_tool_archive)
155
156 def moveRunOutputs(self):
157 """need to move planemo or run outputs into toolfactory collection"""
158 with os.scandir(self.tooloutdir) as outs:
159 for entry in outs:
160 if not entry.is_file():
161 continue
162 if "." in entry.name:
163 _, ext = os.path.splitext(entry.name)
164 if ext in [".tgz", ".json"]:
165 continue
166 if ext in [".yml", ".xml", ".yaml"]:
167 newname = f"{entry.name.replace('.','_')}.txt"
168 else:
169 newname = entry.name
170 else:
171 newname = f"{entry.name}.txt"
172 dest = os.path.join(self.repdir, newname)
173 src = os.path.join(self.tooloutdir, entry.name)
174 shutil.copyfile(src, dest)
175 with os.scandir('.') as outs:
176 for entry in outs:
177 if not entry.is_file():
178 continue
179 if "." in entry.name:
180 _, ext = os.path.splitext(entry.name)
181 if ext in [".yml", ".xml", ".yaml"]:
182 newname = f"{entry.name.replace('.','_')}.txt"
183 else:
184 newname = entry.name
185 else:
186 newname = f"{entry.name}.txt"
187 dest = os.path.join(self.repdir, newname)
188 src =entry.name
189 shutil.copyfile(src, dest)
190 if True or self.args.include_tests:
191 with os.scandir(self.testdir) as outs:
192 for entry in outs:
193 if (not entry.is_file()) or entry.name.endswith(
194 "_planemo_test_report.html"
195 ):
196 continue
197 if "." in entry.name:
198 _, ext = os.path.splitext(entry.name)
199 if ext in [".tgz", ".json"]:
200 continue
201 if ext in [".yml", ".xml", ".yaml"]:
202 newname = f"{entry.name.replace('.','_')}.txt"
203 else:
204 newname = entry.name
205 else:
206 newname = f"{entry.name}.txt"
207 dest = os.path.join(self.repdir, newname)
208 src = os.path.join(self.testdir, entry.name)
209 shutil.copyfile(src, dest)
210
211
212 def update_tests(self,ourdir):
213 for xmlf in self.ourxmls:
214 capture = self.call_planemo(xmlf,ourdir)
215 #sys.stderr.write('%s, stdout=%s, stderr=%s' % (xmlf, capture.stdout, capture.stdout))
216 #print('%s, stdout=%s, stderr=%s' % (capture.stdout, capture.stdout,xmlf))
217
218 def main():
219 """
220 This is a Galaxy wrapper.
221 It expects to be called by a special purpose tool.xml
222
223 """
224 parser = argparse.ArgumentParser()
225 a = parser.add_argument
226 a("--in_tool_archive", default=None)
227 a("--new_tested_tool_archive", default=None)
228 a("--galaxy_root", default="/home/ross/gal21/")
229 args = parser.parse_args()
230 print('Hello from',os.getcwd())
231 tt = ToolTester(args=args, in_tool_archive=args.in_tool_archive, new_tool_archive=args.new_tested_tool_archive)
232
233 if __name__ == "__main__":
234 main()
235
236
237 #end raw]]></configfile>
238 </configfiles>
239 <inputs>
240 <param name="new_tool_name" value="" type="hidden"/>
241 <param name="in_tool_archive" type="data" optional="false" label="Select a no_test tarfile to test and update for a toolshed" help="" format="toolshed.gz" multiple="false"/>
242 <param name="galaxyroot" type="text" value="/home/ross/gal21" label="Galaxy root for planemo to use - MUST be made available in the Galaxy job runner configuration" help=""/>
243 </inputs>
244 <outputs>
245 <data name="new_tested_tool_archive" format="toolshed.gz" label="${in_tool_archive.name.split('_')[0]}_tested_toolshed.gz" hidden="false"/>
246 <data name="tf_archive_tester_log" format="txt" label="${in_tool_archive.name}_test_log" hidden="false"/>
247 <collection name="TF_run_report" type="list" label="${in_tool_archive.name} test Run reports">
248 <discover_datasets pattern="__name_and_ext__" directory="TF_run_report" visible="false"/>
249 </collection>
250 </outputs>
251 <tests>
252 <test>
253 <output name="new_tested_tool_archive" value="new_tested_tool_archive_sample" compare="sim_size" delta_frac="0.5"/>
254 <output name="tf_archive_tester_log" value="tf_archive_tester_log_sample" compare="sim_size" delta_frac="0.1"/>
255 <param name="in_tool_archive" value="in_tool_archive_sample"/>
256 <param name="galaxyroot" value="/home/ross/gal21"/>
257 <output_collection name="TF_run_report"/>
258 </test>
259 </tests>
260 <help><![CDATA[
261
262 **What it Does**
263
264 ------
265
266 Script::
267
268 import argparse
269 import copy
270 import os
271 import subprocess
272 import shutil
273 import sys
274 import tarfile
275 import tempfile
276 import time
277 import xml.etree.ElementTree as ET
278 myversion = "V2.2 April 2021"
279 verbose = True
280 debug = True
281 toolFactoryURL = "https://github.com/fubar2/toolfactory"
282 def timenow():
283 """return current time as a string"""
284 return time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(time.time()))
285
286 class ToolTester():
287 # requires highly insecure docker settings - like write to tool_conf.xml and to tools !
288 # if in a container possibly not so courageous.
289 # Fine on your own laptop but security red flag for most production instances
290 # uncompress passed tar, run planemo and rebuild a new tarball with tests
291 def __init__(self, args=None, in_tool_archive='/galaxy-central/tools/newtool/newtool_toolshed.gz', new_tool_archive=None):
292 self.args = args
293 self.new_tool_archive = new_tool_archive
294 assert tarfile.is_tarfile(in_tool_archive)
295 # this is not going to go well with arbitrary names.
296 tff = tarfile.open(in_tool_archive, "r:*")
297 flist = tff.getnames()
298 ourdir = os.path.commonpath(flist) # eg pyrevpos
299 self.tool_name = ourdir
300 ourxmls = [x for x in flist if x.lower().endswith('.xml') and os.path.split(x)[0] == ourdir]
301 # planemo_test/planemo_test.xml
302 assert len(ourxmls) > 0
303 self.ourxmls = ourxmls # [os.path.join(tool_path,x) for x in ourxmls]
304 res = tff.extractall()
305 self.update_tests(ourdir)
306 tff.close()
307 self.tooloutdir = "./tfout"
308 self.repdir = "./TF_run_report"
309 self.testdir = os.path.join(self.tooloutdir, "test-data")
310 if not os.path.exists(self.tooloutdir):
311 os.mkdir(self.tooloutdir)
312 if not os.path.exists(self.testdir):
313 os.mkdir(self.testdir)
314 if not os.path.exists(self.repdir):
315 os.mkdir(self.repdir)
316 self.makeTool()
317 self.moveRunOutputs()
318 self.makeToolTar()
319
320 def call_planemo(self,xmlpath,ourdir):
321 penv = os.environ
322 penv['HOME'] = '/home/ross/galaxy-release_21.01'
323 toolfile = os.path.split(xmlpath)[1]
324 tool_name = self.tool_name
325 tool_test_output = f"{tool_name}_planemo_test_report.html"
326 cll = [
327 "planemo",
328 "test",
329 "--test_output",
330 os.path.abspath(tool_test_output),
331 "--galaxy_root",
332 self.args.galaxy_root,
333 "--update_test_data",
334 os.path.abspath(xmlpath),
335 ]
336 print(cll)
337 p = subprocess.run(
338 cll,
339 capture_output=True,
340 encoding='utf8',
341 env = penv,
342 shell=False,
343 )
344 return p
345
346 def makeTool(self):
347 """write xmls and input samples into place"""
348 for xreal in self.ourxmls:
349 x = os.path.split(xreal)[1]
350 xout = os.path.join(self.tooloutdir,x)
351 shutil.copyfile(xreal, xout)
352 # for p in self.infiles:
353 # pth = p["name"]
354 # dest = os.path.join(self.testdir, "%s_sample" % p["infilename"])
355 # shutil.copyfile(pth, dest)
356 # dest = os.path.join(self.repdir, "%s_sample" % p["infilename"])
357 # shutil.copyfile(pth, dest)
358
359 def makeToolTar(self):
360 """move outputs into test-data and prepare the tarball"""
361 excludeme = "_planemo_test_report.html"
362 def exclude_function(tarinfo):
363 filename = tarinfo.name
364 return None if filename.endswith(excludeme) else tarinfo
365 newtar = 'new_%s_toolshed.gz' % self.tool_name
366 ttf = tarfile.open(newtar, "w:gz")
367 ttf.add(name=self.tooloutdir,
368 arcname=self.tool_name,
369 filter=exclude_function)
370 ttf.close()
371 shutil.copyfile(newtar, self.new_tool_archive)
372
373 def moveRunOutputs(self):
374 """need to move planemo or run outputs into toolfactory collection"""
375 with os.scandir(self.tooloutdir) as outs:
376 for entry in outs:
377 if not entry.is_file():
378 continue
379 if "." in entry.name:
380 _, ext = os.path.splitext(entry.name)
381 if ext in [".tgz", ".json"]:
382 continue
383 if ext in [".yml", ".xml", ".yaml"]:
384 newname = f"{entry.name.replace('.','_')}.txt"
385 else:
386 newname = entry.name
387 else:
388 newname = f"{entry.name}.txt"
389 dest = os.path.join(self.repdir, newname)
390 src = os.path.join(self.tooloutdir, entry.name)
391 shutil.copyfile(src, dest)
392 with os.scandir('.') as outs:
393 for entry in outs:
394 if not entry.is_file():
395 continue
396 if "." in entry.name:
397 _, ext = os.path.splitext(entry.name)
398 if ext in [".yml", ".xml", ".yaml"]:
399 newname = f"{entry.name.replace('.','_')}.txt"
400 else:
401 newname = entry.name
402 else:
403 newname = f"{entry.name}.txt"
404 dest = os.path.join(self.repdir, newname)
405 src =entry.name
406 shutil.copyfile(src, dest)
407 if True or self.args.include_tests:
408 with os.scandir(self.testdir) as outs:
409 for entry in outs:
410 if (not entry.is_file()) or entry.name.endswith(
411 "_planemo_test_report.html"
412 ):
413 continue
414 if "." in entry.name:
415 _, ext = os.path.splitext(entry.name)
416 if ext in [".tgz", ".json"]:
417 continue
418 if ext in [".yml", ".xml", ".yaml"]:
419 newname = f"{entry.name.replace('.','_')}.txt"
420 else:
421 newname = entry.name
422 else:
423 newname = f"{entry.name}.txt"
424 dest = os.path.join(self.repdir, newname)
425 src = os.path.join(self.testdir, entry.name)
426 shutil.copyfile(src, dest)
427
428 def update_tests(self,ourdir):
429 for xmlf in self.ourxmls:
430 capture = self.call_planemo(xmlf,ourdir)
431 #sys.stderr.write('%s, stdout=%s, stderr=%s' % (xmlf, capture.stdout, capture.stdout))
432 print('%s, stdout=%s, stderr=%s' % (capture.stdout, capture.stdout,xmlf))
433
434 def main():
435 """
436 This is a Galaxy wrapper.
437 It expects to be called by a special purpose tool.xml
438 """
439 parser = argparse.ArgumentParser()
440 a = parser.add_argument
441 a("--in_tool_archive", default=None)
442 a("--new_tested_tool_archive", default=None)
443 a("--galaxy_root", default="/home/ross/gal21/")
444 args = parser.parse_args()
445 print('Hello from',os.getcwd())
446 tt = ToolTester(args=args, in_tool_archive=args.in_tool_archive, new_tool_archive=args.new_tested_tool_archive)
447 if __name__ == "__main__":
448 main()
449
450 ]]></help>
451 <citations>
452 <citation type="doi">10.1093/bioinformatics/bts573</citation>
453 </citations>
454 </tool>
455