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
|
@@ -11,10 +11,13 @@
|
|
|
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
|
+
from collections.abc import Iterable, Generator
|
|
15
|
+
import argparse
|
|
14
16
|
import fcntl
|
|
15
17
|
import itertools
|
|
16
18
|
import logging
|
|
17
19
|
import os
|
|
20
|
+
from pathlib import Path
|
|
18
21
|
import subprocess
|
|
19
22
|
import sys
|
|
20
23
|
import tempfile
|
|
@@ -23,6 +26,7 @@ import time
|
|
|
23
26
|
from abc import ABCMeta, abstractmethod
|
|
24
27
|
from fractions import Fraction
|
|
25
28
|
from unittest import skipIf
|
|
29
|
+
from typing import Optional, Any, TYPE_CHECKING
|
|
26
30
|
|
|
27
31
|
from toil.batchSystems.abstractBatchSystem import (
|
|
28
32
|
AbstractBatchSystem,
|
|
@@ -43,7 +47,9 @@ from toil.batchSystems.registry import (
|
|
|
43
47
|
)
|
|
44
48
|
from toil.batchSystems.singleMachine import SingleMachineBatchSystem
|
|
45
49
|
from toil.common import Config, Toil
|
|
46
|
-
from toil.
|
|
50
|
+
from toil.fileStores.abstractFileStore import AbstractFileStore
|
|
51
|
+
from toil.job import Job, JobDescription, Requirer, ServiceHostJob
|
|
52
|
+
from toil.lib.misc import StrPath
|
|
47
53
|
from toil.lib.retry import retry_flaky_test
|
|
48
54
|
from toil.lib.threading import cpu_count
|
|
49
55
|
from toil.test import (
|
|
@@ -60,8 +66,16 @@ from toil.test import (
|
|
|
60
66
|
needs_slurm,
|
|
61
67
|
needs_torque,
|
|
62
68
|
slow,
|
|
69
|
+
pslow,
|
|
70
|
+
pneeds_mesos,
|
|
63
71
|
)
|
|
64
72
|
|
|
73
|
+
import pytest
|
|
74
|
+
|
|
75
|
+
if TYPE_CHECKING:
|
|
76
|
+
from toil.batchSystems.mesos.batchSystem import MesosBatchSystem
|
|
77
|
+
|
|
78
|
+
|
|
65
79
|
logger = logging.getLogger(__name__)
|
|
66
80
|
|
|
67
81
|
# How many cores should be utilized by this test. The test will fail if the running system
|
|
@@ -83,19 +97,19 @@ class BatchSystemPluginTest(ToilTest):
|
|
|
83
97
|
Class for testing batch system plugin functionality.
|
|
84
98
|
"""
|
|
85
99
|
|
|
86
|
-
def setUp(self):
|
|
100
|
+
def setUp(self) -> None:
|
|
87
101
|
# Save plugin state so our plugin doesn't stick around after the test
|
|
88
102
|
# (and create duplicate options)
|
|
89
103
|
self.__state = save_batch_system_plugin_state()
|
|
90
104
|
super().setUp()
|
|
91
105
|
|
|
92
|
-
def tearDown(self):
|
|
106
|
+
def tearDown(self) -> None:
|
|
93
107
|
# Restore plugin state
|
|
94
108
|
restore_batch_system_plugin_state(self.__state)
|
|
95
109
|
super().tearDown()
|
|
96
110
|
|
|
97
|
-
def test_add_batch_system_factory(self):
|
|
98
|
-
def test_batch_system_factory():
|
|
111
|
+
def test_add_batch_system_factory(self) -> None:
|
|
112
|
+
def test_batch_system_factory() -> type[SingleMachineBatchSystem]:
|
|
99
113
|
# TODO: Adding the same batch system under multiple names means we
|
|
100
114
|
# can't actually create Toil options, because each version tries to
|
|
101
115
|
# add its arguments.
|
|
@@ -112,7 +126,6 @@ class hidden:
|
|
|
112
126
|
|
|
113
127
|
http://stackoverflow.com/questions/1323455/python-unit-test-with-base-and-sub-class#answer-25695512
|
|
114
128
|
"""
|
|
115
|
-
|
|
116
129
|
class AbstractBatchSystemTest(ToilTest, metaclass=ABCMeta):
|
|
117
130
|
"""
|
|
118
131
|
A base test case with generic tests that every batch system should pass.
|
|
@@ -124,11 +137,11 @@ class hidden:
|
|
|
124
137
|
def createBatchSystem(self) -> AbstractBatchSystem:
|
|
125
138
|
raise NotImplementedError
|
|
126
139
|
|
|
127
|
-
def supportsWallTime(self):
|
|
140
|
+
def supportsWallTime(self) -> bool:
|
|
128
141
|
return False
|
|
129
142
|
|
|
130
143
|
@classmethod
|
|
131
|
-
def createConfig(cls):
|
|
144
|
+
def createConfig(cls) -> Config:
|
|
132
145
|
"""
|
|
133
146
|
Returns a dummy config for the batch system tests. We need a workflowID to be set up
|
|
134
147
|
since we are running tests without setting up a jobstore. This is the class version
|
|
@@ -143,7 +156,7 @@ class hidden:
|
|
|
143
156
|
config.cleanWorkDir = "always"
|
|
144
157
|
return config
|
|
145
158
|
|
|
146
|
-
def _createConfig(self):
|
|
159
|
+
def _createConfig(self) -> Config:
|
|
147
160
|
"""
|
|
148
161
|
Returns a dummy config for the batch system tests. We need a workflowID to be set up
|
|
149
162
|
since we are running tests without setting up a jobstore.
|
|
@@ -152,7 +165,9 @@ class hidden:
|
|
|
152
165
|
"""
|
|
153
166
|
return self.createConfig()
|
|
154
167
|
|
|
155
|
-
def _mockJobDescription(
|
|
168
|
+
def _mockJobDescription(
|
|
169
|
+
self, jobStoreID: Optional[str] = None, **kwargs: Any
|
|
170
|
+
) -> JobDescription:
|
|
156
171
|
"""
|
|
157
172
|
Create a mock-up JobDescription with the given ID and other parameters.
|
|
158
173
|
"""
|
|
@@ -168,17 +183,17 @@ class hidden:
|
|
|
168
183
|
return desc
|
|
169
184
|
|
|
170
185
|
@classmethod
|
|
171
|
-
def setUpClass(cls):
|
|
186
|
+
def setUpClass(cls) -> None:
|
|
172
187
|
super().setUpClass()
|
|
173
188
|
logging.basicConfig(level=logging.DEBUG)
|
|
174
189
|
|
|
175
|
-
def setUp(self):
|
|
190
|
+
def setUp(self) -> None:
|
|
176
191
|
super().setUp()
|
|
177
192
|
self.config = self._createConfig()
|
|
178
193
|
self.batchSystem = self.createBatchSystem()
|
|
179
194
|
self.tempDir = self._createTempDir("testFiles")
|
|
180
195
|
|
|
181
|
-
def tearDown(self):
|
|
196
|
+
def tearDown(self) -> None:
|
|
182
197
|
self.batchSystem.shutdown()
|
|
183
198
|
super().tearDown()
|
|
184
199
|
|
|
@@ -189,11 +204,11 @@ class hidden:
|
|
|
189
204
|
"""
|
|
190
205
|
return 120
|
|
191
206
|
|
|
192
|
-
def test_available_cores(self):
|
|
207
|
+
def test_available_cores(self) -> None:
|
|
193
208
|
self.assertTrue(cpu_count() >= numCores)
|
|
194
209
|
|
|
195
210
|
@retry_flaky_test(prepare=[tearDown, setUp])
|
|
196
|
-
def test_run_jobs(self):
|
|
211
|
+
def test_run_jobs(self) -> None:
|
|
197
212
|
jobDesc1 = self._mockJobDescription(
|
|
198
213
|
jobName="test1",
|
|
199
214
|
unitName=None,
|
|
@@ -247,6 +262,7 @@ class hidden:
|
|
|
247
262
|
job3 = self.batchSystem.issueBatchJob("mktemp -d", jobDesc3)
|
|
248
263
|
|
|
249
264
|
jobUpdateInfo = self.batchSystem.getUpdatedBatchJob(maxWait=1000)
|
|
265
|
+
assert jobUpdateInfo is not None
|
|
250
266
|
jobID, exitStatus, wallTime = (
|
|
251
267
|
jobUpdateInfo.jobID,
|
|
252
268
|
jobUpdateInfo.exitStatus,
|
|
@@ -260,6 +276,7 @@ class hidden:
|
|
|
260
276
|
self.assertEqual(jobID, job3)
|
|
261
277
|
self.assertEqual(exitStatus, 0)
|
|
262
278
|
if self.supportsWallTime():
|
|
279
|
+
assert wallTime is not None
|
|
263
280
|
self.assertTrue(wallTime > 0)
|
|
264
281
|
else:
|
|
265
282
|
self.assertIsNone(wallTime)
|
|
@@ -270,7 +287,7 @@ class hidden:
|
|
|
270
287
|
# Make sure killBatchJobs can handle jobs that don't exist
|
|
271
288
|
self.batchSystem.killBatchJobs([10])
|
|
272
289
|
|
|
273
|
-
def test_set_env(self):
|
|
290
|
+
def test_set_env(self) -> None:
|
|
274
291
|
# Start with a relatively safe script
|
|
275
292
|
script_shell = (
|
|
276
293
|
'if [ "x${FOO}" == "xbar" ] ; then exit 23 ; else exit 42 ; fi'
|
|
@@ -289,6 +306,7 @@ class hidden:
|
|
|
289
306
|
)
|
|
290
307
|
job4 = self.batchSystem.issueBatchJob(command, jobDesc4)
|
|
291
308
|
jobUpdateInfo = self.batchSystem.getUpdatedBatchJob(maxWait=1000)
|
|
309
|
+
assert jobUpdateInfo is not None
|
|
292
310
|
jobID, exitStatus, wallTime = (
|
|
293
311
|
jobUpdateInfo.jobID,
|
|
294
312
|
jobUpdateInfo.exitStatus,
|
|
@@ -305,11 +323,12 @@ class hidden:
|
|
|
305
323
|
requirements=defaultRequirements,
|
|
306
324
|
)
|
|
307
325
|
job5 = self.batchSystem.issueBatchJob(command, jobDesc5)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
self.assertEqual(
|
|
326
|
+
jobUpdateInfo2 = self.batchSystem.getUpdatedBatchJob(maxWait=1000)
|
|
327
|
+
assert jobUpdateInfo2 is not None
|
|
328
|
+
self.assertEqual(jobUpdateInfo2.exitStatus, 23)
|
|
329
|
+
self.assertEqual(jobUpdateInfo2.jobID, job5)
|
|
311
330
|
|
|
312
|
-
def test_set_job_env(self):
|
|
331
|
+
def test_set_job_env(self) -> None:
|
|
313
332
|
"""Test the mechanism for setting per-job environment variables to batch system jobs."""
|
|
314
333
|
script = 'if [ "x${FOO}" == "xbar" ] ; then exit 23 ; else exit 42 ; fi'
|
|
315
334
|
command = 'bash -c "\\${@}" bash eval ' + script.replace(";", r"\;")
|
|
@@ -325,6 +344,7 @@ class hidden:
|
|
|
325
344
|
command, job_desc_6, job_environment={"FOO": "bar"}
|
|
326
345
|
)
|
|
327
346
|
job_update_info = self.batchSystem.getUpdatedBatchJob(maxWait=1000)
|
|
347
|
+
assert job_update_info is not None
|
|
328
348
|
self.assertEqual(job_update_info.exitStatus, 23) # this should succeed
|
|
329
349
|
self.assertEqual(job_update_info.jobID, job6)
|
|
330
350
|
# Now check that the environment variable doesn't exist for other jobs
|
|
@@ -335,11 +355,12 @@ class hidden:
|
|
|
335
355
|
requirements=defaultRequirements,
|
|
336
356
|
)
|
|
337
357
|
job7 = self.batchSystem.issueBatchJob(command, job_desc_7)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
self.assertEqual(
|
|
358
|
+
job_update_info2 = self.batchSystem.getUpdatedBatchJob(maxWait=1000)
|
|
359
|
+
assert job_update_info2 is not None
|
|
360
|
+
self.assertEqual(job_update_info2.exitStatus, 42)
|
|
361
|
+
self.assertEqual(job_update_info2.jobID, job7)
|
|
341
362
|
|
|
342
|
-
def testCheckResourceRequest(self):
|
|
363
|
+
def testCheckResourceRequest(self) -> None:
|
|
343
364
|
if isinstance(self.batchSystem, BatchSystemSupport):
|
|
344
365
|
check_resource_request = self.batchSystem.check_resource_request
|
|
345
366
|
# Assuming we have <2000 cores, this should be too many cores
|
|
@@ -405,11 +426,11 @@ class hidden:
|
|
|
405
426
|
Requirer(dict(memory=10, cores=1, disk=100, accelerators=[]))
|
|
406
427
|
)
|
|
407
428
|
|
|
408
|
-
def testScalableBatchSystem(self):
|
|
429
|
+
def testScalableBatchSystem(self) -> None:
|
|
409
430
|
# If instance of scalable batch system
|
|
410
431
|
pass
|
|
411
432
|
|
|
412
|
-
def _waitForJobsToIssue(self, numJobs):
|
|
433
|
+
def _waitForJobsToIssue(self, numJobs: int) -> list[int]:
|
|
413
434
|
issuedIDs = []
|
|
414
435
|
for it in range(20):
|
|
415
436
|
issuedIDs = self.batchSystem.getIssuedBatchJobIDs()
|
|
@@ -418,7 +439,7 @@ class hidden:
|
|
|
418
439
|
time.sleep(1)
|
|
419
440
|
return issuedIDs
|
|
420
441
|
|
|
421
|
-
def _waitForJobsToStart(self, numJobs, tries=20):
|
|
442
|
+
def _waitForJobsToStart(self, numJobs: int, tries: int = 20) -> list[int]:
|
|
422
443
|
"""
|
|
423
444
|
Loop until the given number of distinct jobs are in the
|
|
424
445
|
running state, or until the given number of tries is exhausted
|
|
@@ -437,114 +458,109 @@ class hidden:
|
|
|
437
458
|
time.sleep(1)
|
|
438
459
|
return runningIDs
|
|
439
460
|
|
|
440
|
-
class
|
|
461
|
+
class AbstractGridEngineBatchSystemTest(AbstractBatchSystemTest):
|
|
441
462
|
"""
|
|
442
|
-
An abstract
|
|
443
|
-
|
|
463
|
+
An abstract class to reduce redundancy between Grid Engine, Slurm, and other similar batch
|
|
464
|
+
systems
|
|
444
465
|
"""
|
|
445
466
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
467
|
+
def _createConfig(self) -> Config:
|
|
468
|
+
config = super()._createConfig()
|
|
469
|
+
config.statePollingWait = 0.5 # Reduce polling wait so tests run faster
|
|
470
|
+
# can't use _getTestJobStorePath since that method removes the directory
|
|
471
|
+
config.jobStore = "file:" + self._createTempDir("jobStore")
|
|
472
|
+
return config
|
|
449
473
|
|
|
450
|
-
@abstractmethod
|
|
451
|
-
def getBatchSystemName(self):
|
|
452
|
-
"""
|
|
453
|
-
:rtype: (str, AbstractBatchSystem)
|
|
454
|
-
"""
|
|
455
|
-
raise NotImplementedError
|
|
456
474
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
"""
|
|
463
|
-
options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
|
|
464
|
-
options.logLevel = "DEBUG"
|
|
465
|
-
options.batchSystem = self.batchSystemName
|
|
466
|
-
options.workDir = tempDir
|
|
467
|
-
options.maxCores = self.cpuCount
|
|
468
|
-
return options
|
|
469
|
-
|
|
470
|
-
def setUp(self):
|
|
471
|
-
self.batchSystemName = self.getBatchSystemName()
|
|
472
|
-
super().setUp()
|
|
475
|
+
class AbstractBatchSystemJobTest:
|
|
476
|
+
"""
|
|
477
|
+
An abstract base class for batch system tests that use a full Toil workflow rather
|
|
478
|
+
than using the batch system directly.
|
|
479
|
+
"""
|
|
473
480
|
|
|
474
|
-
|
|
475
|
-
|
|
481
|
+
cpuCount = cpu_count() if cpu_count() < 4 else 4
|
|
482
|
+
allocatedCores = sorted({1, 2, cpuCount})
|
|
483
|
+
sleepTime = 30
|
|
476
484
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
tempDir = self._createTempDir("testFiles")
|
|
484
|
-
options = self.getOptions(tempDir)
|
|
485
|
-
|
|
486
|
-
counterPath = os.path.join(tempDir, "counter")
|
|
487
|
-
resetCounters(counterPath)
|
|
488
|
-
value, maxValue = getCounters(counterPath)
|
|
489
|
-
assert (value, maxValue) == (0, 0)
|
|
490
|
-
|
|
491
|
-
root = Job()
|
|
492
|
-
for _ in range(self.cpuCount):
|
|
493
|
-
root.addFollowOn(
|
|
494
|
-
Job.wrapFn(
|
|
495
|
-
measureConcurrency,
|
|
496
|
-
counterPath,
|
|
497
|
-
self.sleepTime,
|
|
498
|
-
cores=coresPerJob,
|
|
499
|
-
memory="1M",
|
|
500
|
-
disk="1Mi",
|
|
501
|
-
)
|
|
502
|
-
)
|
|
503
|
-
with Toil(options) as toil:
|
|
504
|
-
toil.start(root)
|
|
505
|
-
_, maxValue = getCounters(counterPath)
|
|
506
|
-
self.assertEqual(maxValue, self.cpuCount // coresPerJob)
|
|
485
|
+
@abstractmethod
|
|
486
|
+
def getBatchSystemName(self) -> str:
|
|
487
|
+
"""
|
|
488
|
+
:rtype: (str, AbstractBatchSystem)
|
|
489
|
+
"""
|
|
490
|
+
raise NotImplementedError
|
|
507
491
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
for cores, expected_omp_threads in test_cases.items():
|
|
523
|
-
if os.environ.get("OMP_NUM_THREADS"):
|
|
524
|
-
expected_omp_threads = os.environ.get("OMP_NUM_THREADS")
|
|
525
|
-
logger.info(
|
|
526
|
-
f"OMP_NUM_THREADS is set. Using OMP_NUM_THREADS={expected_omp_threads} instead."
|
|
527
|
-
)
|
|
528
|
-
with Toil(options) as toil:
|
|
529
|
-
output = toil.start(
|
|
530
|
-
Job.wrapFn(
|
|
531
|
-
get_omp_threads, memory="1Mi", cores=cores, disk="1Mi"
|
|
532
|
-
)
|
|
533
|
-
)
|
|
534
|
-
self.assertEqual(output, expected_omp_threads)
|
|
492
|
+
def getOptions(self, tempDir: Path) -> argparse.Namespace:
|
|
493
|
+
"""
|
|
494
|
+
Configures options for Toil workflow and makes job store.
|
|
495
|
+
:param str tempDir: path to test directory
|
|
496
|
+
:return: Toil options object
|
|
497
|
+
"""
|
|
498
|
+
workdir = tempDir / "workdir"
|
|
499
|
+
workdir.mkdir()
|
|
500
|
+
options = Job.Runner.getDefaultOptions(tempDir / "jobstore")
|
|
501
|
+
options.logLevel = "DEBUG"
|
|
502
|
+
options.batchSystem = self.getBatchSystemName()
|
|
503
|
+
options.workDir = str(workdir)
|
|
504
|
+
options.maxCores = self.cpuCount
|
|
505
|
+
return options
|
|
535
506
|
|
|
536
|
-
|
|
507
|
+
@pslow
|
|
508
|
+
@pytest.mark.slow
|
|
509
|
+
def testJobConcurrency(self, tmp_path: Path) -> None:
|
|
537
510
|
"""
|
|
538
|
-
|
|
539
|
-
systems
|
|
511
|
+
Tests that the batch system is allocating core resources properly for concurrent tasks.
|
|
540
512
|
"""
|
|
513
|
+
for coresPerJob in self.allocatedCores:
|
|
514
|
+
tempDir = tmp_path / f"testFiles_{coresPerJob}"
|
|
515
|
+
tempDir.mkdir()
|
|
516
|
+
options = self.getOptions(tempDir)
|
|
517
|
+
|
|
518
|
+
counterPath = tempDir / "counter"
|
|
519
|
+
resetCounters(counterPath)
|
|
520
|
+
value, maxValue = getCounters(counterPath)
|
|
521
|
+
assert (value, maxValue) == (0, 0)
|
|
522
|
+
|
|
523
|
+
root = Job()
|
|
524
|
+
for _ in range(self.cpuCount):
|
|
525
|
+
root.addFollowOn(
|
|
526
|
+
Job.wrapFn(
|
|
527
|
+
measureConcurrency,
|
|
528
|
+
counterPath,
|
|
529
|
+
self.sleepTime,
|
|
530
|
+
cores=coresPerJob,
|
|
531
|
+
memory="1M",
|
|
532
|
+
disk="1Mi",
|
|
533
|
+
)
|
|
534
|
+
)
|
|
535
|
+
with Toil(options) as toil:
|
|
536
|
+
toil.start(root)
|
|
537
|
+
_, maxValue = getCounters(counterPath)
|
|
538
|
+
assert maxValue == (self.cpuCount // coresPerJob)
|
|
541
539
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
540
|
+
def test_omp_threads(self, tmp_path: Path) -> None:
|
|
541
|
+
"""
|
|
542
|
+
Test if the OMP_NUM_THREADS env var is set correctly based on jobs.cores.
|
|
543
|
+
"""
|
|
544
|
+
test_cases = {
|
|
545
|
+
# mapping of the number of cores to the OMP_NUM_THREADS value
|
|
546
|
+
0.1: "1",
|
|
547
|
+
1: "1",
|
|
548
|
+
2: "2",
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
options = self.getOptions(tmp_path)
|
|
552
|
+
|
|
553
|
+
for cores, expected_omp_threads in test_cases.items():
|
|
554
|
+
if eont := os.environ.get("OMP_NUM_THREADS"):
|
|
555
|
+
expected_omp_threads = eont
|
|
556
|
+
logger.info(
|
|
557
|
+
f"OMP_NUM_THREADS is set. Using OMP_NUM_THREADS={expected_omp_threads} instead."
|
|
558
|
+
)
|
|
559
|
+
with Toil(options) as toil:
|
|
560
|
+
output = toil.start(
|
|
561
|
+
Job.wrapFn(get_omp_threads, memory="1Mi", cores=cores, disk="1Mi")
|
|
562
|
+
)
|
|
563
|
+
assert output == expected_omp_threads
|
|
548
564
|
|
|
549
565
|
|
|
550
566
|
@needs_kubernetes
|
|
@@ -555,10 +571,10 @@ class KubernetesBatchSystemTest(hidden.AbstractBatchSystemTest):
|
|
|
555
571
|
Tests against the Kubernetes batch system
|
|
556
572
|
"""
|
|
557
573
|
|
|
558
|
-
def supportsWallTime(self):
|
|
574
|
+
def supportsWallTime(self) -> bool:
|
|
559
575
|
return True
|
|
560
576
|
|
|
561
|
-
def createBatchSystem(self):
|
|
577
|
+
def createBatchSystem(self) -> AbstractBatchSystem:
|
|
562
578
|
# We know we have Kubernetes so we can import the batch system
|
|
563
579
|
from toil.batchSystems.kubernetes import KubernetesBatchSystem
|
|
564
580
|
|
|
@@ -573,7 +589,7 @@ class KubernetesBatchSystemBenchTest(ToilTest):
|
|
|
573
589
|
Kubernetes batch system unit tests that don't need to actually talk to a cluster.
|
|
574
590
|
"""
|
|
575
591
|
|
|
576
|
-
def test_preemptability_constraints(self):
|
|
592
|
+
def test_preemptability_constraints(self) -> None:
|
|
577
593
|
"""
|
|
578
594
|
Make sure we generate the right preemptability constraints.
|
|
579
595
|
"""
|
|
@@ -645,7 +661,7 @@ class KubernetesBatchSystemBenchTest(ToilTest):
|
|
|
645
661
|
str(spot_spec.tolerations),
|
|
646
662
|
)
|
|
647
663
|
|
|
648
|
-
def test_label_constraints(self):
|
|
664
|
+
def test_label_constraints(self) -> None:
|
|
649
665
|
"""
|
|
650
666
|
Make sure we generate the right preemptability constraints.
|
|
651
667
|
"""
|
|
@@ -694,10 +710,10 @@ class AWSBatchBatchSystemTest(hidden.AbstractBatchSystemTest):
|
|
|
694
710
|
Tests against the AWS Batch batch system
|
|
695
711
|
"""
|
|
696
712
|
|
|
697
|
-
def supportsWallTime(self):
|
|
713
|
+
def supportsWallTime(self) -> bool:
|
|
698
714
|
return True
|
|
699
715
|
|
|
700
|
-
def createBatchSystem(self):
|
|
716
|
+
def createBatchSystem(self) -> AbstractBatchSystem:
|
|
701
717
|
from toil.batchSystems.awsBatch import AWSBatchBatchSystem
|
|
702
718
|
|
|
703
719
|
return AWSBatchBatchSystem(
|
|
@@ -716,8 +732,10 @@ class MesosBatchSystemTest(hidden.AbstractBatchSystemTest, MesosTestSupport):
|
|
|
716
732
|
Tests against the Mesos batch system
|
|
717
733
|
"""
|
|
718
734
|
|
|
735
|
+
batchSystem: "MesosBatchSystem"
|
|
736
|
+
|
|
719
737
|
@classmethod
|
|
720
|
-
def createConfig(cls):
|
|
738
|
+
def createConfig(cls) -> Config:
|
|
721
739
|
"""
|
|
722
740
|
needs to set mesos_endpoint to localhost for testing since the default is now the
|
|
723
741
|
private IP address
|
|
@@ -726,10 +744,10 @@ class MesosBatchSystemTest(hidden.AbstractBatchSystemTest, MesosTestSupport):
|
|
|
726
744
|
config.mesos_endpoint = "localhost:5050"
|
|
727
745
|
return config
|
|
728
746
|
|
|
729
|
-
def supportsWallTime(self):
|
|
747
|
+
def supportsWallTime(self) -> bool:
|
|
730
748
|
return True
|
|
731
749
|
|
|
732
|
-
def createBatchSystem(self):
|
|
750
|
+
def createBatchSystem(self) -> "MesosBatchSystem":
|
|
733
751
|
# We know we have Mesos so we can import the batch system
|
|
734
752
|
from toil.batchSystems.mesos.batchSystem import MesosBatchSystem
|
|
735
753
|
|
|
@@ -738,11 +756,11 @@ class MesosBatchSystemTest(hidden.AbstractBatchSystemTest, MesosTestSupport):
|
|
|
738
756
|
config=self.config, maxCores=numCores, maxMemory=1e9, maxDisk=1001
|
|
739
757
|
)
|
|
740
758
|
|
|
741
|
-
def tearDown(self):
|
|
759
|
+
def tearDown(self) -> None:
|
|
742
760
|
self._stopMesos()
|
|
743
761
|
super().tearDown()
|
|
744
762
|
|
|
745
|
-
def testIgnoreNode(self):
|
|
763
|
+
def testIgnoreNode(self) -> None:
|
|
746
764
|
self.batchSystem.ignoreNode("localhost")
|
|
747
765
|
jobDesc = self._mockJobDescription(
|
|
748
766
|
jobName="test2",
|
|
@@ -807,14 +825,14 @@ class SingleMachineBatchSystemTest(hidden.AbstractBatchSystemTest):
|
|
|
807
825
|
import signal
|
|
808
826
|
import sys
|
|
809
827
|
import time
|
|
810
|
-
from typing import Any
|
|
828
|
+
from typing import Any, Iterable
|
|
811
829
|
|
|
812
830
|
def handle_signal(sig: Any, frame: Any) -> None:
|
|
813
831
|
sys.stderr.write(f"{os.getpid()} ignoring signal {sig}\n")
|
|
814
832
|
|
|
815
833
|
if hasattr(signal, "valid_signals"):
|
|
816
834
|
# We can just ask about the signals
|
|
817
|
-
all_signals = signal.valid_signals()
|
|
835
|
+
all_signals: Iterable[signal.Signals] = signal.valid_signals()
|
|
818
836
|
else:
|
|
819
837
|
# Fish them out by name
|
|
820
838
|
all_signals = [
|
|
@@ -901,7 +919,7 @@ class SingleMachineBatchSystemTest(hidden.AbstractBatchSystemTest):
|
|
|
901
919
|
os.unlink(script_path)
|
|
902
920
|
os.unlink(lockable_path)
|
|
903
921
|
|
|
904
|
-
def testHidingProcessEscape(self):
|
|
922
|
+
def testHidingProcessEscape(self) -> None:
|
|
905
923
|
"""
|
|
906
924
|
Test to make sure that child processes and their descendants go away
|
|
907
925
|
when the Toil workflow stops, even if the job process stops and leaves children.
|
|
@@ -980,7 +998,7 @@ class MaxCoresSingleMachineBatchSystemTest(ToilTest):
|
|
|
980
998
|
return " ".join([sys.executable, self.scriptPath, self.counterPath])
|
|
981
999
|
|
|
982
1000
|
@retry_flaky_test(prepare=[tearDown, setUp])
|
|
983
|
-
def test(self):
|
|
1001
|
+
def test(self) -> None:
|
|
984
1002
|
# We'll use fractions to avoid rounding errors. Remember that not every fraction can be
|
|
985
1003
|
# represented as a floating point number.
|
|
986
1004
|
F = Fraction
|
|
@@ -1025,7 +1043,7 @@ class MaxCoresSingleMachineBatchSystemTest(ToilTest):
|
|
|
1025
1043
|
self.assertEqual(len(jobIds), jobs)
|
|
1026
1044
|
while jobIds:
|
|
1027
1045
|
job = bs.getUpdatedBatchJob(maxWait=10)
|
|
1028
|
-
|
|
1046
|
+
assert job is not None
|
|
1029
1047
|
jobId, status, wallTime = (
|
|
1030
1048
|
job.jobID,
|
|
1031
1049
|
job.exitStatus,
|
|
@@ -1057,7 +1075,7 @@ class MaxCoresSingleMachineBatchSystemTest(ToilTest):
|
|
|
1057
1075
|
SingleMachineBatchSystem.numCores < 3,
|
|
1058
1076
|
"Need at least three cores to run this test",
|
|
1059
1077
|
)
|
|
1060
|
-
def testServices(self):
|
|
1078
|
+
def testServices(self) -> None:
|
|
1061
1079
|
options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
|
|
1062
1080
|
options.logLevel = "DEBUG"
|
|
1063
1081
|
options.maxCores = 3
|
|
@@ -1072,38 +1090,38 @@ class MaxCoresSingleMachineBatchSystemTest(ToilTest):
|
|
|
1072
1090
|
# Toil can use only top-level functions so we have to add them here:
|
|
1073
1091
|
|
|
1074
1092
|
|
|
1075
|
-
def parentJob(job, cmd):
|
|
1093
|
+
def parentJob(job: Job, cmd: str) -> None:
|
|
1076
1094
|
job.addChildJobFn(childJob, cmd)
|
|
1077
1095
|
|
|
1078
1096
|
|
|
1079
|
-
def childJob(job, cmd):
|
|
1097
|
+
def childJob(job: Job, cmd: str) -> None:
|
|
1080
1098
|
job.addService(Service(cmd))
|
|
1081
1099
|
job.addChildJobFn(grandChildJob, cmd)
|
|
1082
1100
|
subprocess.check_call(cmd, shell=True)
|
|
1083
1101
|
|
|
1084
1102
|
|
|
1085
|
-
def grandChildJob(job, cmd):
|
|
1103
|
+
def grandChildJob(job: Job, cmd: str) -> None:
|
|
1086
1104
|
job.addService(Service(cmd))
|
|
1087
1105
|
job.addChildFn(greatGrandChild, cmd)
|
|
1088
1106
|
subprocess.check_call(cmd, shell=True)
|
|
1089
1107
|
|
|
1090
1108
|
|
|
1091
|
-
def greatGrandChild(cmd):
|
|
1109
|
+
def greatGrandChild(cmd: str) -> None:
|
|
1092
1110
|
subprocess.check_call(cmd, shell=True)
|
|
1093
1111
|
|
|
1094
1112
|
|
|
1095
1113
|
class Service(Job.Service):
|
|
1096
|
-
def __init__(self, cmd):
|
|
1114
|
+
def __init__(self, cmd: str) -> None:
|
|
1097
1115
|
super().__init__()
|
|
1098
1116
|
self.cmd = cmd
|
|
1099
1117
|
|
|
1100
|
-
def start(self,
|
|
1118
|
+
def start(self, job: ServiceHostJob) -> None:
|
|
1101
1119
|
subprocess.check_call(self.cmd + " 1", shell=True)
|
|
1102
1120
|
|
|
1103
|
-
def check(self):
|
|
1121
|
+
def check(self) -> bool:
|
|
1104
1122
|
return True
|
|
1105
1123
|
|
|
1106
|
-
def stop(self,
|
|
1124
|
+
def stop(self, job: ServiceHostJob) -> None:
|
|
1107
1125
|
subprocess.check_call(self.cmd + " -1", shell=True)
|
|
1108
1126
|
|
|
1109
1127
|
|
|
@@ -1121,7 +1139,7 @@ class GridEngineBatchSystemTest(hidden.AbstractGridEngineBatchSystemTest):
|
|
|
1121
1139
|
config=self.config, maxCores=numCores, maxMemory=1000e9, maxDisk=1e9
|
|
1122
1140
|
)
|
|
1123
1141
|
|
|
1124
|
-
def tearDown(self):
|
|
1142
|
+
def tearDown(self) -> None:
|
|
1125
1143
|
super().tearDown()
|
|
1126
1144
|
# Cleanup GridEngine output log file from qsub
|
|
1127
1145
|
from glob import glob
|
|
@@ -1144,7 +1162,7 @@ class SlurmBatchSystemTest(hidden.AbstractGridEngineBatchSystemTest):
|
|
|
1144
1162
|
config=self.config, maxCores=numCores, maxMemory=1000e9, maxDisk=1e9
|
|
1145
1163
|
)
|
|
1146
1164
|
|
|
1147
|
-
def tearDown(self):
|
|
1165
|
+
def tearDown(self) -> None:
|
|
1148
1166
|
super().tearDown()
|
|
1149
1167
|
# Cleanup 'slurm-%j.out' produced by sbatch
|
|
1150
1168
|
from glob import glob
|
|
@@ -1175,8 +1193,8 @@ class TorqueBatchSystemTest(hidden.AbstractGridEngineBatchSystemTest):
|
|
|
1175
1193
|
Tests against the Torque batch system
|
|
1176
1194
|
"""
|
|
1177
1195
|
|
|
1178
|
-
def _createDummyConfig(self):
|
|
1179
|
-
config = super().
|
|
1196
|
+
def _createDummyConfig(self) -> Config:
|
|
1197
|
+
config = super()._createConfig()
|
|
1180
1198
|
# can't use _getTestJobStorePath since that method removes the directory
|
|
1181
1199
|
config.jobStore = self._createTempDir("jobStore")
|
|
1182
1200
|
return config
|
|
@@ -1188,7 +1206,7 @@ class TorqueBatchSystemTest(hidden.AbstractGridEngineBatchSystemTest):
|
|
|
1188
1206
|
config=self.config, maxCores=numCores, maxMemory=1000e9, maxDisk=1e9
|
|
1189
1207
|
)
|
|
1190
1208
|
|
|
1191
|
-
def tearDown(self):
|
|
1209
|
+
def tearDown(self) -> None:
|
|
1192
1210
|
super().tearDown()
|
|
1193
1211
|
# Cleanup 'toil_job-%j.out' produced by sbatch
|
|
1194
1212
|
from glob import glob
|
|
@@ -1211,42 +1229,39 @@ class HTCondorBatchSystemTest(hidden.AbstractGridEngineBatchSystemTest):
|
|
|
1211
1229
|
config=self.config, maxCores=numCores, maxMemory=1000e9, maxDisk=1e9
|
|
1212
1230
|
)
|
|
1213
1231
|
|
|
1214
|
-
def tearDown(self):
|
|
1232
|
+
def tearDown(self) -> None:
|
|
1215
1233
|
super().tearDown()
|
|
1216
1234
|
|
|
1217
1235
|
|
|
1218
|
-
class
|
|
1236
|
+
class TestSingleMachineBatchSystemJob(AbstractBatchSystemJobTest):
|
|
1219
1237
|
"""
|
|
1220
1238
|
Tests Toil workflow against the SingleMachine batch system
|
|
1221
1239
|
"""
|
|
1222
1240
|
|
|
1223
|
-
def getBatchSystemName(self):
|
|
1241
|
+
def getBatchSystemName(self) -> str:
|
|
1224
1242
|
return "single_machine"
|
|
1225
1243
|
|
|
1226
|
-
@
|
|
1227
|
-
@
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
hidden.AbstractBatchSystemJobTest.setUp,
|
|
1231
|
-
]
|
|
1232
|
-
)
|
|
1233
|
-
def testConcurrencyWithDisk(self):
|
|
1244
|
+
@pslow
|
|
1245
|
+
@pytest.mark.slow
|
|
1246
|
+
@retry_flaky_test(prepare=[])
|
|
1247
|
+
def testConcurrencyWithDisk(self, tmp_path: Path) -> None:
|
|
1234
1248
|
"""
|
|
1235
1249
|
Tests that the batch system is allocating disk resources properly
|
|
1236
1250
|
"""
|
|
1237
|
-
tempDir = self._createTempDir("testFiles")
|
|
1238
1251
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1252
|
+
workdir = tmp_path / "workdir"
|
|
1253
|
+
workdir.mkdir()
|
|
1254
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
1255
|
+
options.workDir = str(workdir)
|
|
1241
1256
|
from toil import physicalDisk
|
|
1242
1257
|
|
|
1243
1258
|
availableDisk = physicalDisk(options.workDir)
|
|
1244
1259
|
logger.info("Testing disk concurrency limits with %s disk space", availableDisk)
|
|
1245
1260
|
# More disk might become available by the time Toil starts, so we limit it here
|
|
1246
1261
|
options.maxDisk = availableDisk
|
|
1247
|
-
options.batchSystem = self.
|
|
1262
|
+
options.batchSystem = self.getBatchSystemName()
|
|
1248
1263
|
|
|
1249
|
-
counterPath =
|
|
1264
|
+
counterPath = tmp_path / "counter"
|
|
1250
1265
|
resetCounters(counterPath)
|
|
1251
1266
|
value, maxValue = getCounters(counterPath)
|
|
1252
1267
|
assert (value, maxValue) == (0, 0)
|
|
@@ -1283,31 +1298,32 @@ class SingleMachineBatchSystemJobTest(hidden.AbstractBatchSystemJobTest):
|
|
|
1283
1298
|
|
|
1284
1299
|
logger.info("After run: %s disk space", physicalDisk(options.workDir))
|
|
1285
1300
|
|
|
1286
|
-
|
|
1301
|
+
assert maxValue == 1
|
|
1287
1302
|
|
|
1288
|
-
@
|
|
1303
|
+
@pytest.mark.skipif(
|
|
1289
1304
|
SingleMachineBatchSystem.numCores < 4,
|
|
1290
|
-
"Need at least four cores to run this test",
|
|
1305
|
+
reason="Need at least four cores to run this test",
|
|
1291
1306
|
)
|
|
1292
|
-
@
|
|
1293
|
-
|
|
1307
|
+
@pslow
|
|
1308
|
+
@pytest.mark.slow
|
|
1309
|
+
def testNestedResourcesDoNotBlock(self, tmp_path: Path) -> None:
|
|
1294
1310
|
"""
|
|
1295
1311
|
Resources are requested in the order Memory > Cpu > Disk.
|
|
1296
1312
|
Test that unavailability of cpus for one job that is scheduled does not block another job
|
|
1297
1313
|
that can run.
|
|
1298
1314
|
"""
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
options = Job.Runner.getDefaultOptions(
|
|
1302
|
-
options.workDir =
|
|
1315
|
+
workdir = tmp_path / "workdir"
|
|
1316
|
+
workdir.mkdir()
|
|
1317
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
1318
|
+
options.workDir = str(workdir)
|
|
1303
1319
|
options.maxCores = 4
|
|
1304
1320
|
from toil import physicalMemory
|
|
1305
1321
|
|
|
1306
1322
|
availableMemory = physicalMemory()
|
|
1307
|
-
options.batchSystem = self.
|
|
1323
|
+
options.batchSystem = self.getBatchSystemName()
|
|
1308
1324
|
|
|
1309
|
-
outFile =
|
|
1310
|
-
open(
|
|
1325
|
+
outFile = tmp_path / "counter"
|
|
1326
|
+
outFile.open("w").close()
|
|
1311
1327
|
|
|
1312
1328
|
root = Job()
|
|
1313
1329
|
|
|
@@ -1395,7 +1411,7 @@ class SingleMachineBatchSystemJobTest(hidden.AbstractBatchSystemJobTest):
|
|
|
1395
1411
|
should not block them, and should only run after they finish.
|
|
1396
1412
|
"""
|
|
1397
1413
|
Job.Runner.startToil(root, options)
|
|
1398
|
-
with open(
|
|
1414
|
+
with outFile.open() as oFH:
|
|
1399
1415
|
outString = oFH.read()
|
|
1400
1416
|
# The ordering of b, fJ and sJ is non-deterministic since they are scheduled at the same
|
|
1401
1417
|
# time. We look for all possible permutations.
|
|
@@ -1406,12 +1422,12 @@ class SingleMachineBatchSystemJobTest(hidden.AbstractBatchSystemJobTest):
|
|
|
1406
1422
|
assert outString.endswith("sJCsJGCfJC")
|
|
1407
1423
|
|
|
1408
1424
|
|
|
1409
|
-
def _resourceBlockTestAuxFn(outFile, sleepTime, writeVal):
|
|
1425
|
+
def _resourceBlockTestAuxFn(outFile: StrPath, sleepTime: int, writeVal: str) -> None:
|
|
1410
1426
|
"""
|
|
1411
1427
|
Write a value to the out file and then sleep for requested seconds.
|
|
1412
|
-
:param
|
|
1413
|
-
:param
|
|
1414
|
-
:param
|
|
1428
|
+
:param outFile: File to write to
|
|
1429
|
+
:param sleepTime: Time to sleep for
|
|
1430
|
+
:param writeVal: Character to write
|
|
1415
1431
|
"""
|
|
1416
1432
|
with open(outFile, "a") as oFH:
|
|
1417
1433
|
fcntl.flock(oFH, fcntl.LOCK_EX)
|
|
@@ -1419,33 +1435,38 @@ def _resourceBlockTestAuxFn(outFile, sleepTime, writeVal):
|
|
|
1419
1435
|
time.sleep(sleepTime)
|
|
1420
1436
|
|
|
1421
1437
|
|
|
1422
|
-
@
|
|
1423
|
-
@
|
|
1424
|
-
|
|
1438
|
+
@pslow
|
|
1439
|
+
@pytest.mark.slow
|
|
1440
|
+
@pneeds_mesos
|
|
1441
|
+
class TestMesosBatchSystemJob(AbstractBatchSystemJobTest, MesosTestSupport):
|
|
1425
1442
|
"""
|
|
1426
1443
|
Tests Toil workflow against the Mesos batch system
|
|
1427
1444
|
"""
|
|
1428
1445
|
|
|
1429
|
-
|
|
1446
|
+
@pytest.fixture(autouse=True)
|
|
1447
|
+
def mesos_support(self) -> Generator[None]:
|
|
1448
|
+
try:
|
|
1449
|
+
self._startMesos(self.cpuCount)
|
|
1450
|
+
yield
|
|
1451
|
+
finally:
|
|
1452
|
+
self._stopMesos()
|
|
1453
|
+
|
|
1454
|
+
def getOptions(self, tempDir: Path) -> argparse.Namespace:
|
|
1430
1455
|
options = super().getOptions(tempDir)
|
|
1431
1456
|
options.mesos_endpoint = "localhost:5050"
|
|
1432
1457
|
return options
|
|
1433
1458
|
|
|
1434
|
-
def getBatchSystemName(self):
|
|
1435
|
-
self._startMesos(self.cpuCount)
|
|
1459
|
+
def getBatchSystemName(self) -> "str":
|
|
1436
1460
|
return "mesos"
|
|
1437
1461
|
|
|
1438
|
-
def tearDown(self):
|
|
1439
|
-
self._stopMesos()
|
|
1440
|
-
|
|
1441
1462
|
|
|
1442
|
-
def measureConcurrency(filepath, sleep_time=10):
|
|
1463
|
+
def measureConcurrency(filepath: StrPath, sleep_time: int = 10) -> int:
|
|
1443
1464
|
"""
|
|
1444
1465
|
Run in parallel to determine the number of concurrent tasks.
|
|
1445
1466
|
This code was copied from toil.batchSystemTestMaxCoresSingleMachineBatchSystemTest
|
|
1446
|
-
:param
|
|
1447
|
-
:param
|
|
1448
|
-
:return
|
|
1467
|
+
:param filepath: path to counter file
|
|
1468
|
+
:param sleep_time: number of seconds to sleep before counting down
|
|
1469
|
+
:return: max concurrency value
|
|
1449
1470
|
"""
|
|
1450
1471
|
count(1, filepath)
|
|
1451
1472
|
try:
|
|
@@ -1454,16 +1475,17 @@ def measureConcurrency(filepath, sleep_time=10):
|
|
|
1454
1475
|
return count(-1, filepath)
|
|
1455
1476
|
|
|
1456
1477
|
|
|
1457
|
-
def count(delta, file_path):
|
|
1478
|
+
def count(delta: int, file_path: StrPath) -> int:
|
|
1458
1479
|
"""
|
|
1459
1480
|
Increments counter file and returns the max number of times the file
|
|
1460
1481
|
has been modified. Counter data must be in the form:
|
|
1461
1482
|
concurrent tasks, max concurrent tasks (counter should be initialized to 0,0)
|
|
1462
1483
|
|
|
1463
|
-
:param
|
|
1464
|
-
:param
|
|
1465
|
-
:return
|
|
1484
|
+
:param delta: increment value
|
|
1485
|
+
:param file_path: path to shared counter file
|
|
1486
|
+
:return: max concurrent tasks
|
|
1466
1487
|
"""
|
|
1488
|
+
|
|
1467
1489
|
fd = os.open(file_path, os.O_RDWR)
|
|
1468
1490
|
try:
|
|
1469
1491
|
fcntl.flock(fd, fcntl.LOCK_EX)
|
|
@@ -1483,13 +1505,13 @@ def count(delta, file_path):
|
|
|
1483
1505
|
return maxValue
|
|
1484
1506
|
|
|
1485
1507
|
|
|
1486
|
-
def getCounters(path):
|
|
1508
|
+
def getCounters(path: StrPath) -> tuple[int, int]:
|
|
1487
1509
|
with open(path, "r+") as f:
|
|
1488
1510
|
concurrentTasks, maxConcurrentTasks = (int(i) for i in f.read().split(","))
|
|
1489
1511
|
return concurrentTasks, maxConcurrentTasks
|
|
1490
1512
|
|
|
1491
1513
|
|
|
1492
|
-
def resetCounters(path):
|
|
1514
|
+
def resetCounters(path: StrPath) -> None:
|
|
1493
1515
|
with open(path, "w") as f:
|
|
1494
1516
|
f.write("0,0")
|
|
1495
1517
|
f.close()
|