toil 7.0.0__py3-none-any.whl → 8.1.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toil/__init__.py +124 -86
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +137 -77
- toil/batchSystems/abstractGridEngineBatchSystem.py +211 -101
- toil/batchSystems/awsBatch.py +237 -128
- toil/batchSystems/cleanup_support.py +22 -16
- toil/batchSystems/contained_executor.py +30 -26
- toil/batchSystems/gridengine.py +85 -49
- toil/batchSystems/htcondor.py +164 -87
- toil/batchSystems/kubernetes.py +622 -386
- toil/batchSystems/local_support.py +17 -12
- toil/batchSystems/lsf.py +132 -79
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +288 -149
- toil/batchSystems/mesos/executor.py +77 -49
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +39 -29
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +293 -123
- toil/batchSystems/slurm.py +651 -155
- toil/batchSystems/torque.py +46 -32
- toil/bus.py +141 -73
- toil/common.py +784 -397
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1137 -534
- toil/cwl/utils.py +17 -22
- toil/deferred.py +62 -41
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +88 -57
- toil/fileStores/cachingFileStore.py +711 -247
- toil/fileStores/nonCachingFileStore.py +113 -75
- toil/job.py +1031 -349
- toil/jobStores/abstractJobStore.py +387 -243
- toil/jobStores/aws/jobStore.py +772 -412
- toil/jobStores/aws/utils.py +161 -109
- toil/jobStores/conftest.py +1 -0
- toil/jobStores/fileJobStore.py +289 -151
- toil/jobStores/googleJobStore.py +137 -70
- toil/jobStores/utils.py +36 -15
- toil/leader.py +614 -269
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +55 -28
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +204 -58
- toil/lib/aws/utils.py +290 -213
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +83 -49
- toil/lib/docker.py +131 -103
- toil/lib/dockstore.py +379 -0
- toil/lib/ec2.py +322 -209
- toil/lib/ec2nodes.py +174 -105
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +4 -2
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/history.py +1271 -0
- toil/lib/history_submission.py +681 -0
- toil/lib/humanize.py +6 -2
- toil/lib/io.py +121 -12
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +83 -18
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +19 -7
- toil/lib/retry.py +125 -87
- toil/lib/threading.py +282 -80
- toil/lib/throttle.py +15 -14
- toil/lib/trs.py +390 -0
- toil/lib/web.py +38 -0
- toil/options/common.py +850 -402
- toil/options/cwl.py +185 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +70 -19
- toil/provisioners/__init__.py +111 -46
- toil/provisioners/abstractProvisioner.py +322 -157
- toil/provisioners/aws/__init__.py +62 -30
- toil/provisioners/aws/awsProvisioner.py +980 -627
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +283 -180
- toil/provisioners/node.py +147 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +127 -61
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +84 -55
- toil/server/utils.py +56 -31
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +183 -65
- toil/test/__init__.py +263 -179
- toil/test/batchSystems/batchSystemTest.py +438 -195
- toil/test/batchSystems/batch_system_plugin_test.py +18 -7
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +265 -49
- toil/test/cactus/test_cactus_integration.py +20 -22
- toil/test/cwl/conftest.py +39 -0
- toil/test/cwl/cwlTest.py +375 -72
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/optional-file.cwl +18 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/docs/scriptsTest.py +60 -34
- toil/test/jobStores/jobStoreTest.py +412 -235
- toil/test/lib/aws/test_iam.py +116 -48
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +57 -49
- toil/test/lib/test_history.py +212 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/lib/test_trs.py +161 -0
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/options.py +7 -2
- toil/test/provisioners/aws/awsProvisionerTest.py +293 -140
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +81 -42
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +140 -100
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +33 -26
- toil/test/src/environmentTest.py +20 -10
- toil/test/src/fileStoreTest.py +538 -271
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +32 -17
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +120 -70
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +6 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +33 -16
- toil/test/utils/toilDebugTest.py +70 -58
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +239 -102
- toil/test/wdl/wdltoil_test.py +789 -148
- toil/test/wdl/wdltoil_test_kubernetes.py +37 -23
- toil/toilState.py +52 -26
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +85 -25
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +251 -145
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +27 -14
- toil/utils/toilSshCluster.py +45 -22
- toil/utils/toilStats.py +75 -36
- toil/utils/toilStatus.py +226 -119
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +6 -6
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3528 -1053
- toil/worker.py +370 -149
- toil-8.1.0b1.dist-info/METADATA +178 -0
- toil-8.1.0b1.dist-info/RECORD +259 -0
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/WHEEL +1 -1
- toil-7.0.0.dist-info/METADATA +0 -158
- toil-7.0.0.dist-info/RECORD +0 -244
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/LICENSE +0 -0
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/entry_points.txt +0 -0
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/top_level.txt +0 -0
toil/test/src/threadingTest.py
CHANGED
|
@@ -6,28 +6,29 @@ import time
|
|
|
6
6
|
import traceback
|
|
7
7
|
from functools import partial
|
|
8
8
|
|
|
9
|
-
from toil.lib.threading import
|
|
10
|
-
cpu_count,
|
|
11
|
-
global_mutex)
|
|
9
|
+
from toil.lib.threading import LastProcessStandingArena, cpu_count, global_mutex
|
|
12
10
|
from toil.test import ToilTest
|
|
13
11
|
|
|
14
12
|
log = logging.getLogger(__name__)
|
|
15
13
|
|
|
14
|
+
|
|
16
15
|
class ThreadingTest(ToilTest):
|
|
17
16
|
"""Test Toil threading/synchronization tools."""
|
|
17
|
+
|
|
18
18
|
def testGlobalMutexOrdering(self):
|
|
19
19
|
for it in range(10):
|
|
20
|
-
log.info(
|
|
20
|
+
log.info("Iteration %d", it)
|
|
21
21
|
|
|
22
22
|
scope = self._createTempDir()
|
|
23
|
-
mutex =
|
|
23
|
+
mutex = "mutex"
|
|
24
24
|
# Use processes (as opposed to threads) to prevent GIL from ordering things artificially
|
|
25
25
|
pool = multiprocessing.Pool(processes=cpu_count())
|
|
26
26
|
try:
|
|
27
27
|
numTasks = 100
|
|
28
28
|
results = pool.map_async(
|
|
29
29
|
func=partial(_testGlobalMutexOrderingTask, scope, mutex),
|
|
30
|
-
iterable=list(range(numTasks))
|
|
30
|
+
iterable=list(range(numTasks)),
|
|
31
|
+
)
|
|
31
32
|
results = results.get()
|
|
32
33
|
finally:
|
|
33
34
|
pool.close()
|
|
@@ -40,17 +41,18 @@ class ThreadingTest(ToilTest):
|
|
|
40
41
|
|
|
41
42
|
def testLastProcessStanding(self):
|
|
42
43
|
for it in range(10):
|
|
43
|
-
log.info(
|
|
44
|
+
log.info("Iteration %d", it)
|
|
44
45
|
|
|
45
46
|
scope = self._createTempDir()
|
|
46
|
-
arena_name =
|
|
47
|
+
arena_name = "thunderdome"
|
|
47
48
|
# Use processes (as opposed to threads) to prevent GIL from ordering things artificially
|
|
48
49
|
pool = multiprocessing.Pool(processes=cpu_count())
|
|
49
50
|
try:
|
|
50
51
|
numTasks = 100
|
|
51
52
|
results = pool.map_async(
|
|
52
53
|
func=partial(_testLastProcessStandingTask, scope, arena_name),
|
|
53
|
-
iterable=list(range(numTasks))
|
|
54
|
+
iterable=list(range(numTasks)),
|
|
55
|
+
)
|
|
54
56
|
results = results.get()
|
|
55
57
|
finally:
|
|
56
58
|
pool.close()
|
|
@@ -61,19 +63,24 @@ class ThreadingTest(ToilTest):
|
|
|
61
63
|
# Make sure all workers say they succeeded
|
|
62
64
|
self.assertEqual(item, True)
|
|
63
65
|
for filename in os.listdir(scope):
|
|
64
|
-
assert not filename.startswith(
|
|
66
|
+
assert not filename.startswith(
|
|
67
|
+
"precious"
|
|
68
|
+
), f"File {filename} still exists"
|
|
69
|
+
|
|
65
70
|
|
|
66
71
|
def _testGlobalMutexOrderingTask(scope, mutex, number):
|
|
67
72
|
try:
|
|
68
73
|
# We will all fight over the potato
|
|
69
|
-
potato = os.path.join(scope,
|
|
74
|
+
potato = os.path.join(scope, "potato")
|
|
70
75
|
|
|
71
76
|
with global_mutex(scope, mutex):
|
|
72
|
-
log.info(
|
|
73
|
-
assert not os.path.exists(
|
|
77
|
+
log.info("PID %d = num %d running", os.getpid(), number)
|
|
78
|
+
assert not os.path.exists(
|
|
79
|
+
potato
|
|
80
|
+
), "We see someone else holding the potato file"
|
|
74
81
|
|
|
75
82
|
# Put our name there
|
|
76
|
-
with open(potato,
|
|
83
|
+
with open(potato, "w") as out_stream:
|
|
77
84
|
out_stream.write(str(number))
|
|
78
85
|
|
|
79
86
|
# Wait
|
|
@@ -82,51 +89,63 @@ def _testGlobalMutexOrderingTask(scope, mutex, number):
|
|
|
82
89
|
# Make sure our name is still there
|
|
83
90
|
with open(potato) as in_stream:
|
|
84
91
|
seen = in_stream.read().rstrip()
|
|
85
|
-
assert seen == str(
|
|
92
|
+
assert seen == str(
|
|
93
|
+
number
|
|
94
|
+
), f"We are {number} but {seen} stole our potato!"
|
|
86
95
|
|
|
87
96
|
os.unlink(potato)
|
|
88
97
|
assert not os.path.exists(potato), "We left the potato behind"
|
|
89
|
-
log.info(
|
|
98
|
+
log.info("PID %d = num %d dropped potato", os.getpid(), number)
|
|
90
99
|
return True
|
|
91
100
|
except:
|
|
92
101
|
traceback.print_exc()
|
|
93
102
|
return False
|
|
94
103
|
|
|
104
|
+
|
|
95
105
|
def _testLastProcessStandingTask(scope, arena_name, number):
|
|
96
106
|
try:
|
|
97
107
|
arena = LastProcessStandingArena(scope, arena_name)
|
|
98
108
|
|
|
99
109
|
arena.enter()
|
|
100
|
-
log.info(
|
|
110
|
+
log.info("PID %d = num %d entered arena", os.getpid(), number)
|
|
101
111
|
try:
|
|
102
112
|
# We all make files
|
|
103
|
-
my_precious = os.path.join(scope,
|
|
113
|
+
my_precious = os.path.join(scope, "precious" + str(number))
|
|
104
114
|
|
|
105
115
|
# Put our name there
|
|
106
|
-
with open(my_precious,
|
|
116
|
+
with open(my_precious, "w") as out_stream:
|
|
107
117
|
out_stream.write(str(number))
|
|
108
118
|
|
|
109
119
|
# Wait
|
|
110
120
|
time.sleep(random.random() * 0.01)
|
|
111
121
|
|
|
112
122
|
# Make sure our file is still there unmodified
|
|
113
|
-
assert os.path.exists(
|
|
123
|
+
assert os.path.exists(
|
|
124
|
+
my_precious
|
|
125
|
+
), f"Precious file {my_precious} has been stolen!"
|
|
114
126
|
with open(my_precious) as in_stream:
|
|
115
127
|
seen = in_stream.read().rstrip()
|
|
116
|
-
assert seen == str(
|
|
128
|
+
assert seen == str(
|
|
129
|
+
number
|
|
130
|
+
), f"We are {number} but saw {seen} in our precious file!"
|
|
117
131
|
finally:
|
|
118
132
|
was_last = False
|
|
119
133
|
for _ in arena.leave():
|
|
120
134
|
was_last = True
|
|
121
|
-
log.info(
|
|
135
|
+
log.info("PID %d = num %d is last standing", os.getpid(), number)
|
|
122
136
|
|
|
123
137
|
# Clean up all the files
|
|
124
138
|
for filename in os.listdir(scope):
|
|
125
|
-
if filename.startswith(
|
|
126
|
-
log.info(
|
|
139
|
+
if filename.startswith("precious"):
|
|
140
|
+
log.info(
|
|
141
|
+
"PID %d = num %d cleaning up %s",
|
|
142
|
+
os.getpid(),
|
|
143
|
+
number,
|
|
144
|
+
filename,
|
|
145
|
+
)
|
|
127
146
|
os.unlink(os.path.join(scope, filename))
|
|
128
147
|
|
|
129
|
-
log.info(
|
|
148
|
+
log.info("PID %d = num %d left arena", os.getpid(), number)
|
|
130
149
|
|
|
131
150
|
return True
|
|
132
151
|
except:
|
|
@@ -29,13 +29,13 @@ class ToilContextManagerTest(ToilTest):
|
|
|
29
29
|
|
|
30
30
|
def testContextManger(self):
|
|
31
31
|
options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
|
|
32
|
-
options.logLevel =
|
|
32
|
+
options.logLevel = "INFO"
|
|
33
33
|
with Toil(options) as toil:
|
|
34
34
|
toil.start(HelloWorld())
|
|
35
35
|
|
|
36
36
|
def testNoContextManger(self):
|
|
37
37
|
options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
|
|
38
|
-
options.logLevel =
|
|
38
|
+
options.logLevel = "INFO"
|
|
39
39
|
toil = Toil(options)
|
|
40
40
|
self.assertRaises(ToilContextManagerException, toil.start, HelloWorld())
|
|
41
41
|
|
|
@@ -45,7 +45,9 @@ class ToilContextManagerTest(ToilTest):
|
|
|
45
45
|
with Toil(options) as toil:
|
|
46
46
|
_ = toil.start(HelloWorld())
|
|
47
47
|
# oh no, an error! :(
|
|
48
|
-
raise RuntimeError(
|
|
48
|
+
raise RuntimeError(
|
|
49
|
+
"we died after workflow completion but before our export finished"
|
|
50
|
+
)
|
|
49
51
|
except RuntimeError:
|
|
50
52
|
pass
|
|
51
53
|
|
|
@@ -54,17 +56,18 @@ class ToilContextManagerTest(ToilTest):
|
|
|
54
56
|
fileID = toil.restart()
|
|
55
57
|
print(fileID)
|
|
56
58
|
# Hopefully the error didn't cause us to lose all our work!
|
|
57
|
-
toil.exportFile(fileID,
|
|
59
|
+
toil.exportFile(fileID, "file://" + self.exportPath)
|
|
58
60
|
with open(self.exportPath) as f:
|
|
59
61
|
# The file should have all our content
|
|
60
62
|
self.assertEqual(f.read(), "Hello, World!")
|
|
61
63
|
|
|
64
|
+
|
|
62
65
|
class HelloWorld(Job):
|
|
63
66
|
def __init__(self):
|
|
64
|
-
Job.__init__(self, memory=100000, disk=
|
|
67
|
+
Job.__init__(self, memory=100000, disk="1M")
|
|
65
68
|
|
|
66
69
|
def run(self, fileStore):
|
|
67
|
-
fileID = self.addChildJobFn(childFn, memory=
|
|
70
|
+
fileID = self.addChildJobFn(childFn, memory="1M", disk="1M").rv()
|
|
68
71
|
return self.addFollowOn(FollowOn(fileID)).rv()
|
|
69
72
|
|
|
70
73
|
|
|
@@ -81,7 +84,7 @@ class FollowOn(Job):
|
|
|
81
84
|
|
|
82
85
|
def run(self, fileStore):
|
|
83
86
|
tempDir = fileStore.getLocalTempDir()
|
|
84
|
-
tempFilePath = "/".join([tempDir,
|
|
87
|
+
tempFilePath = "/".join([tempDir, "LocalCopy"])
|
|
85
88
|
with fileStore.readGlobalFileStream(self.fileId) as globalFile:
|
|
86
89
|
with open(tempFilePath, "wb") as localFile:
|
|
87
90
|
localFile.write(globalFile.read())
|
|
@@ -52,9 +52,11 @@ class UserDefinedJobArgTypeTest(ToilTest):
|
|
|
52
52
|
self._testFromMain()
|
|
53
53
|
|
|
54
54
|
def _testFromMain(self):
|
|
55
|
-
testMethodName = self.id().split(
|
|
56
|
-
self.assertTrue(testMethodName.endswith(
|
|
57
|
-
subprocess.check_call(
|
|
55
|
+
testMethodName = self.id().split(".")[-1]
|
|
56
|
+
self.assertTrue(testMethodName.endswith("FromMain"))
|
|
57
|
+
subprocess.check_call(
|
|
58
|
+
[sys.executable, "-m", self.__module__, testMethodName[:-8]]
|
|
59
|
+
)
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
class JobClass(Job):
|
|
@@ -66,8 +68,9 @@ class JobClass(Job):
|
|
|
66
68
|
def run(self, fileStore):
|
|
67
69
|
self.foo.assertIsCopy()
|
|
68
70
|
if self.level < 2:
|
|
69
|
-
self.addChildJobFn(
|
|
70
|
-
|
|
71
|
+
self.addChildJobFn(
|
|
72
|
+
jobFunction, self.level + 1, Foo(), cores=1, memory="1M", disk="300M"
|
|
73
|
+
)
|
|
71
74
|
|
|
72
75
|
|
|
73
76
|
def jobFunction(job, level, foo):
|
toil/test/src/workerTest.py
CHANGED
|
@@ -12,46 +12,55 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
15
17
|
from toil.common import Config
|
|
16
18
|
from toil.job import CheckpointJobDescription, JobDescription
|
|
17
19
|
from toil.jobStores.fileJobStore import FileJobStore
|
|
18
20
|
from toil.test import ToilTest
|
|
19
21
|
from toil.worker import nextChainable
|
|
20
22
|
|
|
21
|
-
from typing import Optional
|
|
22
|
-
|
|
23
23
|
|
|
24
24
|
class WorkerTests(ToilTest):
|
|
25
25
|
"""Test miscellaneous units of the worker."""
|
|
26
|
+
|
|
26
27
|
def setUp(self):
|
|
27
28
|
super().setUp()
|
|
28
29
|
path = self._getTestJobStorePath()
|
|
29
30
|
self.jobStore = FileJobStore(path)
|
|
30
31
|
self.config = Config()
|
|
31
|
-
self.config.jobStore =
|
|
32
|
+
self.config.jobStore = "file:%s" % path
|
|
32
33
|
self.jobStore.initialize(self.config)
|
|
33
34
|
self.jobNumber = 0
|
|
34
35
|
|
|
35
36
|
def testNextChainable(self):
|
|
36
37
|
"""Make sure chainable/non-chainable jobs are identified correctly."""
|
|
37
|
-
|
|
38
|
+
|
|
39
|
+
def createTestJobDesc(
|
|
40
|
+
memory,
|
|
41
|
+
cores,
|
|
42
|
+
disk,
|
|
43
|
+
preemptible: bool = True,
|
|
44
|
+
checkpoint: bool = False,
|
|
45
|
+
local: Optional[bool] = None,
|
|
46
|
+
):
|
|
38
47
|
"""
|
|
39
48
|
Create a JobDescription with no command (representing a Job that
|
|
40
49
|
has already run) and return the JobDescription.
|
|
41
50
|
"""
|
|
42
|
-
name =
|
|
51
|
+
name = "job%d" % self.jobNumber
|
|
43
52
|
self.jobNumber += 1
|
|
44
53
|
|
|
45
54
|
descClass = CheckpointJobDescription if checkpoint else JobDescription
|
|
46
55
|
jobDesc = descClass(
|
|
47
56
|
requirements={
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
jobName=name,
|
|
54
|
-
local=local
|
|
57
|
+
"memory": memory,
|
|
58
|
+
"cores": cores,
|
|
59
|
+
"disk": disk,
|
|
60
|
+
"preemptible": preemptible,
|
|
61
|
+
},
|
|
62
|
+
jobName=name,
|
|
63
|
+
local=local,
|
|
55
64
|
)
|
|
56
65
|
|
|
57
66
|
# Assign an ID
|
|
@@ -60,7 +69,7 @@ class WorkerTests(ToilTest):
|
|
|
60
69
|
# Save and return the JobDescription
|
|
61
70
|
return self.jobStore.create_job(jobDesc)
|
|
62
71
|
|
|
63
|
-
for successorType in [
|
|
72
|
+
for successorType in ["addChild", "addFollowOn"]:
|
|
64
73
|
# Try with the branch point at both child and follow-on stages
|
|
65
74
|
|
|
66
75
|
# Identical non-checkpoint jobs should be chainable.
|
|
@@ -96,14 +105,22 @@ class WorkerTests(ToilTest):
|
|
|
96
105
|
self.assertEqual(nextChainable(jobDesc1, self.jobStore, self.config), None)
|
|
97
106
|
|
|
98
107
|
# If there is an increase in resource requirements we should get nothing to chain.
|
|
99
|
-
base_reqs = {
|
|
100
|
-
|
|
108
|
+
base_reqs = {
|
|
109
|
+
"memory": 1,
|
|
110
|
+
"cores": 2,
|
|
111
|
+
"disk": 3,
|
|
112
|
+
"preemptible": True,
|
|
113
|
+
"checkpoint": False,
|
|
114
|
+
}
|
|
115
|
+
for increased_attribute in ("memory", "cores", "disk"):
|
|
101
116
|
reqs = dict(base_reqs)
|
|
102
117
|
jobDesc1 = createTestJobDesc(**reqs)
|
|
103
118
|
reqs[increased_attribute] += 1
|
|
104
119
|
jobDesc2 = createTestJobDesc(**reqs)
|
|
105
120
|
getattr(jobDesc1, successorType)(jobDesc2.jobStoreID)
|
|
106
|
-
self.assertEqual(
|
|
121
|
+
self.assertEqual(
|
|
122
|
+
nextChainable(jobDesc1, self.jobStore, self.config), None
|
|
123
|
+
)
|
|
107
124
|
|
|
108
125
|
# A change in preemptability from True to False should be disallowed.
|
|
109
126
|
jobDesc1 = createTestJobDesc(1, 2, 3, preemptible=True)
|
toil/test/utils/toilDebugTest.py
CHANGED
|
@@ -16,12 +16,8 @@ import os
|
|
|
16
16
|
import subprocess
|
|
17
17
|
import tempfile
|
|
18
18
|
|
|
19
|
-
import pytest
|
|
20
|
-
|
|
21
|
-
from toil.test import ToilTest
|
|
22
|
-
|
|
23
19
|
from toil.lib.resources import glob
|
|
24
|
-
from toil.test import
|
|
20
|
+
from toil.test import ToilTest, needs_wdl, slow
|
|
25
21
|
from toil.version import python
|
|
26
22
|
|
|
27
23
|
logger = logging.getLogger(__name__)
|
|
@@ -121,6 +117,7 @@ def testFetchJobStoreFiles() -> None:
|
|
|
121
117
|
for symlink in (True, False):
|
|
122
118
|
fetchFiles(symLink=symlink, jobStoreDir=job_store_dir, outputDir=output_dir)
|
|
123
119
|
|
|
120
|
+
|
|
124
121
|
class DebugJobTest(ToilTest):
|
|
125
122
|
"""
|
|
126
123
|
Test the toil debug-job command.
|
|
@@ -137,23 +134,28 @@ class DebugJobTest(ToilTest):
|
|
|
137
134
|
logger.info("Running workflow that always fails")
|
|
138
135
|
try:
|
|
139
136
|
# Run an always-failing workflow
|
|
140
|
-
subprocess.check_call(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
subprocess.check_call(
|
|
138
|
+
[
|
|
139
|
+
python,
|
|
140
|
+
os.path.abspath("src/toil/test/docs/scripts/example_alwaysfail.py"),
|
|
141
|
+
"--retryCount=0",
|
|
142
|
+
"--logCritical",
|
|
143
|
+
"--disableProgress",
|
|
144
|
+
job_store,
|
|
145
|
+
],
|
|
146
|
+
stderr=subprocess.DEVNULL,
|
|
147
|
+
)
|
|
148
148
|
raise RuntimeError("Failing workflow succeeded!")
|
|
149
149
|
except subprocess.CalledProcessError:
|
|
150
150
|
# Should fail to run
|
|
151
151
|
logger.info("Task failed successfully")
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
|
|
154
153
|
# Get the job ID.
|
|
155
154
|
# TODO: This assumes a lot about the FileJobStore. Use the MessageBus instead?
|
|
156
|
-
job_id =
|
|
155
|
+
job_id = (
|
|
156
|
+
"kind-explode/"
|
|
157
|
+
+ os.listdir(os.path.join(job_store, "jobs/kind-explode"))[0]
|
|
158
|
+
)
|
|
157
159
|
|
|
158
160
|
return job_store, job_id
|
|
159
161
|
|
|
@@ -161,27 +163,41 @@ class DebugJobTest(ToilTest):
|
|
|
161
163
|
"""
|
|
162
164
|
Get a job store and the name of a failed job in it that actually wanted to use some files.
|
|
163
165
|
"""
|
|
164
|
-
|
|
166
|
+
|
|
165
167
|
# First make a job store.
|
|
166
168
|
job_store = os.path.join(self._createTempDir(), "tree")
|
|
167
169
|
|
|
168
170
|
logger.info("Running workflow that always fails")
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
# Run an always-failing workflow
|
|
172
|
+
wf_result = subprocess.run(
|
|
173
|
+
[
|
|
172
174
|
"toil-wdl-runner",
|
|
173
|
-
os.path.abspath(
|
|
175
|
+
os.path.abspath(
|
|
176
|
+
"src/toil/test/docs/scripts/example_alwaysfail_with_files.wdl"
|
|
177
|
+
),
|
|
174
178
|
"--retryCount=0",
|
|
175
|
-
"--
|
|
179
|
+
"--logDebug",
|
|
176
180
|
"--disableProgress",
|
|
177
181
|
"--jobStore",
|
|
178
|
-
job_store
|
|
179
|
-
],
|
|
182
|
+
job_store,
|
|
183
|
+
],
|
|
184
|
+
stdout=subprocess.PIPE,
|
|
185
|
+
stderr=subprocess.STDOUT,
|
|
186
|
+
encoding="utf-8",
|
|
187
|
+
errors="replace",
|
|
188
|
+
)
|
|
189
|
+
logger.debug("Always-failing workflow output: %s", wf_result.stdout)
|
|
190
|
+
if wf_result.returncode == 0:
|
|
180
191
|
raise RuntimeError("Failing workflow succeeded!")
|
|
181
|
-
|
|
182
|
-
# Should fail to run
|
|
192
|
+
else:
|
|
183
193
|
logger.info("Task failed successfully")
|
|
184
|
-
|
|
194
|
+
|
|
195
|
+
# Make sure that the job store we created actually has its job store
|
|
196
|
+
# root job ID file. If it doesn't, we failed during workflow setup and
|
|
197
|
+
# not because of a real failing job.
|
|
198
|
+
assert os.path.exists(
|
|
199
|
+
os.path.join(job_store, "files/shared/rootJobStoreID")
|
|
200
|
+
), "Failed workflow still needs a root job"
|
|
185
201
|
|
|
186
202
|
# Get a job name for a job that fails
|
|
187
203
|
job_name = "WDLTaskJob"
|
|
@@ -198,18 +214,14 @@ class DebugJobTest(ToilTest):
|
|
|
198
214
|
logger.info("Trying to rerun job %s", job_id)
|
|
199
215
|
|
|
200
216
|
# Rerun the job, which should fail again
|
|
201
|
-
output = subprocess.check_output(
|
|
202
|
-
"toil",
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
job_store,
|
|
206
|
-
job_id
|
|
207
|
-
], stderr=subprocess.STDOUT)
|
|
217
|
+
output = subprocess.check_output(
|
|
218
|
+
["toil", "debug-job", "--logDebug", job_store, job_id],
|
|
219
|
+
stderr=subprocess.STDOUT,
|
|
220
|
+
)
|
|
208
221
|
# Even if the job fails, the attempt to run it will succeed.
|
|
209
|
-
log = output.decode(
|
|
222
|
+
log = output.decode("utf-8")
|
|
210
223
|
assert "Boom!" in log, f"Did not find the expected exception message in: {log}"
|
|
211
224
|
|
|
212
|
-
|
|
213
225
|
def test_print_job_info(self):
|
|
214
226
|
"""
|
|
215
227
|
Make sure that we can use --printJobInfo to get information on a job from a job store.
|
|
@@ -220,14 +232,9 @@ class DebugJobTest(ToilTest):
|
|
|
220
232
|
logger.info("Trying to print job info for job %s", job_id)
|
|
221
233
|
|
|
222
234
|
# Print the job info and make sure that doesn't crash.
|
|
223
|
-
subprocess.check_call(
|
|
224
|
-
"toil",
|
|
225
|
-
|
|
226
|
-
"--logDebug",
|
|
227
|
-
job_store,
|
|
228
|
-
"--printJobInfo",
|
|
229
|
-
job_id
|
|
230
|
-
])
|
|
235
|
+
subprocess.check_call(
|
|
236
|
+
["toil", "debug-job", "--logDebug", job_store, "--printJobInfo", job_id]
|
|
237
|
+
)
|
|
231
238
|
|
|
232
239
|
@needs_wdl
|
|
233
240
|
def test_retrieve_task_directory(self):
|
|
@@ -242,18 +249,23 @@ class DebugJobTest(ToilTest):
|
|
|
242
249
|
dest_dir = os.path.join(self._createTempDir(), "dump")
|
|
243
250
|
|
|
244
251
|
# Print the job info and make sure that doesn't crash.
|
|
245
|
-
subprocess.check_call(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
252
|
+
subprocess.check_call(
|
|
253
|
+
[
|
|
254
|
+
"toil",
|
|
255
|
+
"debug-job",
|
|
256
|
+
"--logDebug",
|
|
257
|
+
job_store,
|
|
258
|
+
job_name,
|
|
259
|
+
"--retrieveTaskDirectory",
|
|
260
|
+
dest_dir,
|
|
261
|
+
]
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
first_file = os.path.join(
|
|
265
|
+
dest_dir,
|
|
266
|
+
"inside/mnt/miniwdl_task_container/work/_miniwdl_inputs/0/test.txt",
|
|
267
|
+
)
|
|
268
|
+
assert os.path.exists(
|
|
269
|
+
first_file
|
|
270
|
+
), "Input file not found in fake container environment"
|
|
257
271
|
self.assertEqual(open(first_file).read(), "These are the contents\n")
|
|
258
|
-
|
|
259
|
-
|
toil/test/utils/toilKillTest.py
CHANGED
|
@@ -21,8 +21,7 @@ import time
|
|
|
21
21
|
import unittest
|
|
22
22
|
|
|
23
23
|
from toil.common import Toil
|
|
24
|
-
from toil.jobStores.abstractJobStore import
|
|
25
|
-
NoSuchJobStoreException)
|
|
24
|
+
from toil.jobStores.abstractJobStore import NoSuchFileException, NoSuchJobStoreException
|
|
26
25
|
from toil.jobStores.utils import generate_locator
|
|
27
26
|
from toil.test import ToilTest, needs_aws_s3, needs_cwl
|
|
28
27
|
|
|
@@ -61,7 +60,7 @@ class ToilKillTest(ToilTest):
|
|
|
61
60
|
kill_cmd = ["toil", "kill", self.job_store]
|
|
62
61
|
|
|
63
62
|
# run the sleep workflow
|
|
64
|
-
logger.info(
|
|
63
|
+
logger.info("Running workflow: %s", " ".join(run_cmd))
|
|
65
64
|
cwl_process = subprocess.Popen(run_cmd)
|
|
66
65
|
|
|
67
66
|
# wait until workflow starts running
|
|
@@ -75,9 +74,9 @@ class ToilKillTest(ToilTest):
|
|
|
75
74
|
# kill flag exists to be deleted to kill the leader
|
|
76
75
|
break
|
|
77
76
|
else:
|
|
78
|
-
logger.info(
|
|
77
|
+
logger.info("Waiting for kill flag...")
|
|
79
78
|
except (NoSuchJobStoreException, NoSuchFileException):
|
|
80
|
-
logger.info(
|
|
79
|
+
logger.info("Waiting for job store to be openable...")
|
|
81
80
|
time.sleep(2)
|
|
82
81
|
|
|
83
82
|
# run toil kill
|