toil 5.12.0__py3-none-any.whl → 6.1.0a1__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 +18 -13
- toil/batchSystems/abstractBatchSystem.py +21 -10
- toil/batchSystems/abstractGridEngineBatchSystem.py +2 -2
- toil/batchSystems/awsBatch.py +14 -14
- toil/batchSystems/contained_executor.py +3 -3
- toil/batchSystems/htcondor.py +0 -1
- toil/batchSystems/kubernetes.py +34 -31
- toil/batchSystems/local_support.py +3 -1
- toil/batchSystems/mesos/batchSystem.py +7 -7
- toil/batchSystems/options.py +32 -83
- toil/batchSystems/registry.py +104 -23
- toil/batchSystems/singleMachine.py +16 -13
- toil/batchSystems/slurm.py +3 -3
- toil/batchSystems/torque.py +0 -1
- toil/bus.py +6 -8
- toil/common.py +532 -743
- toil/cwl/__init__.py +28 -32
- toil/cwl/cwltoil.py +523 -520
- toil/cwl/utils.py +55 -10
- toil/fileStores/__init__.py +2 -2
- toil/fileStores/abstractFileStore.py +36 -11
- toil/fileStores/cachingFileStore.py +607 -530
- toil/fileStores/nonCachingFileStore.py +43 -10
- toil/job.py +140 -75
- toil/jobStores/abstractJobStore.py +147 -79
- toil/jobStores/aws/jobStore.py +23 -9
- toil/jobStores/aws/utils.py +1 -2
- toil/jobStores/fileJobStore.py +117 -19
- toil/jobStores/googleJobStore.py +16 -7
- toil/jobStores/utils.py +5 -6
- toil/leader.py +71 -43
- toil/lib/accelerators.py +10 -5
- toil/lib/aws/__init__.py +3 -14
- toil/lib/aws/ami.py +22 -9
- toil/lib/aws/iam.py +21 -13
- toil/lib/aws/session.py +2 -16
- toil/lib/aws/utils.py +4 -5
- toil/lib/compatibility.py +1 -1
- toil/lib/conversions.py +7 -3
- toil/lib/docker.py +22 -23
- toil/lib/ec2.py +10 -6
- toil/lib/ec2nodes.py +106 -100
- toil/lib/encryption/_nacl.py +2 -1
- toil/lib/generatedEC2Lists.py +325 -18
- toil/lib/io.py +21 -0
- toil/lib/misc.py +1 -1
- toil/lib/resources.py +1 -1
- toil/lib/threading.py +74 -26
- toil/options/common.py +738 -0
- toil/options/cwl.py +336 -0
- toil/options/wdl.py +32 -0
- toil/provisioners/abstractProvisioner.py +1 -4
- toil/provisioners/aws/__init__.py +3 -6
- toil/provisioners/aws/awsProvisioner.py +6 -0
- toil/provisioners/clusterScaler.py +3 -2
- toil/provisioners/gceProvisioner.py +2 -2
- toil/realtimeLogger.py +2 -1
- toil/resource.py +24 -18
- toil/server/app.py +2 -3
- toil/server/cli/wes_cwl_runner.py +4 -4
- toil/server/utils.py +1 -1
- toil/server/wes/abstract_backend.py +3 -2
- toil/server/wes/amazon_wes_utils.py +5 -4
- toil/server/wes/tasks.py +2 -3
- toil/server/wes/toil_backend.py +2 -10
- toil/server/wsgi_app.py +2 -0
- toil/serviceManager.py +12 -10
- toil/statsAndLogging.py +5 -1
- toil/test/__init__.py +29 -54
- toil/test/batchSystems/batchSystemTest.py +11 -111
- toil/test/batchSystems/test_slurm.py +3 -2
- toil/test/cwl/cwlTest.py +213 -90
- toil/test/cwl/glob_dir.cwl +15 -0
- toil/test/cwl/preemptible.cwl +21 -0
- toil/test/cwl/preemptible_expression.cwl +28 -0
- toil/test/cwl/revsort.cwl +1 -1
- toil/test/cwl/revsort2.cwl +1 -1
- toil/test/docs/scriptsTest.py +0 -1
- toil/test/jobStores/jobStoreTest.py +27 -16
- toil/test/lib/aws/test_iam.py +4 -14
- toil/test/lib/aws/test_utils.py +0 -3
- toil/test/lib/dockerTest.py +4 -4
- toil/test/lib/test_ec2.py +11 -16
- toil/test/mesos/helloWorld.py +4 -5
- toil/test/mesos/stress.py +1 -1
- toil/test/provisioners/aws/awsProvisionerTest.py +9 -5
- toil/test/provisioners/clusterScalerTest.py +6 -4
- toil/test/provisioners/clusterTest.py +14 -3
- toil/test/provisioners/gceProvisionerTest.py +0 -6
- toil/test/provisioners/restartScript.py +3 -2
- toil/test/server/serverTest.py +1 -1
- toil/test/sort/restart_sort.py +2 -1
- toil/test/sort/sort.py +2 -1
- toil/test/sort/sortTest.py +2 -13
- toil/test/src/autoDeploymentTest.py +45 -45
- toil/test/src/busTest.py +5 -5
- toil/test/src/checkpointTest.py +2 -2
- toil/test/src/deferredFunctionTest.py +1 -1
- toil/test/src/fileStoreTest.py +32 -16
- toil/test/src/helloWorldTest.py +1 -1
- toil/test/src/importExportFileTest.py +1 -1
- toil/test/src/jobDescriptionTest.py +2 -1
- toil/test/src/jobServiceTest.py +1 -1
- toil/test/src/jobTest.py +18 -18
- toil/test/src/miscTests.py +5 -3
- toil/test/src/promisedRequirementTest.py +3 -3
- toil/test/src/realtimeLoggerTest.py +1 -1
- toil/test/src/resourceTest.py +2 -2
- toil/test/src/restartDAGTest.py +1 -1
- toil/test/src/resumabilityTest.py +36 -2
- toil/test/src/retainTempDirTest.py +1 -1
- toil/test/src/systemTest.py +2 -2
- toil/test/src/toilContextManagerTest.py +2 -2
- toil/test/src/userDefinedJobArgTypeTest.py +1 -1
- toil/test/utils/toilDebugTest.py +98 -32
- toil/test/utils/toilKillTest.py +2 -2
- toil/test/utils/utilsTest.py +20 -0
- toil/test/wdl/wdltoil_test.py +148 -45
- toil/toilState.py +7 -6
- toil/utils/toilClean.py +1 -1
- toil/utils/toilConfig.py +36 -0
- toil/utils/toilDebugFile.py +60 -33
- toil/utils/toilDebugJob.py +39 -12
- toil/utils/toilDestroyCluster.py +1 -1
- toil/utils/toilKill.py +1 -1
- toil/utils/toilLaunchCluster.py +13 -2
- toil/utils/toilMain.py +3 -2
- toil/utils/toilRsyncCluster.py +1 -1
- toil/utils/toilSshCluster.py +1 -1
- toil/utils/toilStats.py +240 -143
- toil/utils/toilStatus.py +1 -4
- toil/version.py +11 -11
- toil/wdl/utils.py +2 -122
- toil/wdl/wdltoil.py +999 -386
- toil/worker.py +25 -31
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/METADATA +60 -53
- toil-6.1.0a1.dist-info/RECORD +237 -0
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/WHEEL +1 -1
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/entry_points.txt +0 -1
- toil/batchSystems/parasol.py +0 -379
- toil/batchSystems/tes.py +0 -459
- toil/test/batchSystems/parasolTestSupport.py +0 -117
- toil/test/wdl/builtinTest.py +0 -506
- toil/test/wdl/conftest.py +0 -23
- toil/test/wdl/toilwdlTest.py +0 -522
- toil/wdl/toilwdl.py +0 -141
- toil/wdl/versions/dev.py +0 -107
- toil/wdl/versions/draft2.py +0 -980
- toil/wdl/versions/v1.py +0 -794
- toil/wdl/wdl_analysis.py +0 -116
- toil/wdl/wdl_functions.py +0 -997
- toil/wdl/wdl_synthesis.py +0 -1011
- toil/wdl/wdl_types.py +0 -243
- toil-5.12.0.dist-info/RECORD +0 -244
- /toil/{wdl/versions → options}/__init__.py +0 -0
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/LICENSE +0 -0
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/top_level.txt +0 -0
toil/test/utils/toilDebugTest.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"""A set of test cases for toilwdl.py"""
|
|
2
1
|
# Copyright (C) 2015-2021 Regents of the University of California
|
|
3
2
|
#
|
|
4
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -15,32 +14,33 @@
|
|
|
15
14
|
import logging
|
|
16
15
|
import os
|
|
17
16
|
import subprocess
|
|
18
|
-
|
|
17
|
+
import tempfile
|
|
18
|
+
|
|
19
|
+
import pytest
|
|
20
|
+
|
|
21
|
+
from toil.test import ToilTest
|
|
19
22
|
|
|
20
23
|
from toil.lib.resources import glob
|
|
21
24
|
from toil.test import slow
|
|
22
25
|
from toil.version import python
|
|
23
26
|
|
|
24
|
-
import pytest
|
|
25
|
-
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
jobStorePath = str(tmp_path / "toilWorkflowRun")
|
|
30
|
+
def workflow_debug_jobstore() -> str:
|
|
31
|
+
job_store_path = os.path.join(tempfile.mkdtemp(), "toilWorkflowRun")
|
|
32
32
|
subprocess.check_call(
|
|
33
33
|
[
|
|
34
34
|
python,
|
|
35
35
|
os.path.abspath("src/toil/test/utils/ABCWorkflowDebug/debugWorkflow.py"),
|
|
36
|
-
|
|
36
|
+
job_store_path,
|
|
37
37
|
]
|
|
38
38
|
)
|
|
39
|
-
return
|
|
39
|
+
return job_store_path
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
@slow
|
|
43
|
-
def testJobStoreContents(
|
|
43
|
+
def testJobStoreContents():
|
|
44
44
|
"""
|
|
45
45
|
Test toilDebugFile.printContentsOfJobStore().
|
|
46
46
|
|
|
@@ -48,14 +48,13 @@ def testJobStoreContents(workflow_debug_jobstore: str):
|
|
|
48
48
|
jobStore. 'A.txt', 'C.txt', 'ABC.txt' are then created. This checks to
|
|
49
49
|
make sure these contents are found in the jobStore and printed.
|
|
50
50
|
"""
|
|
51
|
-
jobStoreDir = workflow_debug_jobstore
|
|
52
51
|
contents = ["A.txt", "B.txt", "C.txt", "ABC.txt", "mkFile.py"]
|
|
53
52
|
|
|
54
53
|
subprocess.check_call(
|
|
55
54
|
[
|
|
56
55
|
python,
|
|
57
56
|
os.path.abspath("src/toil/utils/toilDebugFile.py"),
|
|
58
|
-
|
|
57
|
+
workflow_debug_jobstore(),
|
|
59
58
|
"--logDebug",
|
|
60
59
|
"--listFilesInJobStore=True",
|
|
61
60
|
]
|
|
@@ -78,7 +77,7 @@ def testJobStoreContents(workflow_debug_jobstore: str):
|
|
|
78
77
|
os.remove(jobstoreFileContents)
|
|
79
78
|
|
|
80
79
|
|
|
81
|
-
def fetchFiles(symLink, jobStoreDir: str, outputDir):
|
|
80
|
+
def fetchFiles(symLink: bool, jobStoreDir: str, outputDir: str):
|
|
82
81
|
"""
|
|
83
82
|
Fn for testFetchJobStoreFiles() and testFetchJobStoreFilesWSymlinks().
|
|
84
83
|
|
|
@@ -99,8 +98,8 @@ def fetchFiles(symLink, jobStoreDir: str, outputDir):
|
|
|
99
98
|
"*C.txt",
|
|
100
99
|
"*ABC.txt",
|
|
101
100
|
"*mkFile.py",
|
|
102
|
-
"--localFilePath="
|
|
103
|
-
"--useSymlinks="
|
|
101
|
+
f"--localFilePath={outputDir}",
|
|
102
|
+
f"--useSymlinks={symLink}",
|
|
104
103
|
]
|
|
105
104
|
print(cmd)
|
|
106
105
|
subprocess.check_call(cmd)
|
|
@@ -114,22 +113,89 @@ def fetchFiles(symLink, jobStoreDir: str, outputDir):
|
|
|
114
113
|
|
|
115
114
|
|
|
116
115
|
# expected run time = 4s
|
|
117
|
-
def testFetchJobStoreFiles(
|
|
118
|
-
"""Test toilDebugFile.fetchJobStoreFiles()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
def testFetchJobStoreFiles() -> None:
|
|
117
|
+
"""Test toilDebugFile.fetchJobStoreFiles() symlinks."""
|
|
118
|
+
job_store_dir = workflow_debug_jobstore()
|
|
119
|
+
output_dir = os.path.join(os.path.dirname(job_store_dir), "testoutput")
|
|
120
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
121
|
+
for symlink in (True, False):
|
|
122
|
+
fetchFiles(symLink=symlink, jobStoreDir=job_store_dir, outputDir=output_dir)
|
|
123
|
+
|
|
124
|
+
class DebugJobTest(ToilTest):
|
|
125
|
+
"""
|
|
126
|
+
Test the toil debug-job command.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def _get_job_store_and_job_id(self):
|
|
130
|
+
"""
|
|
131
|
+
Get a job store and the ID of a failing job within it.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
# First make a job store.
|
|
135
|
+
job_store = os.path.join(self._createTempDir(), "tree")
|
|
136
|
+
|
|
137
|
+
logger.info("Running workflow that always fails")
|
|
138
|
+
try:
|
|
139
|
+
# Run an always-failign workflow
|
|
140
|
+
subprocess.check_call([
|
|
141
|
+
python,
|
|
142
|
+
os.path.abspath("src/toil/test/docs/scripts/example_alwaysfail.py"),
|
|
143
|
+
"--retryCount=0",
|
|
144
|
+
"--logCritical",
|
|
145
|
+
"--disableProgress=True",
|
|
146
|
+
job_store
|
|
147
|
+
], stderr=subprocess.DEVNULL)
|
|
148
|
+
raise RuntimeError("Failing workflow succeeded!")
|
|
149
|
+
except subprocess.CalledProcessError:
|
|
150
|
+
# Should fail to run
|
|
151
|
+
logger.info("Task failed successfully")
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
# Get the job ID.
|
|
155
|
+
# TODO: This assumes a lot about the FileJobStore. Use the MessageBus instead?
|
|
156
|
+
job_id = "kind-explode/" + os.listdir(os.path.join(job_store, "jobs/kind-explode"))[0]
|
|
157
|
+
|
|
158
|
+
return job_store, job_id
|
|
159
|
+
|
|
160
|
+
def test_run_job(self):
|
|
161
|
+
"""
|
|
162
|
+
Make sure that we can use toil debug-job to try and run a job in-process.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
job_store, job_id = self._get_job_store_and_job_id()
|
|
166
|
+
|
|
167
|
+
logger.info("Trying to rerun job %s", job_id)
|
|
168
|
+
|
|
169
|
+
# Rerun the job, which should fail again
|
|
170
|
+
output = subprocess.check_output([
|
|
171
|
+
"toil",
|
|
172
|
+
"debug-job",
|
|
173
|
+
"--logDebug",
|
|
174
|
+
job_store,
|
|
175
|
+
job_id
|
|
176
|
+
], stderr=subprocess.STDOUT)
|
|
177
|
+
# Even if the job fails, the attempt to run it will succeed.
|
|
178
|
+
log = output.decode('utf-8')
|
|
179
|
+
assert "Boom!" in log, f"Did not find the expected exception message in: {log}"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def test_print_job_info(self):
|
|
183
|
+
"""
|
|
184
|
+
Make sure that we can use --printJobInfo to get information on a job from a job store.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
job_store, job_id = self._get_job_store_and_job_id()
|
|
188
|
+
|
|
189
|
+
logger.info("Trying to print job info for job %s", job_id)
|
|
190
|
+
|
|
191
|
+
# Print the job info and make sure that doesn't crash.
|
|
192
|
+
subprocess.check_call([
|
|
193
|
+
"toil",
|
|
194
|
+
"debug-job",
|
|
195
|
+
"--logDebug",
|
|
196
|
+
job_store,
|
|
197
|
+
"--printJobInfo",
|
|
198
|
+
job_id
|
|
199
|
+
])
|
|
124
200
|
|
|
125
201
|
|
|
126
|
-
# expected run time = 4s
|
|
127
|
-
def testFetchJobStoreFilesWSymlinks(
|
|
128
|
-
tmp_path: Path, workflow_debug_jobstore: str
|
|
129
|
-
) -> None:
|
|
130
|
-
"""Test toilDebugFile.fetchJobStoreFiles() using symlinks."""
|
|
131
|
-
outputDir = tmp_path / "testoutput"
|
|
132
|
-
outputDir.mkdir()
|
|
133
|
-
fetchFiles(
|
|
134
|
-
symLink=True, jobStoreDir=workflow_debug_jobstore, outputDir=str(outputDir)
|
|
135
|
-
)
|
toil/test/utils/toilKillTest.py
CHANGED
|
@@ -21,7 +21,8 @@ import time
|
|
|
21
21
|
import unittest
|
|
22
22
|
|
|
23
23
|
from toil.common import Toil
|
|
24
|
-
from toil.jobStores.abstractJobStore import NoSuchFileException,
|
|
24
|
+
from toil.jobStores.abstractJobStore import (NoSuchFileException,
|
|
25
|
+
NoSuchJobStoreException)
|
|
25
26
|
from toil.jobStores.utils import generate_locator
|
|
26
27
|
from toil.test import ToilTest, needs_aws_s3, needs_cwl
|
|
27
28
|
|
|
@@ -77,7 +78,6 @@ class ToilKillTest(ToilTest):
|
|
|
77
78
|
logger.info('Waiting for kill flag...')
|
|
78
79
|
except (NoSuchJobStoreException, NoSuchFileException):
|
|
79
80
|
logger.info('Waiting for job store to be openable...')
|
|
80
|
-
pass
|
|
81
81
|
time.sleep(2)
|
|
82
82
|
|
|
83
83
|
# run toil kill
|
toil/test/utils/utilsTest.py
CHANGED
|
@@ -115,6 +115,26 @@ class UtilsTest(ToilTest):
|
|
|
115
115
|
commandTokens.append('--failIfNotComplete')
|
|
116
116
|
return commandTokens
|
|
117
117
|
|
|
118
|
+
def test_config_functionality(self):
|
|
119
|
+
"""Ensure that creating and reading back the config file works"""
|
|
120
|
+
config_file = os.path.abspath("config.yaml")
|
|
121
|
+
config_command = [self.toilMain, 'config', config_file]
|
|
122
|
+
# make sure the command `toil config file_path` works
|
|
123
|
+
try:
|
|
124
|
+
subprocess.check_call(config_command)
|
|
125
|
+
except subprocess.CalledProcessError:
|
|
126
|
+
self.fail("The toil config utility failed!")
|
|
127
|
+
|
|
128
|
+
parser = Job.Runner.getDefaultArgumentParser()
|
|
129
|
+
# make sure that toil can read from the generated config file
|
|
130
|
+
try:
|
|
131
|
+
parser.parse_args(["random_jobstore", "--config", config_file])
|
|
132
|
+
except SystemExit:
|
|
133
|
+
self.fail("Failed to parse the default generated config file!")
|
|
134
|
+
finally:
|
|
135
|
+
os.remove(config_file)
|
|
136
|
+
|
|
137
|
+
|
|
118
138
|
@needs_rsync3
|
|
119
139
|
@pytest.mark.timeout(1200)
|
|
120
140
|
@needs_aws_ec2
|
toil/test/wdl/wdltoil_test.py
CHANGED
|
@@ -4,20 +4,36 @@ import shutil
|
|
|
4
4
|
import subprocess
|
|
5
5
|
import unittest
|
|
6
6
|
import uuid
|
|
7
|
-
import
|
|
8
|
-
from
|
|
7
|
+
from typing import Any, Dict, List, Optional, Set
|
|
8
|
+
from unittest.mock import patch
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
from unittest.mock import patch
|
|
11
|
+
from typing import Any, Dict, List, Set
|
|
11
12
|
|
|
12
|
-
from toil.test import ToilTest,
|
|
13
|
+
from toil.test import (ToilTest,
|
|
14
|
+
needs_docker_cuda,
|
|
15
|
+
needs_google_storage,
|
|
16
|
+
needs_singularity_or_docker,
|
|
17
|
+
slow)
|
|
13
18
|
from toil.version import exactPython
|
|
14
|
-
|
|
15
|
-
import toil.test.wdl.toilwdlTest
|
|
19
|
+
from toil.wdl.wdltoil import WDLSectionJob, WDLWorkflowGraph
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
class
|
|
22
|
+
class BaseWDLTest(ToilTest):
|
|
23
|
+
"""Base test class for WDL tests."""
|
|
24
|
+
def setUp(self) -> None:
|
|
25
|
+
"""Runs anew before each test to create farm fresh temp dirs."""
|
|
26
|
+
self.output_dir = os.path.join('/tmp/', 'toil-wdl-test-' + str(uuid.uuid4()))
|
|
27
|
+
os.makedirs(self.output_dir)
|
|
28
|
+
|
|
29
|
+
def tearDown(self) -> None:
|
|
30
|
+
if os.path.exists(self.output_dir):
|
|
31
|
+
shutil.rmtree(self.output_dir)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WDLConformanceTests(BaseWDLTest):
|
|
19
35
|
"""
|
|
20
|
-
|
|
36
|
+
WDL conformance tests for Toil.
|
|
21
37
|
"""
|
|
22
38
|
wdl_dir = "wdl-conformance-tests"
|
|
23
39
|
@classmethod
|
|
@@ -47,7 +63,7 @@ class ToilConformanceTests(toil.test.wdl.toilwdlTest.BaseToilWdlTest):
|
|
|
47
63
|
p = subprocess.run(self.base_command + ["-v", "1.0", "-n", tests_to_run], capture_output=True)
|
|
48
64
|
|
|
49
65
|
if p.returncode != 0:
|
|
50
|
-
print(p.stdout)
|
|
66
|
+
print(p.stdout.decode('utf-8', errors='replace'))
|
|
51
67
|
|
|
52
68
|
p.check_returncode()
|
|
53
69
|
|
|
@@ -58,7 +74,7 @@ class ToilConformanceTests(toil.test.wdl.toilwdlTest.BaseToilWdlTest):
|
|
|
58
74
|
p = subprocess.run(self.base_command + ["-v", "1.1", "-n", tests_to_run], capture_output=True)
|
|
59
75
|
|
|
60
76
|
if p.returncode != 0:
|
|
61
|
-
print(p.stdout)
|
|
77
|
+
print(p.stdout.decode('utf-8', errors='replace'))
|
|
62
78
|
|
|
63
79
|
p.check_returncode()
|
|
64
80
|
|
|
@@ -69,26 +85,23 @@ class ToilConformanceTests(toil.test.wdl.toilwdlTest.BaseToilWdlTest):
|
|
|
69
85
|
shutil.rmtree("wdl-conformance-tests")
|
|
70
86
|
|
|
71
87
|
|
|
72
|
-
class
|
|
73
|
-
"""
|
|
74
|
-
Version of the old Toil WDL tests that tests the new MiniWDL-based implementation.
|
|
75
|
-
"""
|
|
76
|
-
|
|
88
|
+
class WDLTests(BaseWDLTest):
|
|
89
|
+
"""Tests for Toil's MiniWDL-based implementation."""
|
|
77
90
|
@classmethod
|
|
78
91
|
def setUpClass(cls) -> None:
|
|
79
92
|
"""Runs once for all tests."""
|
|
80
93
|
cls.base_command = [exactPython, '-m', 'toil.wdl.wdltoil']
|
|
81
94
|
|
|
82
|
-
# We inherit a testMD5sum but it is going to need Singularity
|
|
83
|
-
#
|
|
95
|
+
# We inherit a testMD5sum but it is going to need Singularity or Docker
|
|
96
|
+
# now. And also needs to have a WDL 1.0+ WDL file. So we replace it.
|
|
84
97
|
@needs_singularity_or_docker
|
|
85
|
-
def
|
|
98
|
+
def test_MD5sum(self):
|
|
86
99
|
"""Test if Toil produces the same outputs as known good outputs for WDL's
|
|
87
100
|
GATK tutorial #1."""
|
|
88
101
|
wdl = os.path.abspath('src/toil/test/wdl/md5sum/md5sum.1.0.wdl')
|
|
89
102
|
json_file = os.path.abspath('src/toil/test/wdl/md5sum/md5sum.json')
|
|
90
103
|
|
|
91
|
-
result_json = subprocess.check_output(self.base_command + [wdl, json_file, '-o', self.output_dir, '--logDebug'])
|
|
104
|
+
result_json = subprocess.check_output(self.base_command + [wdl, json_file, '-o', self.output_dir, '--logDebug', '--retryCount=0'])
|
|
92
105
|
result = json.loads(result_json)
|
|
93
106
|
|
|
94
107
|
assert 'ga4ghMd5.value' in result
|
|
@@ -96,25 +109,13 @@ class WdlToilTest(toil.test.wdl.toilwdlTest.ToilWdlTest):
|
|
|
96
109
|
assert os.path.exists(result['ga4ghMd5.value'])
|
|
97
110
|
assert os.path.basename(result['ga4ghMd5.value']) == 'md5sum.txt'
|
|
98
111
|
|
|
99
|
-
def test_empty_file_path(self):
|
|
100
|
-
"""Test if empty File type inputs are protected against"""
|
|
101
|
-
wdl = os.path.abspath('src/toil/test/wdl/md5sum/md5sum.1.0.wdl')
|
|
102
|
-
json_file = os.path.abspath('src/toil/test/wdl/md5sum/empty_file.json')
|
|
103
|
-
|
|
104
|
-
p = subprocess.Popen(self.base_command + [wdl, json_file, '-o', self.output_dir, '--logDebug'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
105
|
-
stdout, stderr = p.communicate()
|
|
106
|
-
retval = p.wait()
|
|
107
|
-
|
|
108
|
-
assert retval != 0
|
|
109
|
-
assert b'Could not find' in stderr
|
|
110
|
-
|
|
111
112
|
@needs_singularity_or_docker
|
|
112
|
-
def test_miniwdl_self_test(self):
|
|
113
|
+
def test_miniwdl_self_test(self, extra_args: Optional[List[str]] = None) -> None:
|
|
113
114
|
"""Test if the MiniWDL self test runs and produces the expected output."""
|
|
114
115
|
wdl_file = os.path.abspath('src/toil/test/wdl/miniwdl_self_test/self_test.wdl')
|
|
115
116
|
json_file = os.path.abspath('src/toil/test/wdl/miniwdl_self_test/inputs.json')
|
|
116
117
|
|
|
117
|
-
result_json = subprocess.check_output(self.base_command + [wdl_file, json_file, '-o', self.output_dir, '--outputDialect', 'miniwdl'])
|
|
118
|
+
result_json = subprocess.check_output(self.base_command + [wdl_file, json_file, '--logDebug', '-o', self.output_dir, '--outputDialect', 'miniwdl'] + (extra_args or []))
|
|
118
119
|
result = json.loads(result_json)
|
|
119
120
|
|
|
120
121
|
# Expect MiniWDL-style output with a designated "dir"
|
|
@@ -138,10 +139,17 @@ class WdlToilTest(toil.test.wdl.toilwdlTest.ToilWdlTest):
|
|
|
138
139
|
assert 'hello_caller.messages' in outputs
|
|
139
140
|
assert outputs['hello_caller.messages'] == ["Hello, Alyssa P. Hacker!", "Hello, Ben Bitdiddle!"]
|
|
140
141
|
|
|
142
|
+
@needs_singularity_or_docker
|
|
143
|
+
def test_miniwdl_self_test_by_reference(self) -> None:
|
|
144
|
+
"""
|
|
145
|
+
Test if the MiniWDL self test works when passing input files by URL reference.
|
|
146
|
+
"""
|
|
147
|
+
self.test_miniwdl_self_test(extra_args=["--referenceInputs=True"])
|
|
148
|
+
|
|
141
149
|
@slow
|
|
142
150
|
@needs_docker_cuda
|
|
143
151
|
def test_giraffe_deepvariant(self):
|
|
144
|
-
"""Test if Giraffe and
|
|
152
|
+
"""Test if Giraffe and GPU DeepVariant run. This could take 25 minutes."""
|
|
145
153
|
# TODO: enable test if nvidia-container-runtime and Singularity are installed but Docker isn't.
|
|
146
154
|
|
|
147
155
|
json_dir = self._createTempDir()
|
|
@@ -184,7 +192,7 @@ class WdlToilTest(toil.test.wdl.toilwdlTest.ToilWdlTest):
|
|
|
184
192
|
@slow
|
|
185
193
|
@needs_singularity_or_docker
|
|
186
194
|
def test_giraffe(self):
|
|
187
|
-
"""Test if Giraffe runs. This could take 12 minutes. Also we scale it down."""
|
|
195
|
+
"""Test if Giraffe runs. This could take 12 minutes. Also we scale it down but it still demands lots of memory."""
|
|
188
196
|
# TODO: enable test if nvidia-container-runtime and Singularity are installed but Docker isn't.
|
|
189
197
|
|
|
190
198
|
json_dir = self._createTempDir()
|
|
@@ -224,17 +232,112 @@ class WdlToilTest(toil.test.wdl.toilwdlTest.ToilWdlTest):
|
|
|
224
232
|
assert os.path.exists(result['ga4ghMd5.value'])
|
|
225
233
|
assert os.path.basename(result['ga4ghMd5.value']) == 'md5sum.txt'
|
|
226
234
|
|
|
227
|
-
def
|
|
228
|
-
"""
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
+
def test_coalesce(self):
|
|
236
|
+
"""
|
|
237
|
+
Test if WDLSectionJob can coalesce WDL decls.
|
|
238
|
+
|
|
239
|
+
White box test; will need to be changed or removed if the WDL interpreter changes.
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
# Set up data structures for our fake workflow graph to pull from.
|
|
243
|
+
# This has all decl-type nodes
|
|
244
|
+
all_decls: Set[str] = set()
|
|
245
|
+
# And this has all transitive dependencies for all nodes.
|
|
246
|
+
all_deps: Dict[str, Set[str]] = {}
|
|
247
|
+
|
|
248
|
+
def mock_is_decl(self: Any, node_id: str) -> bool:
|
|
249
|
+
"""
|
|
250
|
+
Replacement function to determine if a node is a decl or not.
|
|
251
|
+
"""
|
|
252
|
+
return node_id in all_decls
|
|
253
|
+
|
|
254
|
+
def mock_get_transitive_dependencies(self: Any, node_id: str) -> Set[str]:
|
|
255
|
+
"""
|
|
256
|
+
Replacement function to get all the transitive dependencies of a node.
|
|
257
|
+
"""
|
|
258
|
+
return all_deps[node_id]
|
|
259
|
+
|
|
260
|
+
# These are the only two methods coalesce_nodes calls, so we can
|
|
261
|
+
# replace them to ensure our graph is used without actually needing to
|
|
262
|
+
# make any WDL objects for it.
|
|
263
|
+
#
|
|
264
|
+
# If that changes, the test will need to change! Maybe then it will be
|
|
265
|
+
# worth extracting a base type for this interface.
|
|
266
|
+
with patch.object(WDLWorkflowGraph, 'is_decl', mock_is_decl):
|
|
267
|
+
with patch.object(WDLWorkflowGraph, 'get_transitive_dependencies', mock_get_transitive_dependencies):
|
|
268
|
+
|
|
269
|
+
with self.subTest(msg="Two unrelated decls can coalesce"):
|
|
270
|
+
# Set up two unrelated decls
|
|
271
|
+
all_decls = {"decl1", "decl2"}
|
|
272
|
+
all_deps = {
|
|
273
|
+
"decl1": set(),
|
|
274
|
+
"decl2": set()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
result = WDLSectionJob.coalesce_nodes(["decl1", "decl2"], WDLWorkflowGraph([]))
|
|
278
|
+
|
|
279
|
+
# Make sure they coalesced
|
|
280
|
+
assert len(result) == 1
|
|
281
|
+
assert "decl1" in result[0]
|
|
282
|
+
assert "decl2" in result[0]
|
|
283
|
+
|
|
284
|
+
with self.subTest(msg="A decl will not coalesce with a non-decl"):
|
|
285
|
+
all_decls = {"decl"}
|
|
286
|
+
all_deps = {
|
|
287
|
+
"decl": set(),
|
|
288
|
+
"nondecl": set()
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
result = WDLSectionJob.coalesce_nodes(["decl", "nondecl"], WDLWorkflowGraph([]))
|
|
292
|
+
|
|
293
|
+
assert len(result) == 2
|
|
294
|
+
assert len(result[0]) == 1
|
|
295
|
+
assert len(result[1]) == 1
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
with self.subTest(msg="Two adjacent decls with a common dependency can coalesce"):
|
|
299
|
+
all_decls = {"decl1", "decl2"}
|
|
300
|
+
all_deps = {
|
|
301
|
+
"decl1": {"base"},
|
|
302
|
+
"decl2": {"base"},
|
|
303
|
+
"base": set()
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
result = WDLSectionJob.coalesce_nodes(["base", "decl1", "decl2"], WDLWorkflowGraph([]))
|
|
307
|
+
|
|
308
|
+
assert len(result) == 2
|
|
309
|
+
assert "base" in result[0]
|
|
310
|
+
assert "decl1" in result[1]
|
|
311
|
+
assert "decl2" in result[1]
|
|
312
|
+
|
|
313
|
+
with self.subTest(msg="Two adjacent decls with different dependencies will not coalesce"):
|
|
314
|
+
all_decls = {"decl1", "decl2"}
|
|
315
|
+
all_deps = {
|
|
316
|
+
"decl1": {"base"},
|
|
317
|
+
"decl2": set(),
|
|
318
|
+
"base": set()
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
result = WDLSectionJob.coalesce_nodes(["base", "decl1", "decl2"], WDLWorkflowGraph([]))
|
|
322
|
+
|
|
323
|
+
assert len(result) == 3
|
|
324
|
+
assert "base" in result[0]
|
|
325
|
+
|
|
326
|
+
with self.subTest(msg="Two adjacent decls with different successors will coalesce"):
|
|
327
|
+
all_decls = {"decl1", "decl2"}
|
|
328
|
+
all_deps = {
|
|
329
|
+
"decl1": set(),
|
|
330
|
+
"decl2": set(),
|
|
331
|
+
"successor": {"decl2"}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
result = WDLSectionJob.coalesce_nodes(["decl1", "decl2", "successor"], WDLWorkflowGraph([]))
|
|
335
|
+
|
|
336
|
+
assert len(result) == 2
|
|
337
|
+
assert "decl1" in result[0]
|
|
338
|
+
assert "decl2" in result[0]
|
|
339
|
+
assert "successor" in result[1]
|
|
235
340
|
|
|
236
|
-
assert retval != 0
|
|
237
|
-
assert b'Could not find' in stderr
|
|
238
341
|
|
|
239
342
|
if __name__ == "__main__":
|
|
240
343
|
unittest.main() # run all tests
|
toil/toilState.py
CHANGED
|
@@ -183,6 +183,7 @@ class ToilState:
|
|
|
183
183
|
if job_id in self.__job_database:
|
|
184
184
|
# Update the one true copy in place
|
|
185
185
|
old_truth = self.__job_database[job_id]
|
|
186
|
+
old_truth.check_new_version(new_truth)
|
|
186
187
|
old_truth.__dict__.update(new_truth.__dict__)
|
|
187
188
|
else:
|
|
188
189
|
# Just keep the new one
|
|
@@ -293,7 +294,8 @@ class ToilState:
|
|
|
293
294
|
successor.predecessorsFinished.add(jobDesc.jobStoreID)
|
|
294
295
|
|
|
295
296
|
# If the successor has no predecessors to finish
|
|
296
|
-
|
|
297
|
+
if len(successor.predecessorsFinished) > successor.predecessorNumber:
|
|
298
|
+
raise RuntimeError("There are more finished predecessors than possible.")
|
|
297
299
|
if len(successor.predecessorsFinished) == successor.predecessorNumber:
|
|
298
300
|
|
|
299
301
|
# It is ready to be run, so remove it from the set of waiting jobs
|
|
@@ -322,7 +324,8 @@ class ToilState:
|
|
|
322
324
|
|
|
323
325
|
# We put the successor job in the set of waiting successor
|
|
324
326
|
# jobs with multiple predecessors
|
|
325
|
-
|
|
327
|
+
if successorJobStoreID in self.jobsToBeScheduledWithMultiplePredecessors:
|
|
328
|
+
raise RuntimeError("Failed to schedule the successor job. The successor job is already scheduled.")
|
|
326
329
|
self.jobsToBeScheduledWithMultiplePredecessors.add(successorJobStoreID)
|
|
327
330
|
|
|
328
331
|
# Process successor
|
|
@@ -337,10 +340,8 @@ class ToilState:
|
|
|
337
340
|
# We've already seen the successor
|
|
338
341
|
|
|
339
342
|
# Add the job as a predecessor
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
not in self.successor_to_predecessors[successorJobStoreID]
|
|
343
|
-
)
|
|
343
|
+
if jobDesc.jobStoreID in self.successor_to_predecessors[successorJobStoreID]:
|
|
344
|
+
raise RuntimeError("Failed to add the job as a predecessor. The job is already added as a predecessor.")
|
|
344
345
|
self.successor_to_predecessors[successorJobStoreID].add(
|
|
345
346
|
str(jobDesc.jobStoreID)
|
|
346
347
|
)
|
toil/utils/toilClean.py
CHANGED
|
@@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def main() -> None:
|
|
25
|
-
parser = parser_with_common_options(jobstore_option=True)
|
|
25
|
+
parser = parser_with_common_options(jobstore_option=True, prog="toil clean")
|
|
26
26
|
|
|
27
27
|
options = parser.parse_args()
|
|
28
28
|
set_logging_from_options(options)
|
toil/utils/toilConfig.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright (C) 2015-2021 Regents of the University of California
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""Create a config file with all default Toil options."""
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from configargparse import ArgParser
|
|
19
|
+
|
|
20
|
+
from toil.common import generate_config
|
|
21
|
+
from toil.statsAndLogging import add_logging_options, set_logging_from_options
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main() -> None:
|
|
27
|
+
parser = ArgParser()
|
|
28
|
+
|
|
29
|
+
parser.add_argument("output", default="config.yaml", help="Filepath to write the config file too. Default=%("
|
|
30
|
+
"default)s")
|
|
31
|
+
add_logging_options(parser)
|
|
32
|
+
options = parser.parse_args()
|
|
33
|
+
set_logging_from_options(options)
|
|
34
|
+
logger.debug("Attempting to write a default config file to %s.", os.path.abspath(options.output))
|
|
35
|
+
generate_config(os.path.abspath(options.output))
|
|
36
|
+
logger.info("Successfully wrote a default config file to %s.", os.path.abspath(options.output))
|