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/utils/utilsTest.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
|
+
import re
|
|
16
17
|
import shutil
|
|
17
18
|
import subprocess
|
|
18
19
|
import sys
|
|
@@ -22,7 +23,7 @@ from unittest.mock import patch
|
|
|
22
23
|
|
|
23
24
|
import pytest
|
|
24
25
|
|
|
25
|
-
pkg_root = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
|
26
|
+
pkg_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) # noqa
|
|
26
27
|
sys.path.insert(0, pkg_root) # noqa
|
|
27
28
|
|
|
28
29
|
import toil
|
|
@@ -30,16 +31,18 @@ from toil import resolveEntryPoint
|
|
|
30
31
|
from toil.common import Config, Toil
|
|
31
32
|
from toil.job import Job
|
|
32
33
|
from toil.lib.bioio import system
|
|
33
|
-
from toil.test import (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
from toil.test import (
|
|
35
|
+
ToilTest,
|
|
36
|
+
get_temp_file,
|
|
37
|
+
integrative,
|
|
38
|
+
needs_aws_ec2,
|
|
39
|
+
needs_cwl,
|
|
40
|
+
needs_docker,
|
|
41
|
+
needs_rsync3,
|
|
42
|
+
slow,
|
|
43
|
+
)
|
|
41
44
|
from toil.test.sort.sortTest import makeFileToSort
|
|
42
|
-
from toil.utils.toilStats import
|
|
45
|
+
from toil.utils.toilStats import get_stats, process_data
|
|
43
46
|
from toil.utils.toilStatus import ToilStatus
|
|
44
47
|
from toil.version import python
|
|
45
48
|
|
|
@@ -56,7 +59,8 @@ class UtilsTest(ToilTest):
|
|
|
56
59
|
super().setUp()
|
|
57
60
|
self.tempDir = self._createTempDir()
|
|
58
61
|
self.tempFile = get_temp_file(rootDir=self.tempDir)
|
|
59
|
-
self.outputFile =
|
|
62
|
+
self.outputFile = get_temp_file(rootDir=self.tempDir)
|
|
63
|
+
self.outputFile = "someSortedStuff.txt"
|
|
60
64
|
self.toilDir = os.path.join(self.tempDir, "jobstore")
|
|
61
65
|
self.assertFalse(os.path.exists(self.toilDir))
|
|
62
66
|
self.lines = 1000
|
|
@@ -70,19 +74,19 @@ class UtilsTest(ToilTest):
|
|
|
70
74
|
|
|
71
75
|
self.sort_workflow_cmd = [
|
|
72
76
|
python,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
f
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
"-m",
|
|
78
|
+
"toil.test.sort.sort",
|
|
79
|
+
f"file:{self.toilDir}",
|
|
80
|
+
f"--fileToSort={self.tempFile}",
|
|
81
|
+
f"--outputFile={self.outputFile}",
|
|
82
|
+
"--clean=never",
|
|
79
83
|
]
|
|
80
84
|
|
|
81
85
|
self.restart_sort_workflow_cmd = [
|
|
82
86
|
python,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
f
|
|
87
|
+
"-m",
|
|
88
|
+
"toil.test.sort.restart_sort",
|
|
89
|
+
f"file:{self.toilDir}",
|
|
86
90
|
]
|
|
87
91
|
|
|
88
92
|
def tearDown(self):
|
|
@@ -91,7 +95,11 @@ class UtilsTest(ToilTest):
|
|
|
91
95
|
if os.path.exists(self.toilDir):
|
|
92
96
|
shutil.rmtree(self.toilDir)
|
|
93
97
|
|
|
94
|
-
for f in [
|
|
98
|
+
for f in [
|
|
99
|
+
self.tempFile,
|
|
100
|
+
self.outputFile,
|
|
101
|
+
os.path.join(self.tempDir, "output.txt"),
|
|
102
|
+
]:
|
|
95
103
|
if os.path.exists(f):
|
|
96
104
|
os.remove(f)
|
|
97
105
|
|
|
@@ -99,26 +107,26 @@ class UtilsTest(ToilTest):
|
|
|
99
107
|
|
|
100
108
|
@property
|
|
101
109
|
def toilMain(self):
|
|
102
|
-
return resolveEntryPoint(
|
|
110
|
+
return resolveEntryPoint("toil")
|
|
103
111
|
|
|
104
112
|
@property
|
|
105
113
|
def cleanCommand(self):
|
|
106
|
-
return [self.toilMain,
|
|
114
|
+
return [self.toilMain, "clean", self.toilDir]
|
|
107
115
|
|
|
108
116
|
@property
|
|
109
117
|
def statsCommand(self):
|
|
110
|
-
return [self.toilMain,
|
|
118
|
+
return [self.toilMain, "stats", self.toilDir, "--pretty"]
|
|
111
119
|
|
|
112
120
|
def statusCommand(self, failIfNotComplete=False):
|
|
113
|
-
commandTokens = [self.toilMain,
|
|
121
|
+
commandTokens = [self.toilMain, "status", self.toilDir]
|
|
114
122
|
if failIfNotComplete:
|
|
115
|
-
commandTokens.append(
|
|
123
|
+
commandTokens.append("--failIfNotComplete")
|
|
116
124
|
return commandTokens
|
|
117
125
|
|
|
118
126
|
def test_config_functionality(self):
|
|
119
127
|
"""Ensure that creating and reading back the config file works"""
|
|
120
128
|
config_file = os.path.abspath("config.yaml")
|
|
121
|
-
config_command = [self.toilMain,
|
|
129
|
+
config_command = [self.toilMain, "config", config_file]
|
|
122
130
|
# make sure the command `toil config file_path` works
|
|
123
131
|
try:
|
|
124
132
|
subprocess.check_call(config_command)
|
|
@@ -129,12 +137,18 @@ class UtilsTest(ToilTest):
|
|
|
129
137
|
# make sure that toil can read from the generated config file
|
|
130
138
|
try:
|
|
131
139
|
parser.parse_args(["random_jobstore", "--config", config_file])
|
|
140
|
+
with open(config_file) as cm:
|
|
141
|
+
payload = cm.read()
|
|
142
|
+
expected = "workDir batchSystem symlinkImports defaultMemory retryCount"
|
|
143
|
+
assert all(
|
|
144
|
+
re.search(rf"^#*{ param }:", payload, re.MULTILINE)
|
|
145
|
+
for param in expected.split(" ")
|
|
146
|
+
), f"Generated config contains { expected }"
|
|
132
147
|
except SystemExit:
|
|
133
148
|
self.fail("Failed to parse the default generated config file!")
|
|
134
149
|
finally:
|
|
135
150
|
os.remove(config_file)
|
|
136
151
|
|
|
137
|
-
|
|
138
152
|
@needs_rsync3
|
|
139
153
|
@pytest.mark.timeout(1200)
|
|
140
154
|
@needs_aws_ec2
|
|
@@ -155,30 +169,63 @@ class UtilsTest(ToilTest):
|
|
|
155
169
|
:return:
|
|
156
170
|
"""
|
|
157
171
|
# TODO: Run these for the other clouds.
|
|
158
|
-
clusterName = f
|
|
159
|
-
keyName = os.getenv(
|
|
160
|
-
expected_owner = os.getenv(
|
|
172
|
+
clusterName = f"cluster-utils-test{uuid.uuid4()}"
|
|
173
|
+
keyName = os.getenv("TOIL_AWS_KEYNAME").strip() or "id_rsa"
|
|
174
|
+
expected_owner = os.getenv("TOIL_OWNER_TAG") or keyName
|
|
161
175
|
|
|
162
176
|
try:
|
|
163
177
|
from toil.provisioners.aws.awsProvisioner import AWSProvisioner
|
|
178
|
+
|
|
164
179
|
aws_provisioner = AWSProvisioner.__module__
|
|
165
180
|
logger.debug(f"Found AWSProvisioner: {aws_provisioner}.")
|
|
166
181
|
|
|
167
182
|
# launch master with an assortment of custom tags
|
|
168
|
-
system(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
183
|
+
system(
|
|
184
|
+
[
|
|
185
|
+
self.toilMain,
|
|
186
|
+
"launch-cluster",
|
|
187
|
+
"--clusterType",
|
|
188
|
+
"mesos",
|
|
189
|
+
"-t",
|
|
190
|
+
"key1=value1",
|
|
191
|
+
"-t",
|
|
192
|
+
"key2=value2",
|
|
193
|
+
"--tag",
|
|
194
|
+
"key3=value3",
|
|
195
|
+
"--leaderNodeType=t2.medium",
|
|
196
|
+
"--keyPairName=" + keyName,
|
|
197
|
+
clusterName,
|
|
198
|
+
"--provisioner=aws",
|
|
199
|
+
"--zone=us-west-2a",
|
|
200
|
+
"--logLevel=DEBUG",
|
|
201
|
+
]
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
cluster = toil.provisioners.cluster_factory(
|
|
205
|
+
provisioner="aws", zone="us-west-2a", clusterName=clusterName
|
|
206
|
+
)
|
|
174
207
|
leader = cluster.getLeader()
|
|
175
208
|
|
|
176
209
|
# check that the leader carries the appropriate tags
|
|
177
|
-
tags = {
|
|
210
|
+
tags = {
|
|
211
|
+
"key1": "value1",
|
|
212
|
+
"key2": "value2",
|
|
213
|
+
"key3": "value3",
|
|
214
|
+
"Name": clusterName,
|
|
215
|
+
"Owner": expected_owner,
|
|
216
|
+
}
|
|
178
217
|
for key in tags:
|
|
179
218
|
self.assertEqual(leader.tags.get(key), tags[key])
|
|
180
219
|
finally:
|
|
181
|
-
system(
|
|
220
|
+
system(
|
|
221
|
+
[
|
|
222
|
+
self.toilMain,
|
|
223
|
+
"destroy-cluster",
|
|
224
|
+
"--zone=us-west-2a",
|
|
225
|
+
"--provisioner=aws",
|
|
226
|
+
clusterName,
|
|
227
|
+
]
|
|
228
|
+
)
|
|
182
229
|
|
|
183
230
|
@slow
|
|
184
231
|
def testUtilsSort(self):
|
|
@@ -187,19 +234,27 @@ class UtilsTest(ToilTest):
|
|
|
187
234
|
sort example with the --restart flag.
|
|
188
235
|
"""
|
|
189
236
|
# Get the sort command to run
|
|
190
|
-
toilCommand = [
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
237
|
+
toilCommand = [
|
|
238
|
+
sys.executable,
|
|
239
|
+
"-m",
|
|
240
|
+
toil.test.sort.sort.__name__,
|
|
241
|
+
self.toilDir,
|
|
242
|
+
"--logLevel=DEBUG",
|
|
243
|
+
"--fileToSort",
|
|
244
|
+
self.tempFile,
|
|
245
|
+
"--outputFile",
|
|
246
|
+
self.outputFile,
|
|
247
|
+
"--N",
|
|
248
|
+
str(self.N),
|
|
249
|
+
"--stats",
|
|
250
|
+
"--retryCount=2",
|
|
251
|
+
"--badWorker=0.5",
|
|
252
|
+
"--badWorkerFailInterval=0.05",
|
|
253
|
+
]
|
|
201
254
|
# Try restarting it to check that a JobStoreException is thrown
|
|
202
|
-
self.assertRaises(
|
|
255
|
+
self.assertRaises(
|
|
256
|
+
subprocess.CalledProcessError, system, toilCommand + ["--restart"]
|
|
257
|
+
)
|
|
203
258
|
# Check that trying to run it in restart mode does not create the jobStore
|
|
204
259
|
self.assertFalse(os.path.exists(self.toilDir))
|
|
205
260
|
|
|
@@ -208,9 +263,15 @@ class UtilsTest(ToilTest):
|
|
|
208
263
|
try:
|
|
209
264
|
system(toilCommand)
|
|
210
265
|
finished = True
|
|
211
|
-
except
|
|
266
|
+
except (
|
|
267
|
+
subprocess.CalledProcessError
|
|
268
|
+
): # This happens when the script fails due to having unfinished jobs
|
|
212
269
|
system(self.statusCommand())
|
|
213
|
-
self.assertRaises(
|
|
270
|
+
self.assertRaises(
|
|
271
|
+
subprocess.CalledProcessError,
|
|
272
|
+
system,
|
|
273
|
+
self.statusCommand(failIfNotComplete=True),
|
|
274
|
+
)
|
|
214
275
|
finished = False
|
|
215
276
|
self.assertTrue(os.path.exists(self.toilDir))
|
|
216
277
|
|
|
@@ -221,11 +282,17 @@ class UtilsTest(ToilTest):
|
|
|
221
282
|
totalTrys = 1
|
|
222
283
|
while not finished:
|
|
223
284
|
try:
|
|
224
|
-
system(toilCommand + [
|
|
285
|
+
system(toilCommand + ["--restart"])
|
|
225
286
|
finished = True
|
|
226
|
-
except
|
|
287
|
+
except (
|
|
288
|
+
subprocess.CalledProcessError
|
|
289
|
+
): # This happens when the script fails due to having unfinished jobs
|
|
227
290
|
system(self.statusCommand())
|
|
228
|
-
self.assertRaises(
|
|
291
|
+
self.assertRaises(
|
|
292
|
+
subprocess.CalledProcessError,
|
|
293
|
+
system,
|
|
294
|
+
self.statusCommand(failIfNotComplete=True),
|
|
295
|
+
)
|
|
229
296
|
if totalTrys > 16:
|
|
230
297
|
self.fail() # Exceeded a reasonable number of restarts
|
|
231
298
|
totalTrys += 1
|
|
@@ -253,17 +320,23 @@ class UtilsTest(ToilTest):
|
|
|
253
320
|
Tests the stats commands on a complete run of the stats test.
|
|
254
321
|
"""
|
|
255
322
|
# Get the sort command to run
|
|
256
|
-
toilCommand = [
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
323
|
+
toilCommand = [
|
|
324
|
+
sys.executable,
|
|
325
|
+
"-m",
|
|
326
|
+
toil.test.sort.sort.__name__,
|
|
327
|
+
self.toilDir,
|
|
328
|
+
"--logLevel=DEBUG",
|
|
329
|
+
"--fileToSort",
|
|
330
|
+
self.tempFile,
|
|
331
|
+
"--outputFile",
|
|
332
|
+
self.outputFile,
|
|
333
|
+
"--N",
|
|
334
|
+
str(self.N),
|
|
335
|
+
"--stats",
|
|
336
|
+
"--retryCount=99",
|
|
337
|
+
"--badWorker=0.5",
|
|
338
|
+
"--badWorkerFailInterval=0.01",
|
|
339
|
+
]
|
|
267
340
|
|
|
268
341
|
# Run the script for the first time
|
|
269
342
|
system(toilCommand)
|
|
@@ -282,8 +355,8 @@ class UtilsTest(ToilTest):
|
|
|
282
355
|
|
|
283
356
|
def testUnicodeSupport(self):
|
|
284
357
|
options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
|
|
285
|
-
options.clean =
|
|
286
|
-
options.logLevel =
|
|
358
|
+
options.clean = "always"
|
|
359
|
+
options.logLevel = "debug"
|
|
287
360
|
Job.Runner.startToil(Job.wrapFn(printUnicodeCharacter), options)
|
|
288
361
|
|
|
289
362
|
@slow
|
|
@@ -292,36 +365,60 @@ class UtilsTest(ToilTest):
|
|
|
292
365
|
Tests case where multiple jobs are run on 1 worker to ensure that all jobs report back their data
|
|
293
366
|
"""
|
|
294
367
|
options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
|
|
295
|
-
options.clean =
|
|
368
|
+
options.clean = "never"
|
|
296
369
|
options.stats = True
|
|
297
370
|
Job.Runner.startToil(RunTwoJobsPerWorker(), options)
|
|
298
371
|
config = Config()
|
|
299
372
|
config.setOptions(options)
|
|
300
373
|
jobStore = Toil.resumeJobStore(config.jobStore)
|
|
301
|
-
stats =
|
|
302
|
-
collatedStats =
|
|
303
|
-
self.assertTrue(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
374
|
+
stats = get_stats(jobStore)
|
|
375
|
+
collatedStats = process_data(jobStore.config, stats)
|
|
376
|
+
self.assertTrue(
|
|
377
|
+
len(collatedStats.job_types) == 2,
|
|
378
|
+
"Some jobs are not represented in the stats.",
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def check_status(self, status, status_fn, process=None, seconds=20):
|
|
382
|
+
time_elapsed = 0.0
|
|
383
|
+
has_stopped = process.poll() is not None if process else False
|
|
384
|
+
current_status = status_fn(self.toilDir)
|
|
385
|
+
while current_status != status:
|
|
386
|
+
if has_stopped:
|
|
387
|
+
# If the process has stopped and the stratus is wrong, it will never be right.
|
|
388
|
+
self.assertEqual(
|
|
389
|
+
current_status,
|
|
390
|
+
status,
|
|
391
|
+
f"Process returned {process.returncode} without status reaching {status}; stuck at {current_status}",
|
|
392
|
+
)
|
|
393
|
+
logger.debug(
|
|
394
|
+
"Workflow is %s; waiting for %s (%s/%s elapsed)",
|
|
395
|
+
current_status,
|
|
396
|
+
status,
|
|
397
|
+
time_elapsed,
|
|
398
|
+
seconds
|
|
399
|
+
)
|
|
308
400
|
time.sleep(0.5)
|
|
309
|
-
|
|
310
|
-
if
|
|
311
|
-
|
|
312
|
-
|
|
401
|
+
time_elapsed += 0.5
|
|
402
|
+
has_stopped = process.poll() is not None if process else False
|
|
403
|
+
current_status = status_fn(self.toilDir)
|
|
404
|
+
if time_elapsed > seconds:
|
|
405
|
+
self.assertEqual(
|
|
406
|
+
current_status,
|
|
407
|
+
status,
|
|
408
|
+
f"Waited {seconds} seconds without status reaching {status}; stuck at {current_status}",
|
|
409
|
+
)
|
|
313
410
|
|
|
314
411
|
def testGetPIDStatus(self):
|
|
315
412
|
"""Test that ToilStatus.getPIDStatus() behaves as expected."""
|
|
316
413
|
wf = subprocess.Popen(self.sort_workflow_cmd)
|
|
317
|
-
self.check_status(
|
|
414
|
+
self.check_status("RUNNING", status_fn=ToilStatus.getPIDStatus, process=wf, seconds=60)
|
|
318
415
|
wf.wait()
|
|
319
|
-
self.check_status(
|
|
416
|
+
self.check_status("COMPLETED", status_fn=ToilStatus.getPIDStatus, process=wf, seconds=60)
|
|
320
417
|
|
|
321
418
|
# TODO: we need to reach into the FileJobStore's files and delete this
|
|
322
419
|
# shared file. We assume we know its internal layout.
|
|
323
|
-
os.remove(os.path.join(self.toilDir,
|
|
324
|
-
self.check_status(
|
|
420
|
+
os.remove(os.path.join(self.toilDir, "files/shared/pid.log"))
|
|
421
|
+
self.check_status("QUEUED", status_fn=ToilStatus.getPIDStatus, process=wf, seconds=60)
|
|
325
422
|
|
|
326
423
|
def testGetStatusFailedToilWF(self):
|
|
327
424
|
"""
|
|
@@ -330,41 +427,71 @@ class UtilsTest(ToilTest):
|
|
|
330
427
|
opportunity to test the 'RUNNING' functionality of getStatus().
|
|
331
428
|
"""
|
|
332
429
|
# --badWorker is set to force failure.
|
|
333
|
-
wf = subprocess.Popen(self.sort_workflow_cmd + [
|
|
334
|
-
self.check_status(
|
|
430
|
+
wf = subprocess.Popen(self.sort_workflow_cmd + ["--badWorker=1"])
|
|
431
|
+
self.check_status("RUNNING", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
|
|
335
432
|
wf.wait()
|
|
336
|
-
self.check_status(
|
|
433
|
+
self.check_status("ERROR", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
|
|
337
434
|
|
|
338
435
|
@needs_cwl
|
|
339
436
|
@needs_docker
|
|
340
437
|
def testGetStatusFailedCWLWF(self):
|
|
341
438
|
"""Test that ToilStatus.getStatus() behaves as expected with a failing CWL workflow."""
|
|
342
439
|
# --badWorker is set to force failure.
|
|
343
|
-
cmd = [
|
|
344
|
-
|
|
440
|
+
cmd = [
|
|
441
|
+
"toil-cwl-runner",
|
|
442
|
+
"--logDebug",
|
|
443
|
+
"--jobStore",
|
|
444
|
+
self.toilDir,
|
|
445
|
+
"--clean=never",
|
|
446
|
+
"--badWorker=1",
|
|
447
|
+
"src/toil/test/cwl/sorttool.cwl",
|
|
448
|
+
"--reverse",
|
|
449
|
+
"--input",
|
|
450
|
+
"src/toil/test/cwl/whale.txt",
|
|
451
|
+
f"--outdir={self.tempDir}",
|
|
452
|
+
]
|
|
453
|
+
logger.info("Run command: %s", " ".join(cmd))
|
|
345
454
|
wf = subprocess.Popen(cmd)
|
|
346
|
-
self.check_status(
|
|
455
|
+
self.check_status("RUNNING", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
|
|
347
456
|
wf.wait()
|
|
348
|
-
self.check_status(
|
|
457
|
+
self.check_status("ERROR", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
|
|
349
458
|
|
|
350
459
|
@needs_cwl
|
|
351
460
|
@needs_docker
|
|
352
461
|
def testGetStatusSuccessfulCWLWF(self):
|
|
353
462
|
"""Test that ToilStatus.getStatus() behaves as expected with a successful CWL workflow."""
|
|
354
|
-
cmd = [
|
|
355
|
-
|
|
463
|
+
cmd = [
|
|
464
|
+
"toil-cwl-runner",
|
|
465
|
+
"--jobStore",
|
|
466
|
+
self.toilDir,
|
|
467
|
+
"--clean=never",
|
|
468
|
+
"src/toil/test/cwl/sorttool.cwl",
|
|
469
|
+
"--reverse",
|
|
470
|
+
"--input",
|
|
471
|
+
"src/toil/test/cwl/whale.txt",
|
|
472
|
+
f"--outdir={self.tempDir}",
|
|
473
|
+
]
|
|
356
474
|
wf = subprocess.Popen(cmd)
|
|
357
|
-
self.check_status(
|
|
475
|
+
self.check_status("RUNNING", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
|
|
358
476
|
wf.wait()
|
|
359
|
-
self.check_status(
|
|
477
|
+
self.check_status("COMPLETED", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
|
|
360
478
|
|
|
361
479
|
@needs_cwl
|
|
362
|
-
@patch(
|
|
480
|
+
@patch("builtins.print")
|
|
363
481
|
def testPrintJobLog(self, mock_print):
|
|
364
482
|
"""Test that ToilStatus.printJobLog() reads the log from a failed command without error."""
|
|
365
483
|
# Run a workflow that will always fail
|
|
366
|
-
cmd = [
|
|
367
|
-
|
|
484
|
+
cmd = [
|
|
485
|
+
"toil-cwl-runner",
|
|
486
|
+
"--logDebug",
|
|
487
|
+
"--jobStore",
|
|
488
|
+
self.toilDir,
|
|
489
|
+
"--clean=never",
|
|
490
|
+
"src/toil/test/cwl/alwaysfails.cwl",
|
|
491
|
+
"--message",
|
|
492
|
+
"Testing",
|
|
493
|
+
]
|
|
494
|
+
logger.info("Run command: %s", " ".join(cmd))
|
|
368
495
|
wf = subprocess.Popen(cmd)
|
|
369
496
|
wf.wait()
|
|
370
497
|
# print log and check output
|
|
@@ -373,26 +500,37 @@ class UtilsTest(ToilTest):
|
|
|
373
500
|
|
|
374
501
|
# Make sure it printed some kind of complaint about the missing command.
|
|
375
502
|
args, kwargs = mock_print.call_args
|
|
376
|
-
self.assertIn(
|
|
503
|
+
self.assertIn("invalidcommand", args[0])
|
|
377
504
|
|
|
505
|
+
@pytest.mark.timeout(1200)
|
|
378
506
|
def testRestartAttribute(self):
|
|
379
507
|
"""
|
|
380
|
-
Test that the job store is only destroyed when we observe a
|
|
508
|
+
Test that the job store is only destroyed when we observe a successful workflow run.
|
|
381
509
|
The following simulates a failing workflow that attempts to resume without restart().
|
|
382
510
|
In this case, the job store should not be destroyed until restart() is called.
|
|
383
511
|
"""
|
|
384
512
|
# Run a workflow that will always fail
|
|
385
|
-
cmd = self.restart_sort_workflow_cmd + [
|
|
513
|
+
cmd = self.restart_sort_workflow_cmd + ["--badWorker=1", "--logDebug"]
|
|
386
514
|
subprocess.run(cmd)
|
|
387
515
|
|
|
388
|
-
restart_cmd = self.restart_sort_workflow_cmd + [
|
|
516
|
+
restart_cmd = self.restart_sort_workflow_cmd + [
|
|
517
|
+
"--badWorker=0",
|
|
518
|
+
"--logDebug",
|
|
519
|
+
"--restart",
|
|
520
|
+
]
|
|
389
521
|
subprocess.run(restart_cmd)
|
|
390
522
|
|
|
391
523
|
# Check the job store exists after restart attempt
|
|
392
524
|
self.assertTrue(os.path.exists(self.toilDir))
|
|
393
525
|
|
|
394
|
-
successful_cmd = [
|
|
395
|
-
|
|
526
|
+
successful_cmd = [
|
|
527
|
+
python,
|
|
528
|
+
"-m",
|
|
529
|
+
"toil.test.sort.sort",
|
|
530
|
+
"--logDebug",
|
|
531
|
+
"file:" + self.toilDir,
|
|
532
|
+
"--restart",
|
|
533
|
+
]
|
|
396
534
|
subprocess.run(successful_cmd)
|
|
397
535
|
|
|
398
536
|
# Check the job store is destroyed after calling restart()
|
|
@@ -403,13 +541,14 @@ def printUnicodeCharacter():
|
|
|
403
541
|
# We want to get a unicode character to stdout but we can't print it directly because of
|
|
404
542
|
# Python encoding issues. To work around this we print in a separate Python process. See
|
|
405
543
|
# http://stackoverflow.com/questions/492483/setting-the-correct-encoding-when-piping-stdout-in-python
|
|
406
|
-
subprocess.check_call([sys.executable,
|
|
544
|
+
subprocess.check_call([sys.executable, "-c", "print('\\xc3\\xbc')"])
|
|
407
545
|
|
|
408
546
|
|
|
409
547
|
class RunTwoJobsPerWorker(Job):
|
|
410
548
|
"""
|
|
411
549
|
Runs child job with same resources as self in an attempt to chain the jobs on the same worker
|
|
412
550
|
"""
|
|
551
|
+
|
|
413
552
|
def __init__(self):
|
|
414
553
|
Job.__init__(self)
|
|
415
554
|
|