toil 6.1.0a1__py3-none-any.whl → 8.0.0__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 +122 -315
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +173 -89
- toil/batchSystems/abstractGridEngineBatchSystem.py +272 -148
- toil/batchSystems/awsBatch.py +244 -135
- toil/batchSystems/cleanup_support.py +26 -16
- toil/batchSystems/contained_executor.py +31 -28
- toil/batchSystems/gridengine.py +86 -50
- toil/batchSystems/htcondor.py +166 -89
- toil/batchSystems/kubernetes.py +632 -382
- toil/batchSystems/local_support.py +20 -15
- toil/batchSystems/lsf.py +134 -81
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +290 -151
- toil/batchSystems/mesos/executor.py +79 -50
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +46 -28
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +296 -125
- toil/batchSystems/slurm.py +603 -138
- toil/batchSystems/torque.py +47 -33
- toil/bus.py +186 -76
- toil/common.py +664 -368
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1136 -483
- toil/cwl/utils.py +17 -22
- toil/deferred.py +63 -42
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +140 -60
- toil/fileStores/cachingFileStore.py +717 -269
- toil/fileStores/nonCachingFileStore.py +116 -87
- toil/job.py +1225 -368
- toil/jobStores/abstractJobStore.py +416 -266
- toil/jobStores/aws/jobStore.py +863 -477
- toil/jobStores/aws/utils.py +201 -120
- toil/jobStores/conftest.py +3 -2
- toil/jobStores/fileJobStore.py +292 -154
- toil/jobStores/googleJobStore.py +140 -74
- toil/jobStores/utils.py +36 -15
- toil/leader.py +668 -272
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +74 -31
- 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 +214 -39
- toil/lib/aws/utils.py +287 -231
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +104 -47
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +361 -199
- toil/lib/ec2nodes.py +174 -106
- 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 +5 -3
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/humanize.py +6 -2
- toil/lib/integration.py +341 -0
- toil/lib/io.py +141 -15
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +66 -21
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +68 -15
- toil/lib/retry.py +126 -81
- toil/lib/threading.py +299 -82
- toil/lib/throttle.py +16 -15
- toil/options/common.py +843 -409
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +73 -17
- toil/provisioners/__init__.py +117 -46
- toil/provisioners/abstractProvisioner.py +332 -157
- toil/provisioners/aws/__init__.py +70 -33
- toil/provisioners/aws/awsProvisioner.py +1145 -715
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +155 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +128 -62
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +82 -53
- toil/server/utils.py +54 -28
- 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 +224 -70
- toil/test/__init__.py +282 -183
- toil/test/batchSystems/batchSystemTest.py +460 -210
- toil/test/batchSystems/batch_system_plugin_test.py +90 -0
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +110 -49
- toil/test/cactus/__init__.py +0 -0
- toil/test/cactus/test_cactus_integration.py +56 -0
- toil/test/cwl/cwlTest.py +496 -287
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/cwl/seqtk_seq.cwl +1 -1
- toil/test/docs/scriptsTest.py +69 -46
- toil/test/jobStores/jobStoreTest.py +427 -264
- toil/test/lib/aws/test_iam.py +118 -50
- 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 +58 -50
- toil/test/lib/test_integration.py +104 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/__init__.py +13 -0
- toil/test/options/options.py +42 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +320 -150
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +166 -44
- 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 +141 -101
- 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 +32 -24
- toil/test/src/environmentTest.py +135 -0
- toil/test/src/fileStoreTest.py +539 -272
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +46 -21
- 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 +121 -71
- 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 +10 -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 +73 -23
- toil/test/utils/toilDebugTest.py +103 -33
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +245 -106
- toil/test/wdl/wdltoil_test.py +818 -149
- toil/test/wdl/wdltoil_test_kubernetes.py +91 -0
- toil/toilState.py +120 -35
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +214 -27
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +256 -140
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +32 -14
- toil/utils/toilSshCluster.py +49 -22
- toil/utils/toilStats.py +356 -273
- toil/utils/toilStatus.py +292 -139
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +12 -12
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3913 -1033
- toil/worker.py +367 -184
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/LICENSE +25 -0
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-6.1.0a1.dist-info/METADATA +0 -125
- toil-6.1.0a1.dist-info/RECORD +0 -237
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
toil/test/src/jobTest.py
CHANGED
|
@@ -29,6 +29,7 @@ logger = logging.getLogger(__name__)
|
|
|
29
29
|
|
|
30
30
|
class JobTest(ToilTest):
|
|
31
31
|
"""Tests the job class."""
|
|
32
|
+
|
|
32
33
|
@classmethod
|
|
33
34
|
def setUpClass(cls):
|
|
34
35
|
super().setUpClass()
|
|
@@ -121,9 +122,9 @@ class JobTest(ToilTest):
|
|
|
121
122
|
|
|
122
123
|
@slow
|
|
123
124
|
def testTrivialDAGConsistency(self):
|
|
124
|
-
options = Job.Runner.getDefaultOptions(self._createTempDir() +
|
|
125
|
-
options.clean =
|
|
126
|
-
options.logLevel =
|
|
125
|
+
options = Job.Runner.getDefaultOptions(self._createTempDir() + "/jobStore")
|
|
126
|
+
options.clean = "always"
|
|
127
|
+
options.logLevel = "debug"
|
|
127
128
|
i = Job.wrapJobFn(trivialParent)
|
|
128
129
|
with Toil(options) as toil:
|
|
129
130
|
try:
|
|
@@ -136,9 +137,9 @@ class JobTest(ToilTest):
|
|
|
136
137
|
|
|
137
138
|
@pytest.mark.timeout(300)
|
|
138
139
|
def testDAGConsistency(self):
|
|
139
|
-
options = Job.Runner.getDefaultOptions(self._createTempDir() +
|
|
140
|
-
options.clean =
|
|
141
|
-
options.logLevel =
|
|
140
|
+
options = Job.Runner.getDefaultOptions(self._createTempDir() + "/jobStore")
|
|
141
|
+
options.clean = "always"
|
|
142
|
+
options.logLevel = "debug"
|
|
142
143
|
i = Job.wrapJobFn(parent)
|
|
143
144
|
with Toil(options) as toil:
|
|
144
145
|
try:
|
|
@@ -155,9 +156,9 @@ class JobTest(ToilTest):
|
|
|
155
156
|
Slightly more complex case. The stranded job's predecessors are siblings instead of
|
|
156
157
|
parent/child.
|
|
157
158
|
"""
|
|
158
|
-
options = Job.Runner.getDefaultOptions(self._createTempDir() +
|
|
159
|
-
options.clean =
|
|
160
|
-
options.logLevel =
|
|
159
|
+
options = Job.Runner.getDefaultOptions(self._createTempDir() + "/jobStore")
|
|
160
|
+
options.clean = "always"
|
|
161
|
+
options.logLevel = "debug"
|
|
161
162
|
i = Job.wrapJobFn(diamond)
|
|
162
163
|
with Toil(options) as toil:
|
|
163
164
|
try:
|
|
@@ -196,8 +197,12 @@ class JobTest(ToilTest):
|
|
|
196
197
|
|
|
197
198
|
# Test making multiple roots
|
|
198
199
|
childEdges2 = childEdges.copy()
|
|
199
|
-
childEdges2.add(
|
|
200
|
-
|
|
200
|
+
childEdges2.add(
|
|
201
|
+
(nodeNumber, 1)
|
|
202
|
+
) # This creates an extra root at "nodeNumber"
|
|
203
|
+
rootJob2 = self.makeJobGraph(
|
|
204
|
+
nodeNumber + 1, childEdges2, followOnEdges, None, False
|
|
205
|
+
)
|
|
201
206
|
try:
|
|
202
207
|
rootJob2.checkJobGraphConnected()
|
|
203
208
|
self.assertTrue(False) # Multiple roots were not detected
|
|
@@ -209,8 +214,9 @@ class JobTest(ToilTest):
|
|
|
209
214
|
adjacencyList[fNode].add(tNode)
|
|
210
215
|
self.assertTrue(not self.isAcyclic(adjacencyList))
|
|
211
216
|
try:
|
|
212
|
-
self.makeJobGraph(
|
|
213
|
-
|
|
217
|
+
self.makeJobGraph(
|
|
218
|
+
nodeNumber, childEdges, followOnEdges, None
|
|
219
|
+
).checkJobGraphAcylic()
|
|
214
220
|
self.assertTrue(False) # A cycle was not detected
|
|
215
221
|
except JobGraphDeadlockException:
|
|
216
222
|
pass # This is the expected behaviour
|
|
@@ -218,22 +224,25 @@ class JobTest(ToilTest):
|
|
|
218
224
|
childEdges.remove((fNode, tNode))
|
|
219
225
|
adjacencyList[fNode].remove(tNode)
|
|
220
226
|
# Check is now acyclic again
|
|
221
|
-
self.makeJobGraph(
|
|
222
|
-
|
|
227
|
+
self.makeJobGraph(
|
|
228
|
+
nodeNumber, childEdges, followOnEdges, None, False
|
|
229
|
+
).checkJobGraphAcylic()
|
|
223
230
|
|
|
224
231
|
def checkFollowOnEdgeCycleDetection(fNode, tNode):
|
|
225
232
|
followOnEdges.add((fNode, tNode)) # Create a cycle
|
|
226
233
|
try:
|
|
227
|
-
self.makeJobGraph(
|
|
228
|
-
|
|
234
|
+
self.makeJobGraph(
|
|
235
|
+
nodeNumber, childEdges, followOnEdges, None, False
|
|
236
|
+
).checkJobGraphAcylic()
|
|
229
237
|
# self.assertTrue(False) #The cycle was not detected
|
|
230
238
|
except JobGraphDeadlockException:
|
|
231
239
|
pass # This is the expected behaviour
|
|
232
240
|
# Remove the edges
|
|
233
241
|
followOnEdges.remove((fNode, tNode))
|
|
234
242
|
# Check is now acyclic again
|
|
235
|
-
self.makeJobGraph(
|
|
236
|
-
|
|
243
|
+
self.makeJobGraph(
|
|
244
|
+
nodeNumber, childEdges, followOnEdges, None, False
|
|
245
|
+
).checkJobGraphAcylic()
|
|
237
246
|
|
|
238
247
|
# Now try adding edges that create a cycle
|
|
239
248
|
|
|
@@ -257,9 +266,16 @@ class JobTest(ToilTest):
|
|
|
257
266
|
|
|
258
267
|
# Try adding a follow on edge between two nodes with shared descendants
|
|
259
268
|
fNode, tNode = self.getRandomEdge(nodeNumber)
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
269
|
+
if (
|
|
270
|
+
len(
|
|
271
|
+
self.reachable(tNode, adjacencyList).intersection(
|
|
272
|
+
self.reachable(fNode, adjacencyList)
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
> 0
|
|
276
|
+
and (fNode, tNode) not in childEdges
|
|
277
|
+
and (fNode, tNode) not in followOnEdges
|
|
278
|
+
):
|
|
263
279
|
checkFollowOnEdgeCycleDetection(fNode, tNode)
|
|
264
280
|
|
|
265
281
|
@slow
|
|
@@ -268,7 +284,7 @@ class JobTest(ToilTest):
|
|
|
268
284
|
Test for issue #1465: Detection of checkpoint jobs that are not leaf vertices
|
|
269
285
|
identifies leaf vertices incorrectly
|
|
270
286
|
|
|
271
|
-
Test verification of new checkpoint jobs being leaf
|
|
287
|
+
Test verification of new checkpoint jobs being leaf vertices,
|
|
272
288
|
starting with the following baseline workflow::
|
|
273
289
|
|
|
274
290
|
Parent
|
|
@@ -279,7 +295,9 @@ class JobTest(ToilTest):
|
|
|
279
295
|
|
|
280
296
|
def createWorkflow():
|
|
281
297
|
rootJob = Job.wrapJobFn(simpleJobFn, "Parent")
|
|
282
|
-
childCheckpointJob = rootJob.addChildJobFn(
|
|
298
|
+
childCheckpointJob = rootJob.addChildJobFn(
|
|
299
|
+
simpleJobFn, "Child", checkpoint=True
|
|
300
|
+
)
|
|
283
301
|
return rootJob, childCheckpointJob
|
|
284
302
|
|
|
285
303
|
self.runNewCheckpointIsLeafVertexTest(createWorkflow)
|
|
@@ -315,35 +333,45 @@ class JobTest(ToilTest):
|
|
|
315
333
|
|
|
316
334
|
"""
|
|
317
335
|
|
|
318
|
-
logger.debug(
|
|
319
|
-
self.runCheckpointVertexTest(*createWorkflowFn(),
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
336
|
+
logger.debug("Test checkpoint job that is a leaf vertex")
|
|
337
|
+
self.runCheckpointVertexTest(*createWorkflowFn(), expectedException=None)
|
|
338
|
+
|
|
339
|
+
logger.debug(
|
|
340
|
+
"Test checkpoint job that is not a leaf vertex due to the presence of a service"
|
|
341
|
+
)
|
|
342
|
+
self.runCheckpointVertexTest(
|
|
343
|
+
*createWorkflowFn(),
|
|
344
|
+
checkpointJobService=TrivialService("LeafTestService"),
|
|
345
|
+
expectedException=JobGraphDeadlockException
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
logger.debug(
|
|
349
|
+
"Test checkpoint job that is not a leaf vertex due to the presence of a child job"
|
|
350
|
+
)
|
|
351
|
+
self.runCheckpointVertexTest(
|
|
352
|
+
*createWorkflowFn(),
|
|
353
|
+
checkpointJobChild=Job.wrapJobFn(simpleJobFn, "LeafTestChild"),
|
|
354
|
+
expectedException=JobGraphDeadlockException
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
logger.debug(
|
|
358
|
+
"Test checkpoint job that is not a leaf vertex due to the presence of a follow-on job"
|
|
359
|
+
)
|
|
360
|
+
self.runCheckpointVertexTest(
|
|
361
|
+
*createWorkflowFn(),
|
|
362
|
+
checkpointJobFollowOn=Job.wrapJobFn(simpleJobFn, "LeafTestFollowOn"),
|
|
363
|
+
expectedException=JobGraphDeadlockException
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
def runCheckpointVertexTest(
|
|
367
|
+
self,
|
|
368
|
+
workflowRootJob,
|
|
369
|
+
checkpointJob,
|
|
370
|
+
checkpointJobService=None,
|
|
371
|
+
checkpointJobChild=None,
|
|
372
|
+
checkpointJobFollowOn=None,
|
|
373
|
+
expectedException=None,
|
|
374
|
+
):
|
|
347
375
|
"""
|
|
348
376
|
Modifies the checkpoint job according to the given parameters
|
|
349
377
|
then runs the workflow, checking for the expected exception, if any.
|
|
@@ -380,7 +408,7 @@ class JobTest(ToilTest):
|
|
|
380
408
|
jobStore = self._getTestJobStorePath()
|
|
381
409
|
for test in range(5):
|
|
382
410
|
# Temporary file
|
|
383
|
-
tempDir = self._createTempDir(purpose=
|
|
411
|
+
tempDir = self._createTempDir(purpose="tempDir")
|
|
384
412
|
# Make a random DAG for the set of child edges
|
|
385
413
|
nodeNumber = random.choice(range(2, 8))
|
|
386
414
|
childEdges = self.makeRandomDAG(nodeNumber)
|
|
@@ -421,8 +449,8 @@ class JobTest(ToilTest):
|
|
|
421
449
|
numberOfFailedJobs = 0
|
|
422
450
|
except FailedJobsException as e:
|
|
423
451
|
numberOfFailedJobs = e.numberOfFailedJobs
|
|
424
|
-
if totalTrys > 32:
|
|
425
|
-
self.fail()
|
|
452
|
+
if totalTrys > 32: # p(fail after this many restarts) ~= 0.5**32
|
|
453
|
+
self.fail() # Exceeded a reasonable number of restarts
|
|
426
454
|
totalTrys += 1
|
|
427
455
|
|
|
428
456
|
# For each job check it created a valid output file and add the ordering
|
|
@@ -447,7 +475,7 @@ class JobTest(ToilTest):
|
|
|
447
475
|
def getRandomEdge(nodeNumber):
|
|
448
476
|
assert nodeNumber > 1
|
|
449
477
|
fNode = random.choice(range(nodeNumber - 1))
|
|
450
|
-
return fNode, random.choice(range(fNode+1, nodeNumber))
|
|
478
|
+
return fNode, random.choice(range(fNode + 1, nodeNumber))
|
|
451
479
|
|
|
452
480
|
@staticmethod
|
|
453
481
|
def makeRandomDAG(nodeNumber):
|
|
@@ -457,7 +485,9 @@ class JobTest(ToilTest):
|
|
|
457
485
|
referring to nodes and the edge is from a to b.
|
|
458
486
|
"""
|
|
459
487
|
# Pick number of total edges to create
|
|
460
|
-
edgeNumber = random.choice(
|
|
488
|
+
edgeNumber = random.choice(
|
|
489
|
+
range(nodeNumber - 1, 1 + (nodeNumber * (nodeNumber - 1) // 2))
|
|
490
|
+
)
|
|
461
491
|
# Make a spanning tree of edges so that nodes are connected
|
|
462
492
|
edges = {(random.choice(range(i)), i) for i in range(1, nodeNumber)}
|
|
463
493
|
# Add extra random edges until there are edgeNumber edges
|
|
@@ -499,8 +529,10 @@ class JobTest(ToilTest):
|
|
|
499
529
|
"""
|
|
500
530
|
|
|
501
531
|
def makeAugmentedAdjacencyList():
|
|
502
|
-
augmentedAdjacencyList = [
|
|
503
|
-
|
|
532
|
+
augmentedAdjacencyList = [
|
|
533
|
+
childAdjacencyList[i].union(followOnAdjacencyList[i])
|
|
534
|
+
for i in range(len(childAdjacencyList))
|
|
535
|
+
]
|
|
504
536
|
|
|
505
537
|
def addImpliedEdges(node, followOnEdges):
|
|
506
538
|
# Let node2 be a child of node or a successor of a child of node.
|
|
@@ -550,7 +582,9 @@ class JobTest(ToilTest):
|
|
|
550
582
|
|
|
551
583
|
return followOnEdges
|
|
552
584
|
|
|
553
|
-
def makeJobGraph(
|
|
585
|
+
def makeJobGraph(
|
|
586
|
+
self, nodeNumber, childEdges, followOnEdges, outPath, addServices=True
|
|
587
|
+
):
|
|
554
588
|
"""
|
|
555
589
|
Converts a DAG into a job graph. childEdges and followOnEdges are the lists of child and
|
|
556
590
|
followOn edges.
|
|
@@ -560,9 +594,15 @@ class JobTest(ToilTest):
|
|
|
560
594
|
|
|
561
595
|
def makeJob(string):
|
|
562
596
|
promises = []
|
|
563
|
-
job = Job.wrapFn(
|
|
564
|
-
|
|
565
|
-
|
|
597
|
+
job = Job.wrapFn(
|
|
598
|
+
fn2Test,
|
|
599
|
+
promises,
|
|
600
|
+
string,
|
|
601
|
+
None if outPath is None else os.path.join(outPath, string),
|
|
602
|
+
cores=0.1,
|
|
603
|
+
memory="0.5G",
|
|
604
|
+
disk="0.1G",
|
|
605
|
+
)
|
|
566
606
|
jobsToPromisesMap[job] = promises
|
|
567
607
|
return job
|
|
568
608
|
|
|
@@ -581,7 +621,16 @@ class JobTest(ToilTest):
|
|
|
581
621
|
predecessors[jobs[tNode]].append(jobs[fNode])
|
|
582
622
|
|
|
583
623
|
# Map of jobs to return values
|
|
584
|
-
jobsToRvs = {
|
|
624
|
+
jobsToRvs = {
|
|
625
|
+
job: (
|
|
626
|
+
job.addService(
|
|
627
|
+
TrivialService(job.rv(), cores=0.1, memory="0.5G", disk="0.1G")
|
|
628
|
+
)
|
|
629
|
+
if addServices
|
|
630
|
+
else job.rv()
|
|
631
|
+
)
|
|
632
|
+
for job in jobs
|
|
633
|
+
}
|
|
585
634
|
|
|
586
635
|
def getRandomPredecessor(job):
|
|
587
636
|
predecessor = random.choice(list(predecessors[job]))
|
|
@@ -622,9 +671,11 @@ class JobTest(ToilTest):
|
|
|
622
671
|
return False
|
|
623
672
|
return True
|
|
624
673
|
|
|
674
|
+
|
|
625
675
|
def simpleJobFn(job, value):
|
|
626
676
|
job.fileStore.log_to_leader(value)
|
|
627
677
|
|
|
678
|
+
|
|
628
679
|
def fn1Test(string, outputFile):
|
|
629
680
|
"""
|
|
630
681
|
Function appends the next character after the last character in the given
|
|
@@ -633,7 +684,7 @@ def fn1Test(string, outputFile):
|
|
|
633
684
|
"""
|
|
634
685
|
|
|
635
686
|
rV = string + chr(ord(string[-1]) + 1)
|
|
636
|
-
with open(outputFile,
|
|
687
|
+
with open(outputFile, "w") as fH:
|
|
637
688
|
fH.write(rV)
|
|
638
689
|
return rV
|
|
639
690
|
|
|
@@ -643,7 +694,7 @@ def fn2Test(pStrings, s, outputFile):
|
|
|
643
694
|
Function concatenates the strings in pStrings and s, in that order, and writes the result to
|
|
644
695
|
the output file. Returns s.
|
|
645
696
|
"""
|
|
646
|
-
with open(outputFile,
|
|
697
|
+
with open(outputFile, "w") as fH:
|
|
647
698
|
fH.write(" ".join(pStrings) + " " + s)
|
|
648
699
|
return s
|
|
649
700
|
|
|
@@ -687,13 +738,12 @@ def child(job):
|
|
|
687
738
|
|
|
688
739
|
|
|
689
740
|
def errorChild(job):
|
|
690
|
-
raise RuntimeError(
|
|
741
|
+
raise RuntimeError("Child failure")
|
|
691
742
|
|
|
692
743
|
|
|
693
744
|
class TrivialService(Job.Service):
|
|
694
745
|
def __init__(self, message, *args, **kwargs):
|
|
695
|
-
"""
|
|
696
|
-
"""
|
|
746
|
+
"""Service that does nothing, used to check for deadlocks"""
|
|
697
747
|
Job.Service.__init__(self, *args, **kwargs)
|
|
698
748
|
self.message = message
|
|
699
749
|
|
|
@@ -707,5 +757,5 @@ class TrivialService(Job.Service):
|
|
|
707
757
|
pass
|
|
708
758
|
|
|
709
759
|
|
|
710
|
-
if __name__ ==
|
|
760
|
+
if __name__ == "__main__":
|
|
711
761
|
unittest.main()
|
toil/test/src/miscTests.py
CHANGED
|
@@ -20,10 +20,7 @@ from uuid import uuid4
|
|
|
20
20
|
|
|
21
21
|
from toil.common import getNodeID
|
|
22
22
|
from toil.lib.exceptions import panic, raise_
|
|
23
|
-
from toil.lib.io import
|
|
24
|
-
atomic_install,
|
|
25
|
-
atomic_tmp_file,
|
|
26
|
-
mkdtemp)
|
|
23
|
+
from toil.lib.io import AtomicFileCreate, atomic_install, atomic_tmp_file, mkdtemp
|
|
27
24
|
from toil.lib.misc import CalledProcessErrorStderr, call_command
|
|
28
25
|
from toil.test import ToilTest, slow
|
|
29
26
|
|
|
@@ -36,6 +33,7 @@ class MiscTests(ToilTest):
|
|
|
36
33
|
This class contains miscellaneous tests that don't have enough content to be their own test
|
|
37
34
|
file, and that don't logically fit in with any of the other test suites.
|
|
38
35
|
"""
|
|
36
|
+
|
|
39
37
|
def setUp(self):
|
|
40
38
|
super().setUp()
|
|
41
39
|
self.testDir = self._createTempDir()
|
|
@@ -49,14 +47,14 @@ class MiscTests(ToilTest):
|
|
|
49
47
|
|
|
50
48
|
@slow
|
|
51
49
|
def testGetSizeOfDirectoryWorks(self):
|
|
52
|
-
|
|
50
|
+
"""A test to make sure toil.common.getDirSizeRecursively does not
|
|
53
51
|
underestimate the amount of disk space needed.
|
|
54
52
|
|
|
55
53
|
Disk space allocation varies from system to system. The computed value
|
|
56
54
|
should always be equal to or slightly greater than the creation value.
|
|
57
55
|
This test generates a number of random directories and randomly sized
|
|
58
56
|
files to test this using getDirSizeRecursively.
|
|
59
|
-
|
|
57
|
+
"""
|
|
60
58
|
from toil.common import getDirSizeRecursively
|
|
61
59
|
|
|
62
60
|
# a list of the directories used in the test
|
|
@@ -64,17 +62,17 @@ class MiscTests(ToilTest):
|
|
|
64
62
|
# A dict of {FILENAME: FILESIZE} for all files used in the test
|
|
65
63
|
files = {}
|
|
66
64
|
# Create a random directory structure
|
|
67
|
-
for i in range(0,10):
|
|
68
|
-
directories.append(mkdtemp(dir=random.choice(directories), prefix=
|
|
65
|
+
for i in range(0, 10):
|
|
66
|
+
directories.append(mkdtemp(dir=random.choice(directories), prefix="test"))
|
|
69
67
|
# Create 50 random file entries in different locations in the directories. 75% of the time
|
|
70
68
|
# these are fresh files of size [1, 10] MB and 25% of the time they are hard links to old
|
|
71
69
|
# files.
|
|
72
70
|
while len(files) <= 50:
|
|
73
71
|
fileName = os.path.join(random.choice(directories), self._getRandomName())
|
|
74
|
-
if random.randint(0,100) < 75:
|
|
72
|
+
if random.randint(0, 100) < 75:
|
|
75
73
|
# Create a fresh file in the range of 1-10 MB
|
|
76
74
|
fileSize = int(round(random.random(), 2) * 10 * 1024 * 1024)
|
|
77
|
-
with open(fileName,
|
|
75
|
+
with open(fileName, "wb") as fileHandle:
|
|
78
76
|
fileHandle.write(os.urandom(fileSize))
|
|
79
77
|
files[fileName] = fileSize
|
|
80
78
|
else:
|
|
@@ -83,7 +81,7 @@ class MiscTests(ToilTest):
|
|
|
83
81
|
continue
|
|
84
82
|
linkSrc = random.choice(list(files.keys()))
|
|
85
83
|
os.link(linkSrc, fileName)
|
|
86
|
-
files[fileName] =
|
|
84
|
+
files[fileName] = "Link to %s" % linkSrc
|
|
87
85
|
|
|
88
86
|
computedDirectorySize = getDirSizeRecursively(self.testDir)
|
|
89
87
|
totalExpectedSize = sum(x for x in list(files.values()) if isinstance(x, int))
|
|
@@ -101,7 +99,7 @@ class MiscTests(ToilTest):
|
|
|
101
99
|
|
|
102
100
|
def _write_test_file(self, outf_tmp):
|
|
103
101
|
with open(outf_tmp, "w") as fh:
|
|
104
|
-
fh.write(self.id() +
|
|
102
|
+
fh.write(self.id() + "\n")
|
|
105
103
|
|
|
106
104
|
def test_atomic_install(self):
|
|
107
105
|
outf = self._get_test_out_file(".foo.gz")
|
|
@@ -111,7 +109,7 @@ class MiscTests(ToilTest):
|
|
|
111
109
|
self.assertTrue(os.path.exists(outf))
|
|
112
110
|
|
|
113
111
|
def test_atomic_install_dev(self):
|
|
114
|
-
devn =
|
|
112
|
+
devn = "/dev/null"
|
|
115
113
|
tmp = atomic_tmp_file(devn)
|
|
116
114
|
self.assertEqual(tmp, devn)
|
|
117
115
|
atomic_install(tmp, devn)
|
|
@@ -138,10 +136,13 @@ class MiscTests(ToilTest):
|
|
|
138
136
|
self.assertTrue(isinstance(o, str), str(type(o)))
|
|
139
137
|
|
|
140
138
|
def test_call_command_err(self):
|
|
141
|
-
with self.assertRaisesRegex(
|
|
142
|
-
|
|
139
|
+
with self.assertRaisesRegex(
|
|
140
|
+
CalledProcessErrorStderr,
|
|
141
|
+
"^Command '\\['cat', '/dev/Frankenheimer']' exit status 1: cat: /dev/Frankenheimer: No such file or directory\n$",
|
|
142
|
+
):
|
|
143
143
|
call_command(["cat", "/dev/Frankenheimer"])
|
|
144
144
|
|
|
145
|
+
|
|
145
146
|
class TestPanic(ToilTest):
|
|
146
147
|
def test_panic_by_hand(self):
|
|
147
148
|
try:
|
|
@@ -192,7 +193,7 @@ class TestPanic(ToilTest):
|
|
|
192
193
|
self.line_of_primary_exc = inspect.currentframe().f_lineno + 1
|
|
193
194
|
raise ValueError("primary")
|
|
194
195
|
except:
|
|
195
|
-
with panic(
|
|
196
|
+
with panic(log):
|
|
196
197
|
raise RuntimeError("secondary")
|
|
197
198
|
|
|
198
199
|
def try_and_nested_panic_with_secondary(self):
|
|
@@ -200,8 +201,8 @@ class TestPanic(ToilTest):
|
|
|
200
201
|
self.line_of_primary_exc = inspect.currentframe().f_lineno + 1
|
|
201
202
|
raise ValueError("primary")
|
|
202
203
|
except:
|
|
203
|
-
with panic(
|
|
204
|
-
with panic(
|
|
204
|
+
with panic(log):
|
|
205
|
+
with panic(log):
|
|
205
206
|
raise RuntimeError("secondary")
|
|
206
207
|
|
|
207
208
|
def __assert_raised_exception_is_primary(self):
|