toil 8.1.0b1__py3-none-any.whl → 8.2.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 +0 -35
- toil/batchSystems/abstractBatchSystem.py +1 -1
- toil/batchSystems/abstractGridEngineBatchSystem.py +1 -1
- toil/batchSystems/awsBatch.py +1 -1
- toil/batchSystems/cleanup_support.py +1 -1
- toil/batchSystems/kubernetes.py +53 -7
- toil/batchSystems/local_support.py +1 -1
- toil/batchSystems/mesos/batchSystem.py +13 -8
- toil/batchSystems/mesos/test/__init__.py +3 -2
- toil/batchSystems/singleMachine.py +1 -1
- toil/batchSystems/slurm.py +27 -26
- toil/bus.py +5 -3
- toil/common.py +39 -11
- toil/cwl/cwltoil.py +1 -1
- toil/job.py +64 -49
- toil/jobStores/abstractJobStore.py +24 -3
- toil/jobStores/fileJobStore.py +25 -1
- toil/jobStores/googleJobStore.py +104 -30
- toil/leader.py +9 -0
- toil/lib/accelerators.py +3 -1
- toil/lib/aws/utils.py.orig +504 -0
- toil/lib/bioio.py +1 -1
- toil/lib/docker.py +252 -91
- toil/lib/dockstore.py +11 -3
- toil/lib/exceptions.py +5 -3
- toil/lib/history.py +87 -13
- toil/lib/history_submission.py +23 -9
- toil/lib/io.py +34 -22
- toil/lib/misc.py +7 -1
- toil/lib/resources.py +2 -1
- toil/lib/threading.py +11 -10
- toil/options/common.py +8 -0
- toil/options/wdl.py +11 -0
- toil/server/api_spec/LICENSE +201 -0
- toil/server/api_spec/README.rst +5 -0
- toil/server/cli/wes_cwl_runner.py +2 -1
- toil/test/__init__.py +275 -115
- toil/test/batchSystems/batchSystemTest.py +227 -205
- toil/test/batchSystems/test_slurm.py +27 -0
- toil/test/cactus/pestis.tar.gz +0 -0
- toil/test/conftest.py +7 -0
- toil/test/cwl/2.fasta +11 -0
- toil/test/cwl/2.fastq +12 -0
- toil/test/cwl/conftest.py +1 -1
- toil/test/cwl/cwlTest.py +999 -867
- toil/test/cwl/directory/directory/file.txt +15 -0
- toil/test/cwl/download_directory_file.json +4 -0
- toil/test/cwl/download_directory_s3.json +4 -0
- toil/test/cwl/download_file.json +6 -0
- toil/test/cwl/download_http.json +6 -0
- toil/test/cwl/download_https.json +6 -0
- toil/test/cwl/download_s3.json +6 -0
- toil/test/cwl/download_subdirectory_file.json +5 -0
- toil/test/cwl/download_subdirectory_s3.json +5 -0
- toil/test/cwl/empty.json +1 -0
- toil/test/cwl/mock_mpi/fake_mpi.yml +8 -0
- toil/test/cwl/mock_mpi/fake_mpi_run.py +42 -0
- toil/test/cwl/optional-file-exists.json +6 -0
- toil/test/cwl/optional-file-missing.json +6 -0
- toil/test/cwl/preemptible_expression.json +1 -0
- toil/test/cwl/revsort-job-missing.json +6 -0
- toil/test/cwl/revsort-job.json +6 -0
- toil/test/cwl/s3_secondary_file.json +16 -0
- toil/test/cwl/seqtk_seq_job.json +6 -0
- toil/test/cwl/stream.json +6 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.dat +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f1 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f1i +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f2 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f2_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f3 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f3_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f4 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f4_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f5 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.info +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.lock +0 -0
- toil/test/cwl/whale.txt +16 -0
- toil/test/docs/scripts/example_alwaysfail.py +38 -0
- toil/test/docs/scripts/example_alwaysfail_with_files.wdl +33 -0
- toil/test/docs/scripts/example_cachingbenchmark.py +117 -0
- toil/test/docs/scripts/stagingExampleFiles/in.txt +1 -0
- toil/test/docs/scripts/stagingExampleFiles/out.txt +2 -0
- toil/test/docs/scripts/tutorial_arguments.py +23 -0
- toil/test/docs/scripts/tutorial_debugging.patch +12 -0
- toil/test/docs/scripts/tutorial_debugging_hangs.wdl +126 -0
- toil/test/docs/scripts/tutorial_debugging_works.wdl +129 -0
- toil/test/docs/scripts/tutorial_docker.py +20 -0
- toil/test/docs/scripts/tutorial_dynamic.py +24 -0
- toil/test/docs/scripts/tutorial_encapsulation.py +28 -0
- toil/test/docs/scripts/tutorial_encapsulation2.py +29 -0
- toil/test/docs/scripts/tutorial_helloworld.py +15 -0
- toil/test/docs/scripts/tutorial_invokeworkflow.py +27 -0
- toil/test/docs/scripts/tutorial_invokeworkflow2.py +30 -0
- toil/test/docs/scripts/tutorial_jobfunctions.py +22 -0
- toil/test/docs/scripts/tutorial_managing.py +29 -0
- toil/test/docs/scripts/tutorial_managing2.py +56 -0
- toil/test/docs/scripts/tutorial_multiplejobs.py +25 -0
- toil/test/docs/scripts/tutorial_multiplejobs2.py +21 -0
- toil/test/docs/scripts/tutorial_multiplejobs3.py +22 -0
- toil/test/docs/scripts/tutorial_promises.py +25 -0
- toil/test/docs/scripts/tutorial_promises2.py +30 -0
- toil/test/docs/scripts/tutorial_quickstart.py +22 -0
- toil/test/docs/scripts/tutorial_requirements.py +44 -0
- toil/test/docs/scripts/tutorial_services.py +45 -0
- toil/test/docs/scripts/tutorial_staging.py +45 -0
- toil/test/docs/scripts/tutorial_stats.py +64 -0
- toil/test/lib/aws/test_iam.py +3 -1
- toil/test/lib/dockerTest.py +205 -122
- toil/test/lib/test_history.py +101 -77
- toil/test/provisioners/aws/awsProvisionerTest.py +12 -9
- toil/test/provisioners/clusterTest.py +4 -4
- toil/test/provisioners/gceProvisionerTest.py +16 -14
- toil/test/sort/sort.py +4 -1
- toil/test/src/busTest.py +17 -17
- toil/test/src/deferredFunctionTest.py +145 -132
- toil/test/src/importExportFileTest.py +71 -63
- toil/test/src/jobEncapsulationTest.py +27 -28
- toil/test/src/jobServiceTest.py +149 -133
- toil/test/src/jobTest.py +219 -211
- toil/test/src/miscTests.py +66 -60
- toil/test/src/promisedRequirementTest.py +163 -169
- toil/test/src/regularLogTest.py +24 -24
- toil/test/src/resourceTest.py +82 -76
- toil/test/src/restartDAGTest.py +51 -47
- toil/test/src/resumabilityTest.py +24 -19
- toil/test/src/retainTempDirTest.py +60 -57
- toil/test/src/systemTest.py +17 -13
- toil/test/src/threadingTest.py +29 -32
- toil/test/utils/ABCWorkflowDebug/B_file.txt +1 -0
- toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +204 -0
- toil/test/utils/ABCWorkflowDebug/mkFile.py +16 -0
- toil/test/utils/ABCWorkflowDebug/sleep.cwl +12 -0
- toil/test/utils/ABCWorkflowDebug/sleep.yaml +1 -0
- toil/test/utils/toilDebugTest.py +117 -102
- toil/test/utils/toilKillTest.py +54 -53
- toil/test/utils/utilsTest.py +303 -229
- toil/test/wdl/lint_error.wdl +9 -0
- toil/test/wdl/md5sum/empty_file.json +1 -0
- toil/test/wdl/md5sum/md5sum-gs.json +1 -0
- toil/test/wdl/md5sum/md5sum.1.0.wdl +32 -0
- toil/test/wdl/md5sum/md5sum.input +1 -0
- toil/test/wdl/md5sum/md5sum.json +1 -0
- toil/test/wdl/md5sum/md5sum.wdl +25 -0
- toil/test/wdl/miniwdl_self_test/inputs-namespaced.json +1 -0
- toil/test/wdl/miniwdl_self_test/inputs.json +1 -0
- toil/test/wdl/miniwdl_self_test/self_test.wdl +40 -0
- toil/test/wdl/standard_library/as_map.json +16 -0
- toil/test/wdl/standard_library/as_map_as_input.wdl +23 -0
- toil/test/wdl/standard_library/as_pairs.json +7 -0
- toil/test/wdl/standard_library/as_pairs_as_input.wdl +23 -0
- toil/test/wdl/standard_library/ceil.json +3 -0
- toil/test/wdl/standard_library/ceil_as_command.wdl +16 -0
- toil/test/wdl/standard_library/ceil_as_input.wdl +16 -0
- toil/test/wdl/standard_library/collect_by_key.json +1 -0
- toil/test/wdl/standard_library/collect_by_key_as_input.wdl +23 -0
- toil/test/wdl/standard_library/cross.json +11 -0
- toil/test/wdl/standard_library/cross_as_input.wdl +19 -0
- toil/test/wdl/standard_library/flatten.json +7 -0
- toil/test/wdl/standard_library/flatten_as_input.wdl +18 -0
- toil/test/wdl/standard_library/floor.json +3 -0
- toil/test/wdl/standard_library/floor_as_command.wdl +16 -0
- toil/test/wdl/standard_library/floor_as_input.wdl +16 -0
- toil/test/wdl/standard_library/keys.json +8 -0
- toil/test/wdl/standard_library/keys_as_input.wdl +24 -0
- toil/test/wdl/standard_library/length.json +7 -0
- toil/test/wdl/standard_library/length_as_input.wdl +16 -0
- toil/test/wdl/standard_library/length_as_input_with_map.json +7 -0
- toil/test/wdl/standard_library/length_as_input_with_map.wdl +17 -0
- toil/test/wdl/standard_library/length_invalid.json +3 -0
- toil/test/wdl/standard_library/range.json +3 -0
- toil/test/wdl/standard_library/range_0.json +3 -0
- toil/test/wdl/standard_library/range_as_input.wdl +17 -0
- toil/test/wdl/standard_library/range_invalid.json +3 -0
- toil/test/wdl/standard_library/read_boolean.json +3 -0
- toil/test/wdl/standard_library/read_boolean_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_float.json +3 -0
- toil/test/wdl/standard_library/read_float_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_int.json +3 -0
- toil/test/wdl/standard_library/read_int_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_json.json +3 -0
- toil/test/wdl/standard_library/read_json_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_lines.json +3 -0
- toil/test/wdl/standard_library/read_lines_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_map.json +3 -0
- toil/test/wdl/standard_library/read_map_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_string.json +3 -0
- toil/test/wdl/standard_library/read_string_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_tsv.json +3 -0
- toil/test/wdl/standard_library/read_tsv_as_output.wdl +31 -0
- toil/test/wdl/standard_library/round.json +3 -0
- toil/test/wdl/standard_library/round_as_command.wdl +16 -0
- toil/test/wdl/standard_library/round_as_input.wdl +16 -0
- toil/test/wdl/standard_library/size.json +3 -0
- toil/test/wdl/standard_library/size_as_command.wdl +17 -0
- toil/test/wdl/standard_library/size_as_output.wdl +36 -0
- toil/test/wdl/standard_library/stderr.json +3 -0
- toil/test/wdl/standard_library/stderr_as_output.wdl +30 -0
- toil/test/wdl/standard_library/stdout.json +3 -0
- toil/test/wdl/standard_library/stdout_as_output.wdl +30 -0
- toil/test/wdl/standard_library/sub.json +3 -0
- toil/test/wdl/standard_library/sub_as_input.wdl +17 -0
- toil/test/wdl/standard_library/sub_as_input_with_file.wdl +17 -0
- toil/test/wdl/standard_library/transpose.json +6 -0
- toil/test/wdl/standard_library/transpose_as_input.wdl +18 -0
- toil/test/wdl/standard_library/write_json.json +6 -0
- toil/test/wdl/standard_library/write_json_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_lines.json +7 -0
- toil/test/wdl/standard_library/write_lines_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_map.json +6 -0
- toil/test/wdl/standard_library/write_map_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_tsv.json +6 -0
- toil/test/wdl/standard_library/write_tsv_as_command.wdl +17 -0
- toil/test/wdl/standard_library/zip.json +12 -0
- toil/test/wdl/standard_library/zip_as_input.wdl +19 -0
- toil/test/wdl/test.csv +3 -0
- toil/test/wdl/test.tsv +3 -0
- toil/test/wdl/testfiles/croo.wdl +38 -0
- toil/test/wdl/testfiles/drop_files.wdl +62 -0
- toil/test/wdl/testfiles/drop_files_subworkflow.wdl +13 -0
- toil/test/wdl/testfiles/empty.txt +0 -0
- toil/test/wdl/testfiles/not_enough_outputs.wdl +33 -0
- toil/test/wdl/testfiles/random.wdl +66 -0
- toil/test/wdl/testfiles/string_file_coercion.json +1 -0
- toil/test/wdl/testfiles/string_file_coercion.wdl +35 -0
- toil/test/wdl/testfiles/test.json +4 -0
- toil/test/wdl/testfiles/test_boolean.txt +1 -0
- toil/test/wdl/testfiles/test_float.txt +1 -0
- toil/test/wdl/testfiles/test_int.txt +1 -0
- toil/test/wdl/testfiles/test_lines.txt +5 -0
- toil/test/wdl/testfiles/test_map.txt +2 -0
- toil/test/wdl/testfiles/test_string.txt +1 -0
- toil/test/wdl/testfiles/url_to_file.wdl +13 -0
- toil/test/wdl/testfiles/url_to_optional_file.wdl +13 -0
- toil/test/wdl/testfiles/vocab.json +1 -0
- toil/test/wdl/testfiles/vocab.wdl +66 -0
- toil/test/wdl/testfiles/wait.wdl +34 -0
- toil/test/wdl/wdl_specification/type_pair.json +23 -0
- toil/test/wdl/wdl_specification/type_pair_basic.wdl +36 -0
- toil/test/wdl/wdl_specification/type_pair_with_files.wdl +36 -0
- toil/test/wdl/wdl_specification/v1_spec.json +1 -0
- toil/test/wdl/wdl_specification/v1_spec_declaration.wdl +39 -0
- toil/test/wdl/wdltoil_test.py +680 -407
- toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
- toil/version.py +9 -9
- toil/wdl/wdltoil.py +336 -123
- {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/METADATA +5 -4
- toil-8.2.0.dist-info/RECORD +439 -0
- {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/WHEEL +1 -1
- toil-8.1.0b1.dist-info/RECORD +0 -259
- {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/entry_points.txt +0 -0
- {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info/licenses}/LICENSE +0 -0
- {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -12,29 +12,32 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
import
|
|
15
|
+
from pathlib import Path
|
|
16
16
|
|
|
17
17
|
from toil.exceptions import FailedJobsException
|
|
18
|
-
from toil.job import Job
|
|
18
|
+
from toil.job import Job, JobFunctionWrappingJob
|
|
19
19
|
from toil.jobStores.abstractJobStore import NoSuchFileException
|
|
20
|
-
from toil.test import
|
|
20
|
+
from toil.test import pslow as slow
|
|
21
21
|
|
|
22
|
+
import pytest
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
|
|
25
|
+
class TestResumability:
|
|
24
26
|
"""
|
|
25
27
|
https://github.com/BD2KGenomics/toil/issues/808
|
|
26
28
|
"""
|
|
27
29
|
|
|
28
30
|
@slow
|
|
29
|
-
|
|
31
|
+
@pytest.mark.slow
|
|
32
|
+
def test(self, tmp_path: Path) -> None:
|
|
30
33
|
"""
|
|
31
34
|
Tests that a toil workflow that fails once can be resumed without a NoSuchJobException.
|
|
32
35
|
"""
|
|
33
|
-
options = Job.Runner.getDefaultOptions(
|
|
36
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
34
37
|
options.logLevel = "INFO"
|
|
35
38
|
options.retryCount = 0
|
|
36
39
|
root = Job.wrapJobFn(parent)
|
|
37
|
-
with
|
|
40
|
+
with pytest.raises(FailedJobsException):
|
|
38
41
|
# This one is intended to fail.
|
|
39
42
|
Job.Runner.startToil(root, options)
|
|
40
43
|
|
|
@@ -44,30 +47,32 @@ class ResumabilityTest(ToilTest):
|
|
|
44
47
|
# system code notices that the job has been deleted despite
|
|
45
48
|
# the failure and avoids the failure.
|
|
46
49
|
options.restart = True
|
|
47
|
-
tempDir =
|
|
48
|
-
|
|
50
|
+
tempDir = tmp_path / "tempdir"
|
|
51
|
+
tempDir.mkdir()
|
|
52
|
+
options.logFile = str(tempDir / "log.txt")
|
|
49
53
|
Job.Runner.startToil(root, options)
|
|
50
54
|
with open(options.logFile) as f:
|
|
51
55
|
logString = f.read()
|
|
52
56
|
# We are looking for e.g. "Batch system is reporting that
|
|
53
57
|
# the jobGraph with batch system ID: 1 and jobGraph
|
|
54
58
|
# store ID: n/t/jobwbijqL failed with exit value 1"
|
|
55
|
-
|
|
59
|
+
assert "failed with exit value" not in logString
|
|
56
60
|
|
|
57
|
-
def test_chaining(self):
|
|
61
|
+
def test_chaining(self, tmp_path: Path) -> None:
|
|
58
62
|
"""
|
|
59
63
|
Tests that a job which is chained to and fails can resume and succeed.
|
|
60
64
|
"""
|
|
61
65
|
|
|
62
|
-
options = Job.Runner.getDefaultOptions(
|
|
66
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
63
67
|
options.logLevel = "DEBUG"
|
|
64
68
|
options.retryCount = 0
|
|
65
|
-
tempDir =
|
|
66
|
-
|
|
69
|
+
tempDir = tmp_path / "tempdir"
|
|
70
|
+
tempDir.mkdir()
|
|
71
|
+
options.logFile = str(tempDir / "log.txt")
|
|
67
72
|
|
|
68
73
|
root = Job.wrapJobFn(chaining_parent)
|
|
69
74
|
|
|
70
|
-
with
|
|
75
|
+
with pytest.raises(FailedJobsException):
|
|
71
76
|
# This one is intended to fail.
|
|
72
77
|
Job.Runner.startToil(root, options)
|
|
73
78
|
|
|
@@ -83,7 +88,7 @@ class ResumabilityTest(ToilTest):
|
|
|
83
88
|
Job.Runner.startToil(root, options)
|
|
84
89
|
|
|
85
90
|
|
|
86
|
-
def parent(job):
|
|
91
|
+
def parent(job: Job) -> None:
|
|
87
92
|
"""
|
|
88
93
|
Set up a bunch of dummy child jobs, and a bad job that needs to be
|
|
89
94
|
restarted as the follow on.
|
|
@@ -93,21 +98,21 @@ def parent(job):
|
|
|
93
98
|
job.addFollowOnJobFn(badChild)
|
|
94
99
|
|
|
95
100
|
|
|
96
|
-
def chaining_parent(job):
|
|
101
|
+
def chaining_parent(job: Job) -> None:
|
|
97
102
|
"""
|
|
98
103
|
Set up a failing job to chain to.
|
|
99
104
|
"""
|
|
100
105
|
job.addFollowOnJobFn(badChild)
|
|
101
106
|
|
|
102
107
|
|
|
103
|
-
def goodChild(job):
|
|
108
|
+
def goodChild(job: Job) -> None:
|
|
104
109
|
"""
|
|
105
110
|
Does nothing.
|
|
106
111
|
"""
|
|
107
112
|
return
|
|
108
113
|
|
|
109
114
|
|
|
110
|
-
def badChild(job):
|
|
115
|
+
def badChild(job: JobFunctionWrappingJob) -> None:
|
|
111
116
|
"""
|
|
112
117
|
Fails the first time it's run, succeeds the second time.
|
|
113
118
|
"""
|
|
@@ -11,93 +11,92 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
import argparse
|
|
15
|
+
from collections.abc import Callable
|
|
14
16
|
import os
|
|
15
|
-
import
|
|
17
|
+
from pathlib import Path
|
|
16
18
|
|
|
17
19
|
from toil.exceptions import FailedJobsException
|
|
18
|
-
from toil.job import Job
|
|
19
|
-
from toil.test import ToilTest
|
|
20
|
+
from toil.job import Job, JobFunctionWrappingJob
|
|
20
21
|
|
|
22
|
+
import pytest
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
|
|
25
|
+
class TestCleanWorkDir:
|
|
23
26
|
"""
|
|
24
27
|
Tests testing :class:toil.fileStores.abstractFileStore.AbstractFileStore
|
|
25
28
|
"""
|
|
26
29
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
super().tearDown()
|
|
33
|
-
shutil.rmtree(self.testDir)
|
|
34
|
-
|
|
35
|
-
def testNever(self):
|
|
36
|
-
retainedTempData = self._runAndReturnWorkDir("never", job=tempFileTestJob)
|
|
37
|
-
self.assertNotEqual(
|
|
38
|
-
retainedTempData,
|
|
39
|
-
[],
|
|
30
|
+
def testNever(self, tmp_path: Path) -> None:
|
|
31
|
+
retainedTempData = self._runAndReturnWorkDir(
|
|
32
|
+
tmp_path, "never", job=tempFileTestJob
|
|
33
|
+
)
|
|
34
|
+
assert retainedTempData != [], (
|
|
40
35
|
"The worker's temporary workspace was deleted despite "
|
|
41
|
-
"cleanWorkDir being set to 'never'"
|
|
36
|
+
"cleanWorkDir being set to 'never'"
|
|
42
37
|
)
|
|
43
38
|
|
|
44
|
-
def testAlways(self):
|
|
45
|
-
retainedTempData = self._runAndReturnWorkDir(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
def testAlways(self, tmp_path: Path) -> None:
|
|
40
|
+
retainedTempData = self._runAndReturnWorkDir(
|
|
41
|
+
tmp_path, "always", job=tempFileTestJob
|
|
42
|
+
)
|
|
43
|
+
assert retainedTempData == [], (
|
|
49
44
|
"The worker's temporary workspace was not deleted despite "
|
|
50
|
-
"cleanWorkDir being set to 'always'"
|
|
45
|
+
"cleanWorkDir being set to 'always'"
|
|
51
46
|
)
|
|
52
47
|
|
|
53
|
-
def testOnErrorWithError(self):
|
|
48
|
+
def testOnErrorWithError(self, tmp_path: Path) -> None:
|
|
54
49
|
retainedTempData = self._runAndReturnWorkDir(
|
|
55
|
-
"onError", job=tempFileTestErrorJob, expectError=True
|
|
50
|
+
tmp_path, "onError", job=tempFileTestErrorJob, expectError=True
|
|
56
51
|
)
|
|
57
|
-
|
|
58
|
-
retainedTempData,
|
|
59
|
-
[],
|
|
52
|
+
assert retainedTempData == [], (
|
|
60
53
|
"The worker's temporary workspace was not deleted despite "
|
|
61
|
-
"an error occurring and cleanWorkDir being set to 'onError'"
|
|
54
|
+
"an error occurring and cleanWorkDir being set to 'onError'"
|
|
62
55
|
)
|
|
63
56
|
|
|
64
|
-
def testOnErrorWithNoError(self):
|
|
65
|
-
retainedTempData = self._runAndReturnWorkDir(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
def testOnErrorWithNoError(self, tmp_path: Path) -> None:
|
|
58
|
+
retainedTempData = self._runAndReturnWorkDir(
|
|
59
|
+
tmp_path, "onError", job=tempFileTestJob
|
|
60
|
+
)
|
|
61
|
+
assert retainedTempData != [], (
|
|
69
62
|
"The worker's temporary workspace was deleted despite "
|
|
70
|
-
"no error occurring and cleanWorkDir being set to 'onError'"
|
|
63
|
+
"no error occurring and cleanWorkDir being set to 'onError'"
|
|
71
64
|
)
|
|
72
65
|
|
|
73
|
-
def testOnSuccessWithError(self):
|
|
66
|
+
def testOnSuccessWithError(self, tmp_path: Path) -> None:
|
|
74
67
|
retainedTempData = self._runAndReturnWorkDir(
|
|
75
|
-
"onSuccess", job=tempFileTestErrorJob, expectError=True
|
|
68
|
+
tmp_path, "onSuccess", job=tempFileTestErrorJob, expectError=True
|
|
76
69
|
)
|
|
77
|
-
|
|
78
|
-
retainedTempData,
|
|
79
|
-
[],
|
|
70
|
+
assert retainedTempData != [], (
|
|
80
71
|
"The worker's temporary workspace was deleted despite "
|
|
81
|
-
"an error occurring and cleanWorkDir being set to 'onSuccesss'"
|
|
72
|
+
"an error occurring and cleanWorkDir being set to 'onSuccesss'"
|
|
82
73
|
)
|
|
83
74
|
|
|
84
|
-
def testOnSuccessWithSuccess(self):
|
|
85
|
-
retainedTempData = self._runAndReturnWorkDir(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
75
|
+
def testOnSuccessWithSuccess(self, tmp_path: Path) -> None:
|
|
76
|
+
retainedTempData = self._runAndReturnWorkDir(
|
|
77
|
+
tmp_path, "onSuccess", job=tempFileTestJob
|
|
78
|
+
)
|
|
79
|
+
assert retainedTempData == [], (
|
|
89
80
|
"The worker's temporary workspace was not deleted despite "
|
|
90
|
-
"a successful job execution and cleanWorkDir being set to 'onSuccesss'"
|
|
81
|
+
"a successful job execution and cleanWorkDir being set to 'onSuccesss'"
|
|
91
82
|
)
|
|
92
83
|
|
|
93
|
-
def _runAndReturnWorkDir(
|
|
84
|
+
def _runAndReturnWorkDir(
|
|
85
|
+
self,
|
|
86
|
+
tmp_path: Path,
|
|
87
|
+
cleanWorkDir: str,
|
|
88
|
+
job: Callable[[JobFunctionWrappingJob], None],
|
|
89
|
+
expectError: bool = False,
|
|
90
|
+
) -> list[str]:
|
|
94
91
|
"""
|
|
95
92
|
Runs toil with the specified job and cleanWorkDir setting. expectError determines whether the test's toil
|
|
96
93
|
run is expected to succeed, and the test will fail if that expectation is not met. returns the contents of
|
|
97
94
|
the workDir after completion of the run
|
|
98
95
|
"""
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
workdir = tmp_path / "testDir"
|
|
97
|
+
workdir.mkdir()
|
|
98
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
99
|
+
options.workDir = str(workdir)
|
|
101
100
|
options.clean = "always"
|
|
102
101
|
options.cleanWorkDir = cleanWorkDir
|
|
103
102
|
A = Job.wrapJobFn(job)
|
|
@@ -105,26 +104,30 @@ class CleanWorkDirTest(ToilTest):
|
|
|
105
104
|
self._launchError(A, options)
|
|
106
105
|
else:
|
|
107
106
|
self._launchRegular(A, options)
|
|
108
|
-
return os.listdir(
|
|
107
|
+
return os.listdir(workdir)
|
|
109
108
|
|
|
110
|
-
def _launchRegular(
|
|
109
|
+
def _launchRegular(
|
|
110
|
+
self, A: JobFunctionWrappingJob, options: argparse.Namespace
|
|
111
|
+
) -> None:
|
|
111
112
|
Job.Runner.startToil(A, options)
|
|
112
113
|
|
|
113
|
-
def _launchError(
|
|
114
|
+
def _launchError(
|
|
115
|
+
self, A: JobFunctionWrappingJob, options: argparse.Namespace
|
|
116
|
+
) -> None:
|
|
114
117
|
try:
|
|
115
118
|
Job.Runner.startToil(A, options)
|
|
116
119
|
except FailedJobsException:
|
|
117
120
|
pass # we expect a job to fail here
|
|
118
121
|
else:
|
|
119
|
-
|
|
122
|
+
pytest.fail("Toil run succeeded unexpectedly")
|
|
120
123
|
|
|
121
124
|
|
|
122
|
-
def tempFileTestJob(job):
|
|
125
|
+
def tempFileTestJob(job: JobFunctionWrappingJob) -> None:
|
|
123
126
|
with open(job.fileStore.getLocalTempFile(), "w") as f:
|
|
124
127
|
f.write("test file retention")
|
|
125
128
|
|
|
126
129
|
|
|
127
|
-
def tempFileTestErrorJob(job):
|
|
130
|
+
def tempFileTestErrorJob(job: JobFunctionWrappingJob) -> None:
|
|
128
131
|
with open(job.fileStore.getLocalTempFile(), "w") as f:
|
|
129
132
|
f.write("test file retention")
|
|
130
133
|
raise RuntimeError() # test failure
|
toil/test/src/systemTest.py
CHANGED
|
@@ -1,46 +1,50 @@
|
|
|
1
1
|
import errno
|
|
2
2
|
import multiprocessing
|
|
3
3
|
import os
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
from functools import partial
|
|
6
|
+
from typing import Any, Optional
|
|
5
7
|
|
|
6
8
|
from toil.lib.io import mkdtemp
|
|
7
9
|
from toil.lib.threading import cpu_count
|
|
8
|
-
from toil.test import ToilTest
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
class
|
|
12
|
+
class TestSystem:
|
|
12
13
|
"""Test various assumptions about the operating system's behavior."""
|
|
13
14
|
|
|
14
|
-
def testAtomicityOfNonEmptyDirectoryRenames(self):
|
|
15
|
+
def testAtomicityOfNonEmptyDirectoryRenames(self, tmp_path: Path) -> None:
|
|
15
16
|
for _ in range(100):
|
|
16
|
-
parent =
|
|
17
|
-
|
|
17
|
+
parent = tmp_path / f"parent{_}"
|
|
18
|
+
parent.mkdir()
|
|
19
|
+
child = parent / "child"
|
|
18
20
|
# Use processes (as opposed to threads) to prevent GIL from ordering things artificially
|
|
19
21
|
pool = multiprocessing.Pool(processes=cpu_count())
|
|
20
22
|
try:
|
|
21
23
|
numTasks = cpu_count() * 10
|
|
22
|
-
|
|
24
|
+
temp_grandChildIds = pool.map_async(
|
|
23
25
|
func=partial(
|
|
24
26
|
_testAtomicityOfNonEmptyDirectoryRenamesTask, parent, child
|
|
25
27
|
),
|
|
26
28
|
iterable=list(range(numTasks)),
|
|
27
29
|
)
|
|
28
|
-
grandChildIds =
|
|
30
|
+
grandChildIds = temp_grandChildIds.get()
|
|
29
31
|
finally:
|
|
30
32
|
pool.close()
|
|
31
33
|
pool.join()
|
|
32
|
-
|
|
34
|
+
assert len(grandChildIds) == numTasks
|
|
33
35
|
# Assert that we only had one winner
|
|
34
36
|
grandChildIds = [n for n in grandChildIds if n is not None]
|
|
35
|
-
|
|
37
|
+
assert len(grandChildIds) == 1
|
|
36
38
|
# Assert that the winner's grandChild wasn't silently overwritten by a looser
|
|
37
39
|
expectedGrandChildId = grandChildIds[0]
|
|
38
|
-
actualGrandChild =
|
|
39
|
-
actualGrandChildId =
|
|
40
|
-
|
|
40
|
+
actualGrandChild = child / "grandChild"
|
|
41
|
+
actualGrandChildId = actualGrandChild.stat().st_ino
|
|
42
|
+
assert actualGrandChildId == expectedGrandChildId
|
|
41
43
|
|
|
42
44
|
|
|
43
|
-
def _testAtomicityOfNonEmptyDirectoryRenamesTask(
|
|
45
|
+
def _testAtomicityOfNonEmptyDirectoryRenamesTask(
|
|
46
|
+
parent: Path, child: Path, _: Any
|
|
47
|
+
) -> Optional[int]:
|
|
44
48
|
tmpChildDir = mkdtemp(dir=parent, prefix="child", suffix=".tmp")
|
|
45
49
|
grandChild = os.path.join(tmpChildDir, "grandChild")
|
|
46
50
|
open(grandChild, "w").close()
|
toil/test/src/threadingTest.py
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import multiprocessing
|
|
3
3
|
import os
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
import random
|
|
5
6
|
import time
|
|
6
7
|
import traceback
|
|
7
8
|
from functools import partial
|
|
8
9
|
|
|
9
10
|
from toil.lib.threading import LastProcessStandingArena, cpu_count, global_mutex
|
|
10
|
-
from toil.test import ToilTest
|
|
11
11
|
|
|
12
12
|
log = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class TestThreading:
|
|
16
16
|
"""Test Toil threading/synchronization tools."""
|
|
17
17
|
|
|
18
|
-
def testGlobalMutexOrdering(self):
|
|
18
|
+
def testGlobalMutexOrdering(self, tmp_path: Path) -> None:
|
|
19
19
|
for it in range(10):
|
|
20
20
|
log.info("Iteration %d", it)
|
|
21
21
|
|
|
22
|
-
scope =
|
|
22
|
+
scope = tmp_path / f"tempDir{it}"
|
|
23
|
+
scope.mkdir()
|
|
23
24
|
mutex = "mutex"
|
|
24
25
|
# Use processes (as opposed to threads) to prevent GIL from ordering things artificially
|
|
25
26
|
pool = multiprocessing.Pool(processes=cpu_count())
|
|
26
27
|
try:
|
|
27
28
|
numTasks = 100
|
|
28
|
-
|
|
29
|
+
temp_results = pool.map_async(
|
|
29
30
|
func=partial(_testGlobalMutexOrderingTask, scope, mutex),
|
|
30
31
|
iterable=list(range(numTasks)),
|
|
31
32
|
)
|
|
32
|
-
results =
|
|
33
|
+
results = temp_results.get()
|
|
33
34
|
finally:
|
|
34
35
|
pool.close()
|
|
35
36
|
pool.join()
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
assert len(results) == numTasks
|
|
38
39
|
for item in results:
|
|
39
40
|
# Make sure all workers say they succeeded
|
|
40
|
-
|
|
41
|
+
assert item is True
|
|
41
42
|
|
|
42
|
-
def testLastProcessStanding(self):
|
|
43
|
+
def testLastProcessStanding(self, tmp_path: Path) -> None:
|
|
43
44
|
for it in range(10):
|
|
44
45
|
log.info("Iteration %d", it)
|
|
45
46
|
|
|
46
|
-
scope =
|
|
47
|
+
scope = tmp_path / f"tempDir{it}"
|
|
48
|
+
scope.mkdir()
|
|
47
49
|
arena_name = "thunderdome"
|
|
48
50
|
# Use processes (as opposed to threads) to prevent GIL from ordering things artificially
|
|
49
51
|
pool = multiprocessing.Pool(processes=cpu_count())
|
|
50
52
|
try:
|
|
51
53
|
numTasks = 100
|
|
52
|
-
|
|
54
|
+
temp_results = pool.map_async(
|
|
53
55
|
func=partial(_testLastProcessStandingTask, scope, arena_name),
|
|
54
56
|
iterable=list(range(numTasks)),
|
|
55
57
|
)
|
|
56
|
-
results =
|
|
58
|
+
results = temp_results.get()
|
|
57
59
|
finally:
|
|
58
60
|
pool.close()
|
|
59
61
|
pool.join()
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
assert len(results) == numTasks
|
|
62
64
|
for item in results:
|
|
63
65
|
# Make sure all workers say they succeeded
|
|
64
|
-
|
|
66
|
+
assert item is True
|
|
65
67
|
for filename in os.listdir(scope):
|
|
66
68
|
assert not filename.startswith(
|
|
67
69
|
"precious"
|
|
68
70
|
), f"File {filename} still exists"
|
|
69
71
|
|
|
70
72
|
|
|
71
|
-
def _testGlobalMutexOrderingTask(scope, mutex, number):
|
|
73
|
+
def _testGlobalMutexOrderingTask(scope: Path, mutex: str, number: int) -> bool:
|
|
72
74
|
try:
|
|
73
75
|
# We will all fight over the potato
|
|
74
|
-
potato =
|
|
76
|
+
potato = scope / "potato"
|
|
75
77
|
|
|
76
78
|
with global_mutex(scope, mutex):
|
|
77
79
|
log.info("PID %d = num %d running", os.getpid(), number)
|
|
78
|
-
assert not
|
|
79
|
-
potato
|
|
80
|
-
), "We see someone else holding the potato file"
|
|
80
|
+
assert not potato.exists(), "We see someone else holding the potato file"
|
|
81
81
|
|
|
82
82
|
# Put our name there
|
|
83
|
-
with open(
|
|
83
|
+
with potato.open("w") as out_stream:
|
|
84
84
|
out_stream.write(str(number))
|
|
85
85
|
|
|
86
86
|
# Wait
|
|
87
87
|
time.sleep(random.random() * 0.01)
|
|
88
88
|
|
|
89
89
|
# Make sure our name is still there
|
|
90
|
-
with open(
|
|
90
|
+
with potato.open() as in_stream:
|
|
91
91
|
seen = in_stream.read().rstrip()
|
|
92
92
|
assert seen == str(
|
|
93
93
|
number
|
|
94
94
|
), f"We are {number} but {seen} stole our potato!"
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
assert not
|
|
96
|
+
potato.unlink()
|
|
97
|
+
assert not potato.exists(), "We left the potato behind"
|
|
98
98
|
log.info("PID %d = num %d dropped potato", os.getpid(), number)
|
|
99
99
|
return True
|
|
100
100
|
except:
|
|
@@ -102,7 +102,7 @@ def _testGlobalMutexOrderingTask(scope, mutex, number):
|
|
|
102
102
|
return False
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
def _testLastProcessStandingTask(scope, arena_name, number):
|
|
105
|
+
def _testLastProcessStandingTask(scope: Path, arena_name: str, number: int) -> bool:
|
|
106
106
|
try:
|
|
107
107
|
arena = LastProcessStandingArena(scope, arena_name)
|
|
108
108
|
|
|
@@ -110,20 +110,18 @@ def _testLastProcessStandingTask(scope, arena_name, number):
|
|
|
110
110
|
log.info("PID %d = num %d entered arena", os.getpid(), number)
|
|
111
111
|
try:
|
|
112
112
|
# We all make files
|
|
113
|
-
my_precious =
|
|
113
|
+
my_precious = scope / f"precious{number}"
|
|
114
114
|
|
|
115
115
|
# Put our name there
|
|
116
|
-
with open(
|
|
116
|
+
with my_precious.open("w") as out_stream:
|
|
117
117
|
out_stream.write(str(number))
|
|
118
118
|
|
|
119
119
|
# Wait
|
|
120
120
|
time.sleep(random.random() * 0.01)
|
|
121
121
|
|
|
122
122
|
# Make sure our file is still there unmodified
|
|
123
|
-
assert
|
|
124
|
-
|
|
125
|
-
), f"Precious file {my_precious} has been stolen!"
|
|
126
|
-
with open(my_precious) as in_stream:
|
|
123
|
+
assert my_precious.exists(), f"Precious file {my_precious} has been stolen!"
|
|
124
|
+
with my_precious.open() as in_stream:
|
|
127
125
|
seen = in_stream.read().rstrip()
|
|
128
126
|
assert seen == str(
|
|
129
127
|
number
|
|
@@ -131,7 +129,6 @@ def _testLastProcessStandingTask(scope, arena_name, number):
|
|
|
131
129
|
finally:
|
|
132
130
|
was_last = False
|
|
133
131
|
for _ in arena.leave():
|
|
134
|
-
was_last = True
|
|
135
132
|
log.info("PID %d = num %d is last standing", os.getpid(), number)
|
|
136
133
|
|
|
137
134
|
# Clean up all the files
|
|
@@ -143,7 +140,7 @@ def _testLastProcessStandingTask(scope, arena_name, number):
|
|
|
143
140
|
number,
|
|
144
141
|
filename,
|
|
145
142
|
)
|
|
146
|
-
|
|
143
|
+
(scope / filename).unlink()
|
|
147
144
|
|
|
148
145
|
log.info("PID %d = num %d left arena", os.getpid(), number)
|
|
149
146
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
B
|