comparison toolfactory/rgToolFactory2.py @ 112:5509dc4c1cf2 draft

Uploaded
author fubar
date Sun, 29 Nov 2020 10:34:48 +0000
parents 223b78754735
children 6873c211b250
comparison
equal deleted inserted replaced
111:223b78754735 112:5509dc4c1cf2
12 # Decided to 12 # Decided to
13 # 1. Fix the toolfactory so it works - done for simplest case 13 # 1. Fix the toolfactory so it works - done for simplest case
14 # 2. Fix planemo so the toolfactory function works 14 # 2. Fix planemo so the toolfactory function works
15 # 3. Rewrite bits using galaxyxml functions where that makes sense - done 15 # 3. Rewrite bits using galaxyxml functions where that makes sense - done
16 # 16 #
17 # removed all the old complications including making the new tool use this same script 17 # uses planemo in a biodocker sort of image as a requirement
18 # galaxyxml now generates the tool xml https://github.com/hexylena/galaxyxml 18 # otherwise planemo seems to leak dependencies back into the
19 # No support for automatic HTML file creation from arbitrary outputs 19 # calling venv. Hilarity ensues.
20 # essential problem is to create two command lines - one for the tool xml and a different 20
21 # one to run the executable with the supplied test data and settings 21
22 # Be simpler to write the tool, then run it with planemo and soak up the test outputs.
23 # well well. sh run_tests.sh --id rgtf2 --report_file tool_tests_tool_conf.html functional.test_toolbox
24 # does the needful. Use GALAXY_TEST_SAVE /foo to save outputs - only the tar.gz - not the rest sadly
25 # GALAXY_TEST_NO_CLEANUP GALAXY_TEST_TMP_DIR=wherever
26 # planemo test --engine docker_galaxy --test_data ./test-data/ --docker_extra_volume ./test-data rgToolFactory2.xml
27 22
28 import argparse 23 import argparse
29 import copy 24 import copy
30 import datetime 25 import datetime
31 import grp 26 import grp
56 myversion = "V2.1 July 2020" 51 myversion = "V2.1 July 2020"
57 verbose = True 52 verbose = True
58 debug = True 53 debug = True
59 toolFactoryURL = "https://github.com/fubar2/toolfactory" 54 toolFactoryURL = "https://github.com/fubar2/toolfactory"
60 ourdelim = "~~~" 55 ourdelim = "~~~"
61 ALOT = 10000000 # srsly. command or test overrides use read() so just in case
62 STDIOXML = """<stdio>
63 <exit_code range="100:" level="debug" description="shite happens" />
64 </stdio>"""
65 56
66 # --input_files="$intab.input_files~~~$intab.input_CL~~~$intab.input_formats\ 57 # --input_files="$intab.input_files~~~$intab.input_CL~~~$intab.input_formats\
67 #~~~$intab.input_label~~~$intab.input_help" 58 #~~~$intab.input_label~~~$intab.input_help"
68 IPATHPOS = 0 59 IPATHPOS = 0
69 ICLPOS = 1 60 ICLPOS = 1
261 appendme = [p[IOCLPOS], p[ICLPOS], p[IPATHPOS], ""] 252 appendme = [p[IOCLPOS], p[ICLPOS], p[IPATHPOS], ""]
262 xappendme = [p[IOCLPOS], p[ICLPOS], "$%s" % p[ICLPOS], ""] 253 xappendme = [p[IOCLPOS], p[ICLPOS], "$%s" % p[ICLPOS], ""]
263 clsuffix.append(appendme) 254 clsuffix.append(appendme)
264 xclsuffix.append(xappendme) 255 xclsuffix.append(xappendme)
265 for i, p in enumerate(self.outfiles): 256 for i, p in enumerate(self.outfiles):
266 if p[OCLPOS] == "STDOUT": 257 if p[OOCLPOS] == "STDOUT":
267 self.lastclredirect = [">", p[ONAMEPOS]] 258 self.lastclredirect = [">", p[ONAMEPOS]]
268 self.lastxclredirect = [">", "$%s" % p[OCLPOS]] 259 self.lastxclredirect = [">", "$%s" % p[OCLPOS]]
269 else: 260 else: # for (o_v, k, v, koverride) in self.xclsuffix:
270 clsuffix.append([p[OOCLPOS], p[OCLPOS], p[ONAMEPOS], ""]) 261 clsuffix.append([p[ONAMEPOS], p[ONAMEPOS], p[ONAMEPOS], ""])
271 xclsuffix.append([p[OOCLPOS], p[OCLPOS], "$%s" % p[ONAMEPOS], ""]) 262 xclsuffix.append([p[ONAMEPOS], p[ONAMEPOS], "$%s" % p[ONAMEPOS], ""])
272 for p in self.addpar: 263 for p in self.addpar:
273 clsuffix.append([p[AOCLPOS], p[ACLPOS], p[AVALPOS], p[AOVERPOS]]) 264 clsuffix.append([p[AOCLPOS], p[ACLPOS], p[AVALPOS], p[AOVERPOS]])
274 xclsuffix.append( 265 xclsuffix.append(
275 [p[AOCLPOS], p[ACLPOS], '"$%s"' % p[ANAMEPOS], p[AOVERPOS]] 266 [p[AOCLPOS], p[ACLPOS], '"$%s"' % p[ANAMEPOS], p[AOVERPOS]]
276 ) 267 )
294 ) 285 )
295 tscript = open(self.sfile, "w") 286 tscript = open(self.sfile, "w")
296 tscript.write(self.script) 287 tscript.write(self.script)
297 tscript.close() 288 tscript.close()
298 self.escapedScript = [cheetah_escape(x) for x in rx] 289 self.escapedScript = [cheetah_escape(x) for x in rx]
299 self.spacedScript = [f" {x}" for x in rx] 290 self.spacedScript = [f" {x}" for x in rx if x.strip() > ""]
300 art = "%s.%s" % (self.tool_name, self.executeme) 291 art = "%s.%s" % (self.tool_name, self.executeme)
301 artifact = open(art, "wb") 292 artifact = open(art, "wb")
302 artifact.write(bytes('\n'.join(self.escapedScript),'utf8')) 293 artifact.write(bytes('\n'.join(self.escapedScript),'utf8'))
303 artifact.close() 294 artifact.close()
304 295
328 ].isdigit(), "Positional parameters must be ordinal integers - got %s for %s" % ( 319 ].isdigit(), "Positional parameters must be ordinal integers - got %s for %s" % (
329 p[OCLPOS], 320 p[OCLPOS],
330 p[ONAMEPOS], 321 p[ONAMEPOS],
331 ) 322 )
332 p.append(p[OCLPOS]) # keep copy 323 p.append(p[OCLPOS]) # keep copy
333 if p[OCLPOS].isdigit() or p[OCLPOS] == "STDOUT": 324 if p[OOCLPOS].isdigit() or p[OOCLPOS] == "STDOUT":
334 scl = p[ONAMEPOS] 325 scl = p[ONAMEPOS]
335 p[OCLPOS] = scl 326 p[OCLPOS] = scl
336 self.outfiles[i] = p 327 self.outfiles[i] = p
337 for i, p in enumerate(self.addpar): 328 for i, p in enumerate(self.addpar):
338 if self.args.parampass == "positional": 329 if self.args.parampass == "positional":
376 for (o_v, k, v, koverride) in self.xclsuffix: 367 for (o_v, k, v, koverride) in self.xclsuffix:
377 aXCL(v) 368 aXCL(v)
378 if self.lastxclredirect: 369 if self.lastxclredirect:
379 aXCL(self.lastxclredirect[0]) 370 aXCL(self.lastxclredirect[0])
380 aXCL(self.lastxclredirect[1]) 371 aXCL(self.lastxclredirect[1])
372 if os.path.exists(self.tlog):
373 tout = open(self.tlog, "a")
374 else:
375 tout = open(self.tlog, "w")
376 tout.write(f" #### clpositional: self.clsuffix = {self.clsuffix} and self.xclsuffix = {self.xclsuffix}")
377 tout.close()
378
381 379
382 def clargparse(self): 380 def clargparse(self):
383 """argparse style""" 381 """argparse style"""
384 aCL = self.cl.append 382 aCL = self.cl.append
385 aXCL = self.xmlcl.append 383 aXCL = self.xmlcl.append
386 # inputs then params in argparse named form 384 # inputs then params in argparse named form
385
387 for (o_v, k, v, koverride) in self.xclsuffix: 386 for (o_v, k, v, koverride) in self.xclsuffix:
388 if koverride > "": 387 if koverride > "":
389 k = koverride 388 k = koverride
390 elif len(k.strip()) == 1: 389 elif len(k.strip()) == 1:
391 k = "-%s" % k 390 k = "-%s" % k
400 k = "-%s" % k 399 k = "-%s" % k
401 else: 400 else:
402 k = "--%s" % k 401 k = "--%s" % k
403 aCL(k) 402 aCL(k)
404 aCL(v) 403 aCL(v)
404 if os.path.exists(self.tlog):
405 tout = open(self.tlog, "a")
406 else:
407 tout = open(self.tlog, "w")
408 tout.write(f" #### clargparse: self.clsuffix = {self.clsuffix} and self.xclsuffix = {self.xclsuffix}")
409 tout.close()
410
405 411
406 def getNdash(self, newname): 412 def getNdash(self, newname):
407 if self.is_positional: 413 if self.is_positional:
408 ndash = 0 414 ndash = 0
409 else: 415 else:
891 "installed %s - got retcode %d\n" % (self.tool_name, subp.returncode) 897 "installed %s - got retcode %d\n" % (self.tool_name, subp.returncode)
892 ) 898 )
893 tout.close() 899 tout.close()
894 return subp.returncode 900 return subp.returncode
895 901
896 def planemo_shedLoad(self): 902
897 """
898 planemo shed_create --shed_target testtoolshed
899 planemo shed_init --name=<name>
900 --owner=<shed_username>
901 --description=<short description>
902 [--remote_repository_url=<URL to .shed.yml on github>]
903 [--homepage_url=<Homepage for tool.>]
904 [--long_description=<long description>]
905 [--category=<category name>]*
906
907
908 planemo shed_update --check_diff --shed_target testtoolshed
909 """
910 if os.path.exists(self.tlog):
911 tout = open(self.tlog, "a")
912 else:
913 tout = open(self.tlog, "w")
914 ts = toolshed.ToolShedInstance(
915 url=self.args.toolshed_url, key=self.args.toolshed_api_key, verify=False
916 )
917 repos = ts.repositories.get_repositories()
918 rnames = [x.get("name", "?") for x in repos]
919 rids = [x.get("id", "?") for x in repos]
920 # cat = "ToolFactory generated tools"
921 if self.tool_name not in rnames:
922 cll = [
923 "planemo",
924 "shed_create",
925 "--shed_target",
926 "local",
927 "--owner",
928 "fubar",
929 "--name",
930 self.tool_name,
931 "--shed_key",
932 self.args.toolshed_api_key,
933 ]
934 try:
935 subp = subprocess.run(
936 cll,
937 env=self.ourenv,
938 shell=False,
939 cwd=self.tooloutdir,
940 stdout=tout,
941 stderr=tout,
942 )
943 except:
944 pass
945 if subp.returncode != 0:
946 tout.write("Repository %s exists\n" % self.tool_name)
947 else:
948 tout.write("initiated %s\n" % self.tool_name)
949 cll = [
950 "planemo",
951 "shed_upload",
952 "--shed_target",
953 "local",
954 "--owner",
955 "fubar",
956 "--name",
957 self.tool_name,
958 "--shed_key",
959 self.args.toolshed_api_key,
960 "--tar",
961 self.newtarpath,
962 ]
963 subp = subprocess.run(
964 cll, env=self.ourenv, cwd=self.ourcwd, shell=False, stdout=tout, stderr=tout
965 )
966 tout.write("Ran %s got %d\n" % (" ".join(cll), subp.returncode))
967 tout.close()
968 return subp.returncode
969
970 def eph_test(self, genoutputs=True):
971 """problem getting jobid - ephemeris upload is the job before the one we want - but depends on how many inputs"""
972 if os.path.exists(self.tlog):
973 tout = open(self.tlog, "a")
974 else:
975 tout = open(self.tlog, "w")
976 cll = [
977 "shed-tools",
978 "test",
979 "-g",
980 self.args.galaxy_url,
981 "-a",
982 self.args.galaxy_api_key,
983 "--name",
984 self.tool_name,
985 "--owner",
986 "fubar",
987 ]
988 if genoutputs:
989 dummy, tfile = tempfile.mkstemp()
990 subp = subprocess.run(
991 cll,
992 env=self.ourenv,
993 cwd=self.ourcwd,
994 shell=False,
995 stderr=dummy,
996 stdout=dummy,
997 )
998
999 with open("tool_test_output.json", "rb") as f:
1000 s = json.loads(f.read())
1001 print("read %s" % s)
1002 cl = s["tests"][0]["data"]["job"]["command_line"].split()
1003 n = cl.index("--script_path")
1004 jobdir = cl[n + 1]
1005 jobdir = jobdir.replace('"', "")
1006 jobdir = jobdir.split("/configs")[0]
1007 print("jobdir=%s" % jobdir)
1008
1009 # "/home/ross/galthrow/database/jobs_directory/000/649/configs/tmptfxu51gs\"
1010 src = os.path.join(jobdir, "working", self.newtarpath)
1011 if os.path.exists(src):
1012 dest = os.path.join(self.testdir, self.newtarpath)
1013 shutil.copyfile(src, dest)
1014 else:
1015 tout.write(
1016 "No toolshed archive found after first ephemeris test - not a good sign"
1017 )
1018 ephouts = os.path.join(jobdir, "working", "tfout", "test-data")
1019 with os.scandir(ephouts) as outs:
1020 for entry in outs:
1021 if not entry.is_file():
1022 continue
1023 dest = os.path.join(self.tooloutdir, entry.name)
1024 src = os.path.join(ephouts, entry.name)
1025 shutil.copyfile(src, dest)
1026 else:
1027 subp = subprocess.run(
1028 cll,
1029 env=self.ourenv,
1030 cwd=self.ourcwd,
1031 shell=False,
1032 stderr=tout,
1033 stdout=tout,
1034 )
1035 tout.write("eph_test Ran %s got %d" % (" ".join(cll), subp.returncode))
1036 tout.close()
1037 return subp.returncode
1038
1039 def planemo_test_biocontainer(self, genoutputs=True):
1040 """planemo is a requirement so is available for testing but testing in a biocontainer
1041 requires some fiddling to use the hacked galaxy-central .venv
1042
1043 Planemo runs:
1044 python ./scripts/functional_tests.py -v --with-nosehtml --html-report-file
1045 /export/galaxy-central/database/job_working_directory/000/17/working/TF_run_report_tempdir/tacrev_planemo_test_report.html
1046 --with-xunit --xunit-file /tmp/tmpt90p7f9h/xunit.xml --with-structureddata
1047 --structured-data-file
1048 /export/galaxy-central/database/job_working_directory/000/17/working/tfout/tool_test_output.json functional.test_toolbox
1049
1050
1051 for the planemo-biocontainer,
1052 planemo test --conda_dependency_resolution --skip_venv --galaxy_root /galthrow/ rgToolFactory2.xml
1053
1054 """
1055 xreal = "%s.xml" % self.tool_name
1056 tool_test_path = os.path.join(
1057 self.repdir, f"{self.tool_name}_planemo_test_report.html"
1058 )
1059 if os.path.exists(self.tlog):
1060 tout = open(self.tlog, "a")
1061 else:
1062 tout = open(self.tlog, "w")
1063 if genoutputs:
1064 dummy, tfile = tempfile.mkstemp()
1065 cll = [
1066 ".",
1067 os.path.join(self.args.galaxy_root, ".venv", "bin", "activate"),
1068 "&&",
1069 "planemo",
1070 "test",
1071 "--test_data",
1072 self.testdir,
1073 "--test_output",
1074 tool_test_path,
1075 "--skip_venv",
1076 "--galaxy_root",
1077 self.args.galaxy_root,
1078 "--update_test_data",
1079 xreal,
1080 ]
1081 subp = subprocess.run(
1082 cll,
1083 env=self.ourenv,
1084 shell=False,
1085 cwd=self.tooloutdir,
1086 stderr=dummy,
1087 stdout=dummy,
1088 )
1089
1090 else:
1091 cll = [
1092 ".",
1093 os.path.join(self.args.galaxy_root, ".venv", "bin", "activate"),
1094 "&&",
1095 "planemo",
1096 "test",
1097 "--test_data",
1098 os.path.self.testdir,
1099 "--test_output",
1100 os.path.tool_test_path,
1101 "--skip_venv",
1102 "--galaxy_root",
1103 self.args.galaxy_root,
1104 xreal,
1105 ]
1106 subp = subprocess.run(
1107 cll,
1108 env=self.ourenv,
1109 shell=False,
1110 cwd=self.tooloutdir,
1111 stderr=tout,
1112 stdout=tout,
1113 )
1114 tout.close()
1115 return subp.returncode
1116 903
1117 def writeShedyml(self): 904 def writeShedyml(self):
1118 """for planemo""" 905 """for planemo"""
1119 yuser = self.args.user_email.split("@")[0] 906 yuser = self.args.user_email.split("@")[0]
1120 yfname = os.path.join(self.tooloutdir, ".shed.yml") 907 yfname = os.path.join(self.tooloutdir, ".shed.yml")