toil 8.1.0b1__py3-none-any.whl → 9.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toil/__init__.py +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/registry.py +15 -118
- toil/batchSystems/singleMachine.py +1 -1
- toil/batchSystems/slurm.py +27 -26
- toil/bus.py +5 -3
- toil/common.py +59 -12
- toil/cwl/cwltoil.py +81 -38
- toil/cwl/utils.py +103 -3
- toil/job.py +64 -49
- toil/jobStores/abstractJobStore.py +35 -239
- toil/jobStores/aws/jobStore.py +2 -1
- toil/jobStores/fileJobStore.py +27 -2
- toil/jobStores/googleJobStore.py +110 -33
- toil/leader.py +9 -0
- toil/lib/accelerators.py +4 -2
- 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/generatedEC2Lists.py +81 -19
- toil/lib/history.py +87 -13
- toil/lib/history_submission.py +23 -9
- toil/lib/io.py +34 -22
- toil/lib/misc.py +8 -2
- toil/lib/plugins.py +106 -0
- toil/lib/resources.py +2 -1
- toil/lib/threading.py +11 -10
- toil/lib/url.py +320 -0
- toil/options/common.py +8 -0
- toil/options/cwl.py +13 -1
- toil/options/runner.py +17 -10
- toil/options/wdl.py +22 -0
- toil/provisioners/aws/awsProvisioner.py +25 -2
- toil/server/api_spec/LICENSE +201 -0
- toil/server/api_spec/README.rst +5 -0
- toil/server/app.py +12 -6
- toil/server/cli/wes_cwl_runner.py +3 -2
- toil/server/wes/abstract_backend.py +21 -43
- toil/server/wes/toil_backend.py +2 -2
- toil/test/__init__.py +275 -115
- toil/test/batchSystems/batchSystemTest.py +228 -213
- toil/test/batchSystems/batch_system_plugin_test.py +7 -0
- 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 +1175 -870
- 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/docs/scriptsTest.py +2 -1
- 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/lib/test_url.py +69 -0
- toil/test/lib/url_plugin_test.py +105 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +13 -10
- toil/test/provisioners/clusterTest.py +17 -4
- toil/test/provisioners/gceProvisionerTest.py +17 -15
- toil/test/server/serverTest.py +78 -36
- 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/read_file.wdl +18 -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 +14 -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 +751 -529
- toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
- toil/utils/toilSshCluster.py +23 -0
- toil/utils/toilUpdateEC2Instances.py +1 -0
- toil/version.py +5 -5
- toil/wdl/wdltoil.py +518 -437
- toil/worker.py +11 -6
- {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/METADATA +25 -24
- toil-9.0.0.dist-info/RECORD +444 -0
- {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/WHEEL +1 -1
- toil-8.1.0b1.dist-info/RECORD +0 -259
- {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/entry_points.txt +0 -0
- {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info/licenses}/LICENSE +0 -0
- {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/top_level.txt +0 -0
|
@@ -11,72 +11,81 @@
|
|
|
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 argparse import Namespace
|
|
15
|
+
from collections.abc import Callable, Generator, Sequence
|
|
14
16
|
import logging
|
|
15
17
|
import os
|
|
18
|
+
from pathlib import Path
|
|
16
19
|
import signal
|
|
17
20
|
import time
|
|
18
|
-
from
|
|
21
|
+
from typing import Optional
|
|
19
22
|
from uuid import uuid4
|
|
20
23
|
|
|
21
24
|
import psutil
|
|
25
|
+
import pytest
|
|
22
26
|
|
|
23
27
|
from toil.exceptions import FailedJobsException
|
|
24
28
|
from toil.job import Job
|
|
25
29
|
from toil.lib.threading import cpu_count
|
|
26
|
-
from toil.test import
|
|
30
|
+
from toil.test import pslow as slow
|
|
27
31
|
|
|
28
32
|
logger = logging.getLogger(__name__)
|
|
29
33
|
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
@pytest.fixture(scope="function")
|
|
36
|
+
def options(tmp_path: Path) -> Generator[Namespace]:
|
|
37
|
+
try:
|
|
38
|
+
testDir = tmp_path / "testDir"
|
|
39
|
+
testDir.mkdir()
|
|
40
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
41
|
+
options.logLevel = "INFO"
|
|
42
|
+
options.workDir = str(testDir)
|
|
43
|
+
options.clean = "always"
|
|
44
|
+
options.logFile = str(testDir / "logFile")
|
|
45
|
+
yield options
|
|
46
|
+
finally:
|
|
47
|
+
pass # no cleanup
|
|
48
|
+
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _getTestJobStore(self):
|
|
38
|
-
if self.jobStoreType == "file":
|
|
39
|
-
return self._getTestJobStorePath()
|
|
40
|
-
elif self.jobStoreType == "aws":
|
|
41
|
-
return f"aws:{self.awsRegion()}:cache-tests-{uuid4()}"
|
|
42
|
-
elif self.jobStoreType == "google":
|
|
43
|
-
projectID = os.getenv("TOIL_GOOGLE_PROJECTID")
|
|
44
|
-
return f"google:{projectID}:cache-tests-{str(uuid4())}"
|
|
45
|
-
else:
|
|
46
|
-
raise RuntimeError("Illegal job store type.")
|
|
47
|
-
|
|
48
|
-
def setUp(self):
|
|
49
|
-
super().setUp()
|
|
50
|
-
testDir = self._createTempDir()
|
|
51
|
-
self.options = Job.Runner.getDefaultOptions(self._getTestJobStore())
|
|
52
|
-
self.options.logLevel = "INFO"
|
|
53
|
-
self.options.workDir = testDir
|
|
54
|
-
self.options.clean = "always"
|
|
55
|
-
self.options.logFile = os.path.join(testDir, "logFile")
|
|
50
|
+
class TestDeferredFunction:
|
|
51
|
+
"""Test the deferred function system."""
|
|
56
52
|
|
|
57
53
|
# Tests for the various defer possibilities
|
|
58
|
-
def testDeferredFunctionRunsWithMethod(
|
|
54
|
+
def testDeferredFunctionRunsWithMethod(
|
|
55
|
+
self, tmp_path: Path, options: Namespace
|
|
56
|
+
) -> None:
|
|
59
57
|
"""
|
|
60
58
|
Refer docstring in _testDeferredFunctionRuns.
|
|
61
59
|
Test with Method
|
|
62
60
|
"""
|
|
63
|
-
self._testDeferredFunctionRuns(_writeNonLocalFilesMethod)
|
|
61
|
+
self._testDeferredFunctionRuns(tmp_path, options, _writeNonLocalFilesMethod)
|
|
64
62
|
|
|
65
|
-
def testDeferredFunctionRunsWithClassMethod(
|
|
63
|
+
def testDeferredFunctionRunsWithClassMethod(
|
|
64
|
+
self, tmp_path: Path, options: Namespace
|
|
65
|
+
) -> None:
|
|
66
66
|
"""
|
|
67
67
|
Refer docstring in _testDeferredFunctionRuns.
|
|
68
68
|
Test with Class Method
|
|
69
69
|
"""
|
|
70
|
-
self._testDeferredFunctionRuns(
|
|
70
|
+
self._testDeferredFunctionRuns(
|
|
71
|
+
tmp_path, options, _writeNonLocalFilesClassMethod
|
|
72
|
+
)
|
|
71
73
|
|
|
72
|
-
def testDeferredFunctionRunsWithLambda(
|
|
74
|
+
def testDeferredFunctionRunsWithLambda(
|
|
75
|
+
self, tmp_path: Path, options: Namespace
|
|
76
|
+
) -> None:
|
|
73
77
|
"""
|
|
74
78
|
Refer docstring in _testDeferredFunctionRuns.
|
|
75
79
|
Test with Lambda
|
|
76
80
|
"""
|
|
77
|
-
self._testDeferredFunctionRuns(_writeNonLocalFilesLambda)
|
|
78
|
-
|
|
79
|
-
def _testDeferredFunctionRuns(
|
|
81
|
+
self._testDeferredFunctionRuns(tmp_path, options, _writeNonLocalFilesLambda)
|
|
82
|
+
|
|
83
|
+
def _testDeferredFunctionRuns(
|
|
84
|
+
self,
|
|
85
|
+
tmp_path: Path,
|
|
86
|
+
options: Namespace,
|
|
87
|
+
callableFn: Callable[[Job, tuple[Path, Path]], None],
|
|
88
|
+
) -> None:
|
|
80
89
|
"""
|
|
81
90
|
Create 2 files. Make a job that writes data to them. Register a deferred function that
|
|
82
91
|
deletes the two files (one passed as an arg, and one as a kwarg) and later assert that
|
|
@@ -85,20 +94,24 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
|
|
|
85
94
|
:param function callableFn: The function to use in the test.
|
|
86
95
|
:return: None
|
|
87
96
|
"""
|
|
88
|
-
workdir =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
assert
|
|
97
|
+
workdir = tmp_path / "nonLocalDir"
|
|
98
|
+
workdir.mkdir()
|
|
99
|
+
nonLocalFile1 = workdir / str(uuid4())
|
|
100
|
+
nonLocalFile2 = workdir / str(uuid4())
|
|
101
|
+
nonLocalFile1.touch()
|
|
102
|
+
nonLocalFile2.touch()
|
|
103
|
+
assert nonLocalFile1.exists()
|
|
104
|
+
assert nonLocalFile2.exists()
|
|
95
105
|
A = Job.wrapJobFn(callableFn, files=(nonLocalFile1, nonLocalFile2))
|
|
96
|
-
Job.Runner.startToil(A,
|
|
97
|
-
assert not
|
|
98
|
-
assert not
|
|
106
|
+
Job.Runner.startToil(A, options)
|
|
107
|
+
assert not nonLocalFile1.exists()
|
|
108
|
+
assert not nonLocalFile2.exists()
|
|
99
109
|
|
|
100
110
|
@slow
|
|
101
|
-
|
|
111
|
+
@pytest.mark.slow
|
|
112
|
+
def testDeferredFunctionRunsWithFailures(
|
|
113
|
+
self, options: Namespace, tmp_path: Path
|
|
114
|
+
) -> None:
|
|
102
115
|
"""
|
|
103
116
|
Create 2 non local filesto use as flags. Create a job that registers a function that
|
|
104
117
|
deletes one non local file. If that file exists, the job SIGKILLs itself. If it doesn't
|
|
@@ -114,23 +127,30 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
|
|
|
114
127
|
Incidentally, this also tests for multiple registered deferred functions, and the case
|
|
115
128
|
where a deferred function fails (since the first file doesn't exist on the retry).
|
|
116
129
|
"""
|
|
117
|
-
|
|
118
|
-
workdir =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
assert
|
|
130
|
+
options.retryCount = 1
|
|
131
|
+
workdir = tmp_path / "nonLocalDir"
|
|
132
|
+
workdir.mkdir()
|
|
133
|
+
nonLocalFile1 = workdir / str(uuid4())
|
|
134
|
+
nonLocalFile2 = workdir / str(uuid4())
|
|
135
|
+
nonLocalFile1.touch()
|
|
136
|
+
nonLocalFile2.touch()
|
|
137
|
+
assert nonLocalFile1.exists()
|
|
138
|
+
assert nonLocalFile2.exists()
|
|
125
139
|
A = Job.wrapJobFn(
|
|
126
140
|
_deferredFunctionRunsWithFailuresFn, files=(nonLocalFile1, nonLocalFile2)
|
|
127
141
|
)
|
|
128
|
-
Job.Runner.startToil(A,
|
|
129
|
-
assert not
|
|
130
|
-
assert not
|
|
142
|
+
Job.Runner.startToil(A, options)
|
|
143
|
+
assert not nonLocalFile1.exists()
|
|
144
|
+
assert not nonLocalFile2.exists()
|
|
131
145
|
|
|
132
146
|
@slow
|
|
133
|
-
|
|
147
|
+
@pytest.mark.slow
|
|
148
|
+
@pytest.mark.skipif(
|
|
149
|
+
cpu_count() < 2, reason="Not enough CPUs to run two tasks at once"
|
|
150
|
+
)
|
|
151
|
+
def testNewJobsCanHandleOtherJobDeaths(
|
|
152
|
+
self, options: Namespace, tmp_path: Path
|
|
153
|
+
) -> None:
|
|
134
154
|
"""
|
|
135
155
|
Create 2 non-local files and then create 2 jobs. The first job registers a deferred job
|
|
136
156
|
to delete the second non-local file, deletes the first non-local file and then kills
|
|
@@ -141,19 +161,16 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
|
|
|
141
161
|
end of the run.
|
|
142
162
|
"""
|
|
143
163
|
|
|
144
|
-
# Check to make sure we can run two jobs in parallel
|
|
145
|
-
cpus = cpu_count()
|
|
146
|
-
assert cpus >= 2, "Not enough CPUs to run two tasks at once"
|
|
147
|
-
|
|
148
164
|
# There can be no retries
|
|
149
|
-
|
|
150
|
-
workdir =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
assert
|
|
165
|
+
options.retryCount = 0
|
|
166
|
+
workdir = tmp_path / "nonLocalDir"
|
|
167
|
+
workdir.mkdir()
|
|
168
|
+
nonLocalFile1 = workdir / str(uuid4())
|
|
169
|
+
nonLocalFile2 = workdir / str(uuid4())
|
|
170
|
+
nonLocalFile1.touch()
|
|
171
|
+
nonLocalFile2.touch()
|
|
172
|
+
assert nonLocalFile1.exists()
|
|
173
|
+
assert nonLocalFile2.exists()
|
|
157
174
|
files = [nonLocalFile1, nonLocalFile2]
|
|
158
175
|
root = Job()
|
|
159
176
|
# A and B here must run in parallel for this to work
|
|
@@ -169,11 +186,13 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
|
|
|
169
186
|
root.addChild(B)
|
|
170
187
|
B.addChild(C)
|
|
171
188
|
try:
|
|
172
|
-
Job.Runner.startToil(root,
|
|
173
|
-
except FailedJobsException
|
|
189
|
+
Job.Runner.startToil(root, options)
|
|
190
|
+
except FailedJobsException:
|
|
174
191
|
pass
|
|
175
192
|
|
|
176
|
-
def testBatchSystemCleanupCanHandleWorkerDeaths(
|
|
193
|
+
def testBatchSystemCleanupCanHandleWorkerDeaths(
|
|
194
|
+
self, options: Namespace, tmp_path: Path
|
|
195
|
+
) -> None:
|
|
177
196
|
"""
|
|
178
197
|
Create some non-local files. Create a job that registers a deferred
|
|
179
198
|
function to delete the file and then kills its worker.
|
|
@@ -184,17 +203,17 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
|
|
|
184
203
|
"""
|
|
185
204
|
|
|
186
205
|
# There can be no retries
|
|
187
|
-
|
|
188
|
-
workdir =
|
|
189
|
-
|
|
190
|
-
|
|
206
|
+
options.retryCount = 0
|
|
207
|
+
workdir = tmp_path / "nonLocalDir"
|
|
208
|
+
workdir.mkdir()
|
|
209
|
+
nonLocalFile1 = workdir / str(uuid4())
|
|
210
|
+
nonLocalFile2 = workdir / str(uuid4())
|
|
191
211
|
# The first file has to be non zero or meseeks will go into an infinite sleep
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
assert
|
|
197
|
-
assert os.path.exists(nonLocalFile2)
|
|
212
|
+
with nonLocalFile1.open("w") as file1:
|
|
213
|
+
file1.write("test")
|
|
214
|
+
nonLocalFile2.touch()
|
|
215
|
+
assert nonLocalFile1.exists()
|
|
216
|
+
assert nonLocalFile2.exists()
|
|
198
217
|
# We only use the "A" job here, and we fill in the first file, so all
|
|
199
218
|
# it will do is defer deleting the second file, delete the first file,
|
|
200
219
|
# and die.
|
|
@@ -202,117 +221,111 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
|
|
|
202
221
|
_testNewJobsCanHandleOtherJobDeaths_A, files=(nonLocalFile1, nonLocalFile2)
|
|
203
222
|
)
|
|
204
223
|
try:
|
|
205
|
-
Job.Runner.startToil(A,
|
|
224
|
+
Job.Runner.startToil(A, options)
|
|
206
225
|
except FailedJobsException:
|
|
207
226
|
pass
|
|
208
|
-
assert not
|
|
209
|
-
assert not
|
|
227
|
+
assert not nonLocalFile1.exists()
|
|
228
|
+
assert not nonLocalFile2.exists()
|
|
210
229
|
|
|
211
230
|
|
|
212
|
-
def _writeNonLocalFilesMethod(job, files):
|
|
231
|
+
def _writeNonLocalFilesMethod(job: Job, files: tuple[Path, Path]) -> None:
|
|
213
232
|
"""
|
|
214
233
|
Write some data to 2 files. Pass them to a registered deferred method.
|
|
215
234
|
|
|
216
235
|
:param tuple files: the tuple of the two files to work with
|
|
217
|
-
:return: None
|
|
218
236
|
"""
|
|
219
237
|
for nlf in files:
|
|
220
|
-
with open(
|
|
238
|
+
with nlf.open("wb") as nonLocalFileHandle:
|
|
221
239
|
nonLocalFileHandle.write(os.urandom(1 * 1024 * 1024))
|
|
222
240
|
job.defer(_deleteMethods._deleteFileMethod, files[0], nlf=files[1])
|
|
223
|
-
return None
|
|
224
241
|
|
|
225
242
|
|
|
226
|
-
def _writeNonLocalFilesClassMethod(job, files):
|
|
243
|
+
def _writeNonLocalFilesClassMethod(job: Job, files: tuple[Path, Path]) -> None:
|
|
227
244
|
"""
|
|
228
245
|
Write some data to 2 files. Pass them to a registered deferred class method.
|
|
229
246
|
|
|
230
247
|
:param tuple files: the tuple of the two files to work with
|
|
231
|
-
:return: None
|
|
232
248
|
"""
|
|
233
249
|
for nlf in files:
|
|
234
|
-
with open(
|
|
250
|
+
with nlf.open("wb") as nonLocalFileHandle:
|
|
235
251
|
nonLocalFileHandle.write(os.urandom(1 * 1024 * 1024))
|
|
236
252
|
job.defer(_deleteMethods._deleteFileClassMethod, files[0], nlf=files[1])
|
|
237
|
-
return None
|
|
238
253
|
|
|
239
254
|
|
|
240
|
-
def _writeNonLocalFilesLambda(job, files):
|
|
255
|
+
def _writeNonLocalFilesLambda(job: Job, files: tuple[Path, Path]) -> None:
|
|
241
256
|
"""
|
|
242
257
|
Write some data to 2 files. Pass them to a registered deferred Lambda.
|
|
243
258
|
|
|
244
259
|
:param tuple files: the tuple of the two files to work with
|
|
245
|
-
:return: None
|
|
246
260
|
"""
|
|
247
|
-
|
|
261
|
+
|
|
262
|
+
def lmd(x: Path, nlf: Path) -> None:
|
|
263
|
+
x.unlink()
|
|
264
|
+
nlf.unlink()
|
|
248
265
|
for nlf in files:
|
|
249
|
-
with open(
|
|
266
|
+
with nlf.open("wb") as nonLocalFileHandle:
|
|
250
267
|
nonLocalFileHandle.write(os.urandom(1 * 1024 * 1024))
|
|
251
268
|
job.defer(lmd, files[0], nlf=files[1])
|
|
252
|
-
return None
|
|
253
269
|
|
|
254
270
|
|
|
255
|
-
def _deferredFunctionRunsWithFailuresFn(job, files):
|
|
271
|
+
def _deferredFunctionRunsWithFailuresFn(job: Job, files: tuple[Path, Path]) -> None:
|
|
256
272
|
"""
|
|
257
273
|
Refer testDeferredFunctionRunsWithFailures
|
|
258
274
|
|
|
259
275
|
:param tuple files: the tuple of the two files to work with
|
|
260
|
-
:return: None
|
|
261
276
|
"""
|
|
262
277
|
job.defer(_deleteFile, files[0])
|
|
263
|
-
if
|
|
278
|
+
if files[0].exists():
|
|
264
279
|
os.kill(os.getpid(), signal.SIGKILL)
|
|
265
280
|
else:
|
|
266
|
-
assert
|
|
281
|
+
assert files[1].exists()
|
|
267
282
|
job.defer(_deleteFile, files[1])
|
|
268
283
|
|
|
269
284
|
|
|
270
|
-
def _deleteFile(nonLocalFile, nlf=None):
|
|
285
|
+
def _deleteFile(nonLocalFile: Path, nlf: Optional[Path] = None) -> None:
|
|
271
286
|
"""
|
|
272
287
|
Delete nonLocalFile and nlf
|
|
273
|
-
:param
|
|
274
|
-
:param
|
|
275
|
-
:return: None
|
|
288
|
+
:param nonLocalFile:
|
|
289
|
+
:param nlf:
|
|
276
290
|
"""
|
|
277
291
|
logger.debug("Removing file: %s", nonLocalFile)
|
|
278
|
-
|
|
292
|
+
nonLocalFile.unlink()
|
|
279
293
|
logger.debug("Successfully removed file: %s", nonLocalFile)
|
|
280
294
|
if nlf is not None:
|
|
281
295
|
logger.debug("Removing file: %s", nlf)
|
|
282
|
-
|
|
296
|
+
nlf.unlink()
|
|
283
297
|
logger.debug("Successfully removed file: %s", nlf)
|
|
284
298
|
|
|
285
299
|
|
|
286
|
-
def _testNewJobsCanHandleOtherJobDeaths_A(job, files):
|
|
300
|
+
def _testNewJobsCanHandleOtherJobDeaths_A(job: Job, files: tuple[Path, Path]) -> None:
|
|
287
301
|
"""
|
|
288
302
|
Defer deletion of files[1], then wait for _testNewJobsCanHandleOtherJobDeaths_B to
|
|
289
303
|
start up, and finally delete files[0] before sigkilling self.
|
|
290
304
|
|
|
291
305
|
:param tuple files: the tuple of the two files to work with
|
|
292
|
-
:return: None
|
|
293
306
|
"""
|
|
294
307
|
|
|
295
308
|
# Write the pid to files[1] such that we can be sure that this process has died before
|
|
296
309
|
# we spawn the next job that will do the cleanup.
|
|
297
|
-
with
|
|
310
|
+
with files[1].open("w") as fileHandle:
|
|
298
311
|
fileHandle.write(str(os.getpid()))
|
|
299
312
|
job.defer(_deleteFile, files[1])
|
|
300
313
|
logger.info("Deferred delete of %s", files[1])
|
|
301
|
-
while
|
|
314
|
+
while files[0].stat().st_size == 0:
|
|
302
315
|
time.sleep(0.5)
|
|
303
|
-
|
|
316
|
+
files[0].unlink()
|
|
304
317
|
os.kill(os.getpid(), signal.SIGKILL)
|
|
305
318
|
|
|
306
319
|
|
|
307
|
-
def _testNewJobsCanHandleOtherJobDeaths_B(job, files):
|
|
320
|
+
def _testNewJobsCanHandleOtherJobDeaths_B(job: Job, files: tuple[Path, Path]) -> None:
|
|
308
321
|
# Write something to files[0] such that we can be sure that this process has started
|
|
309
322
|
# before _testNewJobsCanHandleOtherJobDeaths_A kills itself.
|
|
310
|
-
with
|
|
323
|
+
with files[0].open("w") as fileHandle:
|
|
311
324
|
fileHandle.write(str(os.getpid()))
|
|
312
|
-
while
|
|
325
|
+
while files[0].exists():
|
|
313
326
|
time.sleep(0.5)
|
|
314
327
|
# Get the pid of _testNewJobsCanHandleOtherJobDeaths_A and wait for it to truly be dead.
|
|
315
|
-
with
|
|
328
|
+
with files[1].open() as fileHandle:
|
|
316
329
|
pid = int(fileHandle.read())
|
|
317
330
|
assert pid > 0
|
|
318
331
|
while psutil.pid_exists(pid):
|
|
@@ -321,37 +334,37 @@ def _testNewJobsCanHandleOtherJobDeaths_B(job, files):
|
|
|
321
334
|
# spawn the next job
|
|
322
335
|
|
|
323
336
|
|
|
324
|
-
def _testNewJobsCanHandleOtherJobDeaths_C(
|
|
337
|
+
def _testNewJobsCanHandleOtherJobDeaths_C(
|
|
338
|
+
job: Job, files: Sequence[Path], expectedResult: bool
|
|
339
|
+
) -> None:
|
|
325
340
|
"""
|
|
326
341
|
Asserts whether the files exist or not.
|
|
327
342
|
|
|
328
|
-
:param
|
|
329
|
-
:param
|
|
330
|
-
:param
|
|
343
|
+
:param job: Job
|
|
344
|
+
:param files: list of files to test
|
|
345
|
+
:param expectedResult: Are we expecting the files to exist or not?
|
|
331
346
|
"""
|
|
332
347
|
for testFile in files:
|
|
333
|
-
assert
|
|
348
|
+
assert testFile.exists() is expectedResult
|
|
334
349
|
|
|
335
350
|
|
|
336
351
|
class _deleteMethods:
|
|
337
352
|
@staticmethod
|
|
338
|
-
def _deleteFileMethod(nonLocalFile, nlf=None):
|
|
353
|
+
def _deleteFileMethod(nonLocalFile: Path, nlf: Optional[Path] = None) -> None:
|
|
339
354
|
"""
|
|
340
355
|
Delete nonLocalFile and nlf
|
|
341
|
-
|
|
342
|
-
:return: None
|
|
343
356
|
"""
|
|
344
|
-
|
|
357
|
+
nonLocalFile.unlink()
|
|
345
358
|
if nlf is not None:
|
|
346
|
-
|
|
359
|
+
nlf.unlink()
|
|
347
360
|
|
|
348
361
|
@classmethod
|
|
349
|
-
def _deleteFileClassMethod(
|
|
362
|
+
def _deleteFileClassMethod(
|
|
363
|
+
cls, nonLocalFile: Path, nlf: Optional[Path] = None
|
|
364
|
+
) -> None:
|
|
350
365
|
"""
|
|
351
366
|
Delete nonLocalFile and nlf
|
|
352
|
-
|
|
353
|
-
:return: None
|
|
354
367
|
"""
|
|
355
|
-
|
|
368
|
+
nonLocalFile.unlink()
|
|
356
369
|
if nlf is not None:
|
|
357
|
-
|
|
370
|
+
nlf.unlink()
|